Compare commits
10 Commits
c62f070e39
...
8305ce7e76
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8305ce7e76 | ||
| 270025a1b4 | |||
| 655971a000 | |||
| 6e5c41431a | |||
| bbe43cef95 | |||
| d411d41cff | |||
| 08262f3dfd | |||
| 225b5df774 | |||
| c6fa363cd9 | |||
| 33d1f1871a |
147
flake.lock
generated
Normal file
147
flake.lock
generated
Normal file
@ -0,0 +1,147 @@
|
||||
{
|
||||
"nodes": {
|
||||
"build-gradle-application": {
|
||||
"inputs": {
|
||||
"flake-parts": "flake-parts",
|
||||
"nixpkgs": "nixpkgs"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1761837331,
|
||||
"narHash": "sha256-uqmuTLq3VJfPqD1frbzIv9EcmlKcl7pRjNFwKkWIr6I=",
|
||||
"ref": "refs/heads/main",
|
||||
"rev": "411fff172011aafc7b2afc365ced450ffe7d729d",
|
||||
"revCount": 98,
|
||||
"type": "git",
|
||||
"url": "file:/home/mysaa/Documents/Projets/buildGradleApplication"
|
||||
},
|
||||
"original": {
|
||||
"type": "git",
|
||||
"url": "file:/home/mysaa/Documents/Projets/buildGradleApplication"
|
||||
}
|
||||
},
|
||||
"flake-parts": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": "nixpkgs-lib"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1756770412,
|
||||
"narHash": "sha256-+uWLQZccFHwqpGqr2Yt5VsW/PbeJVTn9Dk6SHWhNRPw=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "4524271976b625a4a605beefd893f270620fd751",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "flake-parts",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"locked": {
|
||||
"lastModified": 1605370193,
|
||||
"narHash": "sha256-YyMTf3URDL/otKdKgtoMChu4vfVL3vCMkRqpGifhUn0=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "5021eac20303a61fafe17224c087f5519baed54d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gradle2nix": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1755902591,
|
||||
"narHash": "sha256-mnPaPH9k6Mbr7O0KzBBdkiDDS88oB5NiFHVSFkCzswU=",
|
||||
"owner": "tadfisher",
|
||||
"repo": "gradle2nix",
|
||||
"rev": "30cfe5889188524223364ee7919d94e83d6ee44a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "tadfisher",
|
||||
"ref": "v2",
|
||||
"repo": "gradle2nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1758035966,
|
||||
"narHash": "sha256-qqIJ3yxPiB0ZQTT9//nFGQYn8X/PBoJbofA7hRKZnmE=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "8d4ddb19d03c65a36ad8d189d001dc32ffb0306b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-lib": {
|
||||
"locked": {
|
||||
"lastModified": 1754788789,
|
||||
"narHash": "sha256-x2rJ+Ovzq0sCMpgfgGaaqgBSwY+LST+WbZ6TytnT9Rk=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"rev": "a73b9c743612e4244d865a2fdee11865283c04e6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1763678758,
|
||||
"narHash": "sha256-+hBiJ+kG5IoffUOdlANKFflTT5nO3FrrR2CA3178Y5s=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "117cc7f94e8072499b0a7aa4c52084fa4e11cc9b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_3": {
|
||||
"locked": {
|
||||
"lastModified": 1763618868,
|
||||
"narHash": "sha256-v5afmLjn/uyD9EQuPBn7nZuaZVV9r+JerayK/4wvdWA=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a8d610af3f1a5fb71e23e08434d8d61a466fc942",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"build-gradle-application": "build-gradle-application",
|
||||
"gradle2nix": "gradle2nix",
|
||||
"nixpkgs": "nixpkgs_3"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
84
flake.nix
Normal file
84
flake.nix
Normal file
@ -0,0 +1,84 @@
|
||||
{
|
||||
description = "Misael server";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
|
||||
build-gradle-application.url = "git+file:/home/mysaa/Documents/Projets/buildGradleApplication";#"github:raphiz/buildGradleApplication";
|
||||
|
||||
gradle2nix.url = "github:tadfisher/gradle2nix/v2";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, build-gradle-application, gradle2nix, ... }@inputs:
|
||||
let system = "x86_64-linux";
|
||||
pkgs = import nixpkgs { inherit system; overlays = [ build-gradle-application.overlays.default ]; };
|
||||
lib = pkgs.lib;
|
||||
jdk = pkgs.openjdk21;
|
||||
gradle = pkgs.gradle;
|
||||
|
||||
misael = gradle2nix.builders.x86_64-linux.buildGradlePackage {
|
||||
pname = "misael";
|
||||
version = "1.0";
|
||||
lockFile = ./gradle.lock;
|
||||
gradleInstallFlags = [ "assemble" ];
|
||||
inherit gradle;
|
||||
buildJdk = jdk;
|
||||
src = ./.;
|
||||
buildPhase = ''
|
||||
gradle assemble
|
||||
'';
|
||||
installPhase = ''
|
||||
mkdir $out
|
||||
cp ./build/libs/misael-beta.jar $out/misael.jar
|
||||
cp -r ./build/resources/ $out/resources/
|
||||
'';
|
||||
};
|
||||
|
||||
misael-launcher = pkgs.writeScriptBin "misael" ''
|
||||
echo "Checking Database Migration"
|
||||
${pkgs.flyway} "-url=$MISAEL_DATABASE" -user=misael "-password=$MISAEL_PASSWORD" -locations="filesystem:${misael}/resources/main/db/migration/" -schemas=misael migrate
|
||||
|
||||
echo "Launching misael"
|
||||
${jdk}/bin/java -jar ${misael}/misael.jar --spring.datasource.url=$MISAEL_DATABASE --spring.datasource.password=$MISAEL_PASSWORD
|
||||
'';
|
||||
in {
|
||||
packages.${system} = {
|
||||
default = self.packages.${system}.misael;
|
||||
misael = misael;
|
||||
};
|
||||
|
||||
apps.${system} = {
|
||||
default = self.apps.${system}.misael;
|
||||
misael = { type = "app"; program = "${misael-launcher}/bin/misael"; };
|
||||
};
|
||||
|
||||
devShells.${system} = {
|
||||
default = pkgs.mkShell {
|
||||
packages = [
|
||||
jdk
|
||||
gradle
|
||||
(pkgs.vscode-with-extensions.override {
|
||||
vscode = pkgs.vscodium;
|
||||
vscodeExtensions = with pkgs.vscode-extensions; [
|
||||
redhat.java
|
||||
vscjava.vscode-java-debug
|
||||
vscjava.vscode-java-test
|
||||
vscjava.vscode-gradle
|
||||
vscjava.vscode-java-dependency
|
||||
sonarsource.sonarlint-vscode
|
||||
bbenoist.nix
|
||||
redhat.vscode-yaml
|
||||
];
|
||||
})
|
||||
];
|
||||
shellHook = ''
|
||||
echo "Starting Gradle daemon ..."
|
||||
gradle
|
||||
echo "Gradle daemon started."
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
formatter.${system} = pkgs.nixpkgs-fmt;
|
||||
};
|
||||
}
|
||||
2746
gradle.lock
Normal file
2746
gradle.lock
Normal file
File diff suppressed because it is too large
Load Diff
@ -4,7 +4,7 @@ import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
public enum Privilege implements GrantedAuthority {
|
||||
|
||||
LIST_USERS,ADD_USERS,LIST_QUIZZ;
|
||||
LIST_USERS,ADD_USERS,LIST_QUIZZ,CREATE_QUIZZ,VIEW_ALL_FORMS;
|
||||
|
||||
@Override
|
||||
public String getAuthority() {
|
||||
|
||||
@ -2,9 +2,8 @@ package com.bernard.misael.model;
|
||||
|
||||
import com.bernard.misael.questions.QTypes;
|
||||
import com.bernard.misael.questions.QuestionType;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.bernard.misael.service.JsonNodeConverter;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Getter;
|
||||
@ -36,19 +35,13 @@ public class Question {
|
||||
private Quizz quizz;
|
||||
|
||||
@Setter
|
||||
private String value;
|
||||
@Convert(converter = JsonNodeConverter.class)
|
||||
private JsonNode value;
|
||||
|
||||
transient QuestionType qtype = null;
|
||||
public QuestionType getQT() {
|
||||
if(qtype==null){
|
||||
ObjectMapper om = new ObjectMapper();
|
||||
JsonNode jsNode;
|
||||
try {
|
||||
jsNode = om.readTree(value);
|
||||
qtype = type.construct(jsNode);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
qtype = type.construct(value);
|
||||
}
|
||||
return qtype;
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@ public class Quizz {
|
||||
|
||||
@Column(nullable=false)
|
||||
@ColumnDefault("false")
|
||||
private boolean isPublic;
|
||||
private boolean isComplete;
|
||||
|
||||
@Column(nullable=false)
|
||||
@ColumnDefault("0")
|
||||
@ -39,4 +39,7 @@ public class Quizz {
|
||||
|
||||
@OneToMany(mappedBy="quizz")
|
||||
private Set<Question> questions;
|
||||
|
||||
@Column(nullable=true)
|
||||
private Integer publicQuestionCount;
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
package com.bernard.misael.repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import com.bernard.misael.model.Answer;
|
||||
@ -9,5 +11,6 @@ import com.bernard.misael.model.QuizzForm;
|
||||
public interface AnswerRepository extends JpaRepository<Answer,Long> {
|
||||
|
||||
public Answer findByFormAndQuestion(QuizzForm qf, Question q);
|
||||
public List<Answer> findByFormAndQuestionIn(QuizzForm qf, List<Question> qz);
|
||||
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package com.bernard.misael.repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
@ -12,5 +13,6 @@ public interface QuestionRepository extends JpaRepository<Question,Long> {
|
||||
|
||||
public Question findByQuizzAndIndex(Quizz quizz, int index);
|
||||
public Set<Question> findByQuizz(Quizz quizz);
|
||||
public List<Question> findByQuizzOrderByIndexAsc(Quizz quizz);
|
||||
|
||||
}
|
||||
@ -1,5 +1,7 @@
|
||||
package com.bernard.misael.repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import com.bernard.misael.model.Quizz;
|
||||
@ -8,6 +10,10 @@ import com.bernard.misael.model.User;
|
||||
|
||||
public interface QuizzFormRepository extends JpaRepository<QuizzForm,Long> {
|
||||
|
||||
public List<QuizzForm> findByQuizz(Quizz q);
|
||||
|
||||
public QuizzForm findByUserAndQuizz(User u, Quizz q);
|
||||
public List<QuizzForm> findByUserAndDoneTrue(User u);
|
||||
public List<QuizzForm> findByUserAndDoneFalse(User u);
|
||||
|
||||
}
|
||||
|
||||
@ -7,11 +7,14 @@ import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
import com.bernard.misael.model.Quizz;
|
||||
import com.bernard.misael.model.User;
|
||||
|
||||
public interface QuizzRepository extends JpaRepository<Quizz,Long> {
|
||||
|
||||
public @NonNull Optional<Quizz> findById(@NonNull Long id);
|
||||
|
||||
public @NonNull Set<Quizz> findByIsPublicTrue();
|
||||
public @NonNull Set<Quizz> findByPublicQuestionCountIsNotNullAndIsCompleteTrue();
|
||||
public @NonNull Set<Quizz> findByOwnerAndIsCompleteTrue(@NonNull User owner);
|
||||
public @NonNull Set<Quizz> findByOwnerAndIsCompleteFalse(@NonNull User owner);
|
||||
|
||||
}
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
package com.bernard.misael.service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
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.fasterxml.jackson.databind.JsonNode;
|
||||
@ -12,10 +14,15 @@ public interface QuizzManager {
|
||||
public JsonNode answer(User user, long quizzId,JsonNode data);
|
||||
public JsonNode next(User user, long quizzId);
|
||||
|
||||
public Quizz newQuizz(User user);
|
||||
|
||||
public boolean canAccessQuizz(User user, long quizzId);
|
||||
public List<Quizz> accessibleQuizz(User user);
|
||||
public List<Quizz> editableQuizz(User user);
|
||||
public List<Quizz> answerableQuizz(User user);
|
||||
|
||||
public boolean canEditQuizz(User user, long quizzId);
|
||||
public Optional<QuizzForm> canViewQuizzForm(User user, long quizzFormId);
|
||||
public Optional<Quizz> canViewQuizzFormsOfQuizz(User user, long quizzId);
|
||||
|
||||
public JsonNode getQuizzInfo(User user, long quizzId);
|
||||
public JsonNode setQuizzName(User user, long quizzId, String newName);
|
||||
@ -25,4 +32,9 @@ public interface QuizzManager {
|
||||
public JsonNode editQuestion(User user, long quizzId, long questionId, JsonNode value);
|
||||
public JsonNode setQuestionType(User user, long quizzId, long questionId, QTypes type);
|
||||
|
||||
public JsonNode getQuizzFormData(User user, long quizzFormId);
|
||||
public JsonNode getAllFormsData(User u, long id);
|
||||
public JsonNode getQuizzFormAdvancments(User user, long quizzId);
|
||||
|
||||
public Quizz duplicateQuizz(User user, long quizzId);
|
||||
}
|
||||
@ -14,23 +14,19 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.bernard.misael.model.Answer;
|
||||
import com.bernard.misael.model.Privilege;
|
||||
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;
|
||||
import com.bernard.misael.repository.QuizzFormRepository;
|
||||
import com.bernard.misael.repository.QuizzRepository;
|
||||
import com.bernard.misael.repository.*;
|
||||
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;
|
||||
@ -40,6 +36,9 @@ import jakarta.persistence.EntityNotFoundException;
|
||||
@Service
|
||||
public class QuizzManagerImpl implements QuizzManager {
|
||||
|
||||
@Autowired
|
||||
UserRepository uRepository;
|
||||
|
||||
@Autowired
|
||||
QuizzFormRepository qfRepository;
|
||||
|
||||
@ -52,6 +51,11 @@ public class QuizzManagerImpl implements QuizzManager {
|
||||
@Autowired
|
||||
AnswerRepository answerRepository;
|
||||
|
||||
@Autowired
|
||||
UserService uService;
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public JsonNode answer(User user, long quizzId, JsonNode data) {
|
||||
if(!data.has("index") || !data.get("index").isInt())
|
||||
@ -65,6 +69,8 @@ public class QuizzManagerImpl implements QuizzManager {
|
||||
Optional<Quizz> oquizz = qRepository.findById(quizzId);
|
||||
if(!oquizz.isPresent())
|
||||
return errorNode("Could not find the quizz with id "+quizzId);
|
||||
if(!oquizz.get().isComplete())
|
||||
return errorNode("Quizz is not complete");
|
||||
Quizz quizz = oquizz.get();
|
||||
QuizzForm qf = qfRepository.findByUserAndQuizz(user, quizz);
|
||||
if(qf == null)
|
||||
@ -75,6 +81,8 @@ public class QuizzManagerImpl implements QuizzManager {
|
||||
if(qindex != data.get("index").intValue())
|
||||
return errorNode("You are not answering the right question (you answer question "+data.get("index").intValue()
|
||||
+" where you should answer question "+qindex+")");
|
||||
if(qindex >= Optional.ofNullable(quizz.getPublicQuestionCount()).orElse(Integer.MAX_VALUE))
|
||||
return errorNode("La question suivante est encore bloquée");
|
||||
Question q = questionRepository.findByQuizzAndIndex(quizz,qindex);
|
||||
if(q == null)
|
||||
return errorNode("Could not find question "+qindex);
|
||||
@ -124,6 +132,8 @@ public class QuizzManagerImpl implements QuizzManager {
|
||||
Optional<Quizz> oquizz = qRepository.findById(quizzId);
|
||||
if(!oquizz.isPresent())
|
||||
return errorNode("Could not find quizz with id "+quizzId);
|
||||
if(!oquizz.get().isComplete())
|
||||
return errorNode("Quizz is not complete");
|
||||
Quizz quizz = oquizz.get();
|
||||
QuizzForm qf = qfRepository.findByUserAndQuizz(user, quizz);
|
||||
if(qf == null){
|
||||
@ -133,6 +143,8 @@ public class QuizzManagerImpl implements QuizzManager {
|
||||
if(qf.isDone())
|
||||
return errorNode("No more questions");
|
||||
int qindex = qf.getCurrentQuestion();
|
||||
if(qindex >= Optional.ofNullable(quizz.getPublicQuestionCount()).orElse(Integer.MAX_VALUE))
|
||||
return errorNode("La question suivante est encore bloquée");
|
||||
Question q = questionRepository.findByQuizzAndIndex(quizz,qindex);
|
||||
if(q == null)
|
||||
return errorNode("Could not find question "+qindex);
|
||||
@ -165,7 +177,7 @@ public class QuizzManagerImpl implements QuizzManager {
|
||||
return out;
|
||||
}
|
||||
|
||||
public QuizzForm newQuizzForm(User user, Quizz quizz) {
|
||||
private QuizzForm newQuizzForm(User user, Quizz quizz) {
|
||||
QuizzForm qf = new QuizzForm();
|
||||
qf.setUser(user);
|
||||
qf.setQuizz(quizz);
|
||||
@ -176,7 +188,16 @@ public class QuizzManagerImpl implements QuizzManager {
|
||||
return qf;
|
||||
}
|
||||
|
||||
public static final JsonNode errorNode(String err){
|
||||
@Override
|
||||
public Quizz newQuizz(User user) {
|
||||
Quizz q = new Quizz();
|
||||
q.setName("Super questions de "+user.getName()+" ("+Integer.toHexString((int)(Math.random()*0xFFFFFFF))+")");
|
||||
q.setOwner(user);
|
||||
q = qRepository.save(q);
|
||||
return q;
|
||||
}
|
||||
|
||||
private static final JsonNode errorNode(String err){
|
||||
ObjectNode out = JsonNodeFactory.instance.objectNode();
|
||||
out.set("success", JsonNodeFactory.instance.booleanNode(false));
|
||||
out.set("message", JsonNodeFactory.instance.textNode(err));
|
||||
@ -187,16 +208,16 @@ public class QuizzManagerImpl implements QuizzManager {
|
||||
public boolean canAccessQuizz(User user, long quizzId) {
|
||||
try{
|
||||
Quizz quizz = qRepository.getReferenceById(quizzId);
|
||||
return quizz.isPublic() || quizz.getOwner().equals(user);
|
||||
return quizz.getPublicQuestionCount()!=null || quizz.getOwner().equals(user);
|
||||
} catch (EntityNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Quizz> accessibleQuizz(User user) {
|
||||
Set<Quizz> ownQuizz = user.getMyQuizzs();
|
||||
Set<Quizz> publicQuizz = qRepository.findByIsPublicTrue();
|
||||
public List<Quizz> answerableQuizz(User user) {
|
||||
Set<Quizz> ownQuizz = qRepository.findByOwnerAndIsCompleteTrue(user);
|
||||
Set<Quizz> publicQuizz = qRepository.findByPublicQuestionCountIsNotNullAndIsCompleteTrue();
|
||||
publicQuizz.removeAll(ownQuizz);
|
||||
|
||||
return Stream.concat(
|
||||
@ -207,6 +228,14 @@ public class QuizzManagerImpl implements QuizzManager {
|
||||
).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Quizz> editableQuizz(User user) {
|
||||
Set<Quizz> ownQuizz = qRepository.findByOwnerAndIsCompleteFalse(user);
|
||||
return ownQuizz.stream()
|
||||
.sorted((q1,q2) -> q1.getName().compareTo(q2.getName()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
Logger logger = LoggerFactory.getLogger(QuestionsController.class);
|
||||
@Override
|
||||
public boolean canEditQuizz(User user, long quizzId) {
|
||||
@ -228,6 +257,8 @@ public class QuizzManagerImpl implements QuizzManager {
|
||||
Optional<Quizz> oquizz = qRepository.findById(quizzId);
|
||||
if(!oquizz.isPresent())
|
||||
return Optional.of(errorNode("Could not find quizz with id "+quizzId));
|
||||
if(oquizz.get().isComplete())
|
||||
return Optional.of(errorNode("Quizz is complete, cannot edit, answers might have already been cast"));
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@ -245,12 +276,7 @@ public class QuizzManagerImpl implements QuizzManager {
|
||||
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);
|
||||
}
|
||||
nn.set("value",q.getValue());
|
||||
n.set(q.getIndex(),nn);
|
||||
}
|
||||
|
||||
@ -289,7 +315,7 @@ public class QuizzManagerImpl implements QuizzManager {
|
||||
Question q = new Question();
|
||||
q.setType(DEFAULT_QTYPE);
|
||||
JsonNode n = DEFAULT_QTYPE.getDefaultQuestion();
|
||||
q.setValue(n.toString());
|
||||
q.setValue(n);
|
||||
q.setQuizz(quizz);
|
||||
q.setIndex(quizz.getQuestionCount());
|
||||
quizz.setQuestionCount(quizz.getQuestionCount()+1);
|
||||
@ -297,6 +323,7 @@ public class QuizzManagerImpl implements QuizzManager {
|
||||
|
||||
ObjectNode out = JsonNodeFactory.instance.objectNode();
|
||||
out.set("success", JsonNodeFactory.instance.booleanNode(true));
|
||||
out.set("id", JsonNodeFactory.instance.numberNode(q.getId()));
|
||||
out.set("type", JsonNodeFactory.instance.textNode(DEFAULT_QTYPE.name()));
|
||||
out.set("value", n);
|
||||
return out;
|
||||
@ -384,7 +411,7 @@ public class QuizzManagerImpl implements QuizzManager {
|
||||
|
||||
if(!q.getType().validate(value))
|
||||
return errorNode("Invalid question value");
|
||||
q.setValue(value.toString());
|
||||
q.setValue(value);
|
||||
|
||||
ObjectNode out = JsonNodeFactory.instance.objectNode();
|
||||
out.set("success", JsonNodeFactory.instance.booleanNode(true));
|
||||
@ -414,14 +441,9 @@ public class QuizzManagerImpl implements QuizzManager {
|
||||
// Then we need to change
|
||||
q.setType(type);
|
||||
n = type.getDefaultQuestion();
|
||||
q.setValue(n.toString());
|
||||
q.setValue(n);
|
||||
} 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);
|
||||
}
|
||||
n =q.getValue();
|
||||
}
|
||||
|
||||
ObjectNode out = JsonNodeFactory.instance.objectNode();
|
||||
@ -430,4 +452,150 @@ public class QuizzManagerImpl implements QuizzManager {
|
||||
return out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<QuizzForm> canViewQuizzForm(User user, long quizzFormId) {
|
||||
Optional<QuizzForm> oqf = qfRepository.findById(quizzFormId);
|
||||
if(oqf.isEmpty()) return oqf;
|
||||
QuizzForm qf = oqf.get();
|
||||
if(!qf.isDone()) return Optional.empty();
|
||||
if(qf.getUser().equals(user)) return oqf;
|
||||
if(uService.hasPrivilege(user, Privilege.VIEW_ALL_FORMS)) return oqf;
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Quizz> canViewQuizzFormsOfQuizz(User user, long quizzId) {
|
||||
Optional<Quizz> oq = qRepository.findById(quizzId);
|
||||
if(oq.isEmpty()) return oq;
|
||||
Quizz q = oq.get();
|
||||
if(q.getOwner().equals(user)) return oq;
|
||||
if(uService.hasPrivilege(user, Privilege.VIEW_ALL_FORMS)) return oq;
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonNode getQuizzFormData(User user, long quizzFormId) {
|
||||
Optional<QuizzForm> oqf = canViewQuizzForm(user, quizzFormId);
|
||||
if(oqf.isEmpty())
|
||||
return errorNode("Could not access the quizzform"); //TODO more precise error node
|
||||
QuizzForm form = oqf.get();
|
||||
List<Question> questions = questionRepository.findByQuizzOrderByIndexAsc(form.getQuizz());
|
||||
List<Answer> answers = answerRepository.findByFormAndQuestionIn(form, questions);
|
||||
assert questions.size() == answers.size();
|
||||
ArrayNode answersNode = JsonNodeFactory.instance.arrayNode();
|
||||
for(int i=0;i<questions.size();i++) {
|
||||
ObjectNode anode = JsonNodeFactory.instance.objectNode();
|
||||
anode.set("qid", JsonNodeFactory.instance.numberNode(questions.get(i).getId()));
|
||||
anode.set("type", JsonNodeFactory.instance.textNode(questions.get(i).getType().name()));
|
||||
anode.set("qvalue", questions.get(i).getValue());
|
||||
anode.set("aid", JsonNodeFactory.instance.numberNode(answers.get(i).getId()));
|
||||
anode.set("avalue", answers.get(i).getValue());
|
||||
answersNode.add(anode);
|
||||
}
|
||||
ObjectNode out = JsonNodeFactory.instance.objectNode();
|
||||
out.set("success", JsonNodeFactory.instance.booleanNode(true));
|
||||
out.set("data", answersNode);
|
||||
return out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonNode getAllFormsData(User u, long quizzId) {
|
||||
Optional<Quizz> oq = canViewQuizzFormsOfQuizz(u, quizzId);
|
||||
if(oq.isEmpty())
|
||||
return errorNode("Could not access the quizzform"); //TODO more precise error node
|
||||
Quizz quizz = oq.get();
|
||||
ObjectNode out = JsonNodeFactory.instance.objectNode();
|
||||
out.set("id",JsonNodeFactory.instance.numberNode(quizz.getId()));
|
||||
out.set("name",JsonNodeFactory.instance.textNode(quizz.getName()));
|
||||
|
||||
ArrayNode questionNode = JsonNodeFactory.instance.arrayNode(quizz.getQuestionCount());
|
||||
List<Question> questions = quizz.getQuestions().stream().sorted((p,q) -> Integer.valueOf(p.getIndex()).compareTo(q.getIndex())).toList();
|
||||
for(int i=0;i<questions.size();i++) {
|
||||
ObjectNode qNode = JsonNodeFactory.instance.objectNode();
|
||||
qNode.set("id", JsonNodeFactory.instance.numberNode(questions.get(i).getId()));
|
||||
qNode.set("type", JsonNodeFactory.instance.textNode(questions.get(i).getType().name()));
|
||||
qNode.set("value",questions.get(i).getValue());
|
||||
questionNode.add(qNode);
|
||||
}
|
||||
out.set("questions", questionNode);
|
||||
|
||||
ObjectNode qfzNodes = JsonNodeFactory.instance.objectNode();
|
||||
for(QuizzForm qf : qfRepository.findByQuizz(quizz)) {
|
||||
ObjectNode qfNode = JsonNodeFactory.instance.objectNode();
|
||||
qfNode.set("id", JsonNodeFactory.instance.numberNode(qf.getId()));
|
||||
qfNode.set("currentQuestion", JsonNodeFactory.instance.numberNode(qf.getCurrentQuestion()));
|
||||
qfNode.set("answerStep", JsonNodeFactory.instance.numberNode(qf.getAnswerStep()));
|
||||
qfNode.set("done", JsonNodeFactory.instance.booleanNode(qf.isDone()));
|
||||
|
||||
List<Answer> answers = answerRepository.findByFormAndQuestionIn(qf, questions);
|
||||
ArrayNode answersNode = JsonNodeFactory.instance.arrayNode();
|
||||
for(int i=0;i<answers.size();i++) {
|
||||
answersNode.add(answers.get(i).getValue());
|
||||
}
|
||||
qfNode.set("answers", answersNode);
|
||||
qfzNodes.set(qf.getUser().getName(), qfNode);
|
||||
}
|
||||
out.set("forms", qfzNodes);
|
||||
|
||||
out.set("success", JsonNodeFactory.instance.booleanNode(true));
|
||||
return out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonNode getQuizzFormAdvancments(User user, long quizzId) {
|
||||
Optional<Quizz> oq = canViewQuizzFormsOfQuizz(user, quizzId);
|
||||
if(oq.isEmpty())
|
||||
return errorNode("Could not access the forms for this quizz"); //TODO more precise error node
|
||||
Quizz quizz = oq.get();
|
||||
List<QuizzForm> quizzForms = qfRepository.findByQuizz(quizz);
|
||||
quizzForms.sort((qfa,qfb) -> qfa.getUser().getName().compareTo(qfb.getUser().getName()));
|
||||
|
||||
ArrayNode formsNode = JsonNodeFactory.instance.arrayNode();
|
||||
for(QuizzForm qf : quizzForms) {
|
||||
ObjectNode anode = JsonNodeFactory.instance.objectNode();
|
||||
anode.set("qfid", JsonNodeFactory.instance.numberNode(qf.getId()));
|
||||
anode.set("done", JsonNodeFactory.instance.booleanNode(qf.isDone()));
|
||||
anode.set("position", JsonNodeFactory.instance.numberNode(qf.getCurrentQuestion()));
|
||||
anode.set("step", JsonNodeFactory.instance.numberNode(qf.getAnswerStep()));
|
||||
anode.set("username", JsonNodeFactory.instance.textNode(qf.getUser().getName()));
|
||||
formsNode.add(anode);
|
||||
}
|
||||
ObjectNode out = JsonNodeFactory.instance.objectNode();
|
||||
out.set("success", JsonNodeFactory.instance.booleanNode(true));
|
||||
out.set("data", formsNode);
|
||||
return out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Quizz duplicateQuizz(User u, long quizzId) {
|
||||
if (!canEditQuizz(u, quizzId))
|
||||
return null;
|
||||
Optional<Quizz> oq = qRepository.findById(quizzId);
|
||||
// CHECKED BEFORE if (oq.isEmpty()) return null;
|
||||
Quizz q = oq.get();
|
||||
|
||||
|
||||
Quizz nq = new Quizz();
|
||||
nq.setName(q.getName() + "("+ Integer.toHexString((int)(Math.random()*0xFFFFFFF)) +")");
|
||||
nq.setOwner(q.getOwner());
|
||||
nq.setQuestionCount(q.getQuestionCount());
|
||||
nq.setPublicQuestionCount(q.getPublicQuestionCount());
|
||||
nq.setComplete(q.isComplete());
|
||||
|
||||
nq = qRepository.save(nq);
|
||||
|
||||
// We duplicate questions
|
||||
for (Question qu : q.getQuestions()) {
|
||||
Question nqu = new Question();
|
||||
nqu.setIndex(qu.getIndex());
|
||||
nqu.setType(qu.getType());
|
||||
nqu.setValue(qu.getValue());
|
||||
nqu.setQuizz(nq);
|
||||
questionRepository.save(nqu);
|
||||
}
|
||||
return nq;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ package com.bernard.misael.service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.bernard.misael.model.Privilege;
|
||||
import com.bernard.misael.model.User;
|
||||
import com.bernard.misael.service.dto.UserDto;
|
||||
|
||||
@ -13,4 +14,6 @@ public interface UserService {
|
||||
User findUserByName(String name);
|
||||
|
||||
List<UserDto> findAllUsers();
|
||||
|
||||
boolean hasPrivilege(User u, Privilege p);
|
||||
}
|
||||
@ -5,6 +5,7 @@ import java.util.stream.Collectors;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.bernard.misael.model.Privilege;
|
||||
import com.bernard.misael.model.User;
|
||||
import com.bernard.misael.repository.UserRepository;
|
||||
import com.bernard.misael.service.dto.UserDto;
|
||||
@ -54,4 +55,10 @@ public class UserServiceImpl implements UserService {
|
||||
userDto.setName(user.getName());
|
||||
return userDto;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPrivilege(User u, Privilege p) {
|
||||
//TODO faire une query sql propre avec ça
|
||||
return u.getRoles().stream().anyMatch(r -> r.getPrivileges().contains(p));
|
||||
}
|
||||
}
|
||||
@ -6,7 +6,6 @@ import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
@ -41,21 +40,8 @@ public class AuthController {
|
||||
return "login";
|
||||
}
|
||||
|
||||
public User getLoggedInUser() {
|
||||
if(SecurityContextHolder.getContext().getAuthentication().getPrincipal()
|
||||
instanceof org.springframework.security.core.userdetails.User){
|
||||
org.springframework.security.core.userdetails.User user
|
||||
= (org.springframework.security.core.userdetails.User)
|
||||
SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||
return userService.findUserByName(user.getUsername());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/")
|
||||
public String index(Model model) {
|
||||
|
||||
return "index";
|
||||
}
|
||||
|
||||
|
||||
@ -11,13 +11,16 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
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.repository.QuizzFormRepository;
|
||||
import com.bernard.misael.repository.QuizzRepository;
|
||||
import com.bernard.misael.repository.UserRepository;
|
||||
import com.bernard.misael.service.QuizzManager;
|
||||
@ -45,28 +48,137 @@ public class QuestionsController {
|
||||
@Autowired
|
||||
QuizzRepository qrepo;
|
||||
|
||||
@GetMapping("/quizz")
|
||||
@Autowired
|
||||
QuizzFormRepository qfrepo;
|
||||
|
||||
/*
|
||||
* List all quizz
|
||||
*/
|
||||
@GetMapping("/quizz")
|
||||
public String getQuizz(Model model, Principal p) {
|
||||
User u = null;
|
||||
if (p!=null)
|
||||
u = ur.findByName(p.getName());
|
||||
if(u!=null) {
|
||||
model.addAttribute("quizz",qm.accessibleQuizz(u));
|
||||
model.addAttribute("answerableQuizz",qm.answerableQuizz(u));
|
||||
model.addAttribute("editableQuizz",qm.editableQuizz(u));
|
||||
}
|
||||
|
||||
return "quizz.html";
|
||||
}
|
||||
|
||||
@GetMapping("/forms")
|
||||
/*
|
||||
* List all forms started or finished by the user
|
||||
*/
|
||||
public String getForms() {
|
||||
@GetMapping("/forms")
|
||||
public String getForms(Model model, Principal p) {
|
||||
User u = null;
|
||||
if (p!=null)
|
||||
u = ur.findByName(p.getName());
|
||||
if(u!=null) {
|
||||
model.addAttribute("finishedForms",qfrepo.findByUserAndDoneTrue(u));
|
||||
model.addAttribute("openForms",qfrepo.findByUserAndDoneFalse(u));
|
||||
}
|
||||
|
||||
return "forms.html";
|
||||
}
|
||||
|
||||
/*
|
||||
* Show one (completed) form of one user
|
||||
*/
|
||||
@GetMapping("/showform/{id}")
|
||||
public Object showForm(@PathVariable("id") long id, Model m, Principal p) {
|
||||
User u = null;
|
||||
if (p!=null)
|
||||
u = ur.findByName(p.getName());
|
||||
if(u==null)
|
||||
return "redirect:/login?restricted";
|
||||
Optional<QuizzForm> oqf = qm.canViewQuizzForm(u, id);
|
||||
if (oqf.isEmpty())
|
||||
//TODO Faire un mesasge d'erreur dépendant des circonstances (unatuhorized, not found, not complete ...)
|
||||
return new ResponseEntity<>(JsonNodeFactory.instance.objectNode(),HttpStatus.UNAUTHORIZED);
|
||||
|
||||
m.addAttribute("formId", id);
|
||||
return "showform.html";
|
||||
}
|
||||
|
||||
/*
|
||||
* Show one (completed) form of one user
|
||||
*/
|
||||
@GetMapping("/showformsadvancements/{id}")
|
||||
public Object showFormsAdvancements(@PathVariable("id") long id, Model m, Principal p) {
|
||||
User u = null;
|
||||
if (p!=null)
|
||||
u = ur.findByName(p.getName());
|
||||
if(u==null)
|
||||
return "redirect:/login?restricted";
|
||||
Optional<Quizz> oq = qm.canViewQuizzFormsOfQuizz(u, id);
|
||||
if (oq.isEmpty())
|
||||
//TODO Faire un mesasge d'erreur dépendant des circonstances (unatuhorized, not found, not complete ...)
|
||||
return new ResponseEntity<>(JsonNodeFactory.instance.objectNode(),HttpStatus.UNAUTHORIZED);
|
||||
|
||||
m.addAttribute("quizzId", id);
|
||||
return "showformadvancements.html";
|
||||
}
|
||||
|
||||
/*
|
||||
* API get the form
|
||||
*/
|
||||
@PostMapping("/getformdata/{id}")
|
||||
public Object showFormApi(@PathVariable("id") long id, Principal p) {
|
||||
User u = null;
|
||||
if (p!=null)
|
||||
u = ur.findByName(p.getName());
|
||||
if(u==null)
|
||||
return "redirect:/login?restricted";
|
||||
JsonNode out = qm.getQuizzFormData(u, id);
|
||||
return new ResponseEntity<>(out, HttpStatus.OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* API get the forms for a specific quizz
|
||||
*/
|
||||
@GetMapping("/getallformsdata/{id}")
|
||||
public Object getAllFormsData(@PathVariable("id") long id, Principal p) {
|
||||
User u = null;
|
||||
if (p!=null)
|
||||
u = ur.findByName(p.getName());
|
||||
if(u==null)
|
||||
return "redirect:/login?restricted";
|
||||
JsonNode out = qm.getAllFormsData(u, id);
|
||||
return new ResponseEntity<>(out, HttpStatus.OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* API get the form
|
||||
*/
|
||||
@PostMapping("/getformadvancements/{id}")
|
||||
public Object showFormAdvancements(@PathVariable("id") long id, Principal p) {
|
||||
User u = null;
|
||||
if (p!=null)
|
||||
u = ur.findByName(p.getName());
|
||||
if(u==null)
|
||||
return "redirect:/login?restricted";
|
||||
JsonNode out = qm.getQuizzFormAdvancments(u, id);
|
||||
return new ResponseEntity<>(out, HttpStatus.OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* API get the form
|
||||
*/
|
||||
@GetMapping("/duplicate-quizz/{id}")
|
||||
public Object duplicateQuizz(@PathVariable("id") long id, Principal p) {
|
||||
User u = null;
|
||||
if (p!=null)
|
||||
u = ur.findByName(p.getName());
|
||||
if(u==null)
|
||||
return "redirect:/login?restricted";
|
||||
Quizz q = qm.duplicateQuizz(u, id);
|
||||
if(q == null)
|
||||
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
|
||||
return new ResponseEntity<>(HttpStatus.OK);
|
||||
}
|
||||
|
||||
@GetMapping("/form/{q}")
|
||||
public String formpage(@PathVariable("q") long quizzId, Principal p, Model m) {
|
||||
if (p==null)
|
||||
@ -98,6 +210,18 @@ public class QuestionsController {
|
||||
return new ResponseEntity<>(out, HttpStatus.OK);
|
||||
}
|
||||
|
||||
@GetMapping("/new-quizz")
|
||||
@Secured("CREATE_QUIZZ")
|
||||
public Object newQuizz(Principal p, Model m) {
|
||||
if (p==null)
|
||||
return "redirect:/login?restricted";
|
||||
User u = ur.findByName(p.getName());
|
||||
if (u==null)
|
||||
return new ResponseEntity<>(HttpStatus.FORBIDDEN);
|
||||
Quizz q = qm.newQuizz(u);
|
||||
|
||||
return "redirect:/questions/quizz-edit/"+Long.toString(q.getId());
|
||||
}
|
||||
|
||||
Logger logger = LoggerFactory.getLogger(QuestionsController.class);
|
||||
@GetMapping("/quizz-edit/{q}")
|
||||
@ -105,7 +229,6 @@ public class QuestionsController {
|
||||
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);
|
||||
|
||||
@ -0,0 +1 @@
|
||||
alter table quizz add column "is_complete" BOOLEAN NOT NULL DEFAULT FALSE;
|
||||
8
src/main/resources/db/migration/V5__new_privileges.sql
Normal file
8
src/main/resources/db/migration/V5__new_privileges.sql
Normal file
@ -0,0 +1,8 @@
|
||||
alter table role_privileges drop constraint role_privileges_privileges_check;
|
||||
alter table role_privileges add constraint role_privileges_privileges_check
|
||||
check (privileges in ('LIST_USERS','ADD_USERS','LIST_QUIZZ','CREATE_QUIZZ','VIEW_ALL_FORMS'));
|
||||
|
||||
insert into role_privileges VALUES
|
||||
((select id from roles where "name" = 'ADMIN'),'CREATE_QUIZZ');
|
||||
insert into role_privileges VALUES
|
||||
((select id from roles where "name" = 'ADMIN'),'VIEW_ALL_FORMS');
|
||||
@ -0,0 +1,2 @@
|
||||
alter table if exists quizz add column public_question_count integer default NULL;
|
||||
alter table if exists quizz drop column is_public;
|
||||
@ -1,3 +1,22 @@
|
||||
main h1 input, main h1 span{
|
||||
font-size: 30pt;
|
||||
font-weight: bold;
|
||||
color: teal;
|
||||
}
|
||||
|
||||
main h1 button{
|
||||
width: 7em;
|
||||
}
|
||||
|
||||
main .buttonbar button{
|
||||
width: 7em;
|
||||
}
|
||||
|
||||
main .buttonbar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
ol#questions-list {
|
||||
display: table;
|
||||
border-spacing: 2px;
|
||||
|
||||
28
src/main/resources/static/css/showform.css
Normal file
28
src/main/resources/static/css/showform.css
Normal file
@ -0,0 +1,28 @@
|
||||
main h1 input, main h1 span{
|
||||
font-size: 30pt;
|
||||
font-weight: bold;
|
||||
color: teal;
|
||||
}
|
||||
|
||||
ol#questions-list {
|
||||
width: 90%;
|
||||
}
|
||||
ol#questions-list li {
|
||||
width: 90%;
|
||||
background-color: lemonchiffon;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* DCC */
|
||||
ol#questions-list li.dcc-box {
|
||||
background-color: gainsboro;
|
||||
color: black;
|
||||
font-size: 12pt;
|
||||
}
|
||||
li.dcc-box h5 {
|
||||
font-size: 12pt;
|
||||
font-weight: bold;
|
||||
width: 100%;
|
||||
}
|
||||
33
src/main/resources/static/css/showformadvancements.css
Normal file
33
src/main/resources/static/css/showformadvancements.css
Normal file
@ -0,0 +1,33 @@
|
||||
main h1 input, main h1 span{
|
||||
font-size: 30pt;
|
||||
font-weight: bold;
|
||||
color: teal;
|
||||
}
|
||||
|
||||
table#forms-table {
|
||||
width: 90%;
|
||||
}
|
||||
table#forms-table tr {
|
||||
width: 90%;
|
||||
background-color: lemonchiffon;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* DCC */
|
||||
table#forms-table tbody tr.done-true {
|
||||
background-color: rgb(161, 161, 161);
|
||||
color: black;
|
||||
font-size: 12pt;
|
||||
font-style: italic;
|
||||
}
|
||||
table#forms-table tbody tr {
|
||||
background-color: gainsboro;
|
||||
color: black;
|
||||
font-size: 12pt;
|
||||
}
|
||||
table#forms-table thead tr {
|
||||
font-size: 14pt;
|
||||
font-weight: bold;
|
||||
}
|
||||
@ -44,13 +44,13 @@ nav .navbar .menu{
|
||||
}
|
||||
main{
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #96c7e8;
|
||||
font-size: 24px;
|
||||
flex-direction: column;
|
||||
margin-top: 115px;
|
||||
}
|
||||
.button a{
|
||||
position: fixed;
|
||||
|
||||
@ -15,11 +15,11 @@
|
||||
<div class="buttonbox" id="answer-select">
|
||||
<button class="squared-button bcol1" id="answer-select-duo">Duo</button>
|
||||
<button class="squared-button bcol2" id="answer-select-carre">Carré</button>
|
||||
<button class="squared-button bcol3" id="answer-select-cache">Cache</button>
|
||||
<button class="squared-button bcol3" id="answer-select-cash">Cash</button>
|
||||
</div>
|
||||
<div class="buttonbox" id="answer-cache">
|
||||
<textarea class="squared-input" id="answer-cache-input" type="text"></textarea>
|
||||
<button class="squared-button bcol4" id="answer-cache-button">Envoyer la réponse</button>
|
||||
<div class="buttonbox" id="answer-cash">
|
||||
<textarea class="squared-input" id="answer-cash-input" type="text"></textarea>
|
||||
<button class="squared-button bcol4" id="answer-cash-button">Envoyer la réponse</button>
|
||||
</div>
|
||||
<div class="buttonbox" id="answer-duo">
|
||||
<button class="squared-button bcol1" id="answer-duo-button-1"></button>
|
||||
@ -47,6 +47,9 @@
|
||||
console.log(txt)
|
||||
$("#error-textbox").text(txt)
|
||||
}
|
||||
function clearerror() {
|
||||
$("#error-textbox").text("")
|
||||
}
|
||||
function next() {
|
||||
$.ajax({
|
||||
url: "/questions/question/"+qid,
|
||||
@ -61,23 +64,24 @@
|
||||
error(res["message"])
|
||||
return
|
||||
}
|
||||
clearerror()
|
||||
$("#question-counter").text((res["index"]+1)+"/"+qlength)
|
||||
$("#question-text").text(res["data"]["text"])
|
||||
if(qstep==0){
|
||||
$("#answer-select").show()
|
||||
$("#answer-duo").hide()
|
||||
$("#answer-carre").hide()
|
||||
$("#answer-cache").hide()
|
||||
$("#answer-cash").hide()
|
||||
}else if(qstep==1){
|
||||
awrs = res["data"]["answers"]
|
||||
|
||||
$("#answer-select").hide()
|
||||
$("#answer-duo").hide()
|
||||
$("#answer-carre").hide()
|
||||
$("#answer-cache").hide()
|
||||
$("#answer-cash").hide()
|
||||
if(typeof awrs === 'undefined'){
|
||||
$("#answer-cache").show()
|
||||
$("#answer-cache-input").val("")
|
||||
$("#answer-cash").show()
|
||||
$("#answer-cash-input").val("")
|
||||
} else if (awrs.length == 2) {
|
||||
$("#answer-duo").show()
|
||||
$("#answer-duo-button-1").text(res.data.answers[0])
|
||||
@ -108,6 +112,7 @@
|
||||
success: function(res) {
|
||||
console.log(res)
|
||||
if(res["success"]){
|
||||
clearerror()
|
||||
next()
|
||||
} else {
|
||||
error(res["message"])
|
||||
@ -130,14 +135,15 @@
|
||||
success: function(res) {
|
||||
console.log(res)
|
||||
if(res["success"]){
|
||||
if(qindex+1 >= qlength) {
|
||||
$("#question-text").text("Plus de questions !")
|
||||
clearerror()
|
||||
$("#answer-select").hide()
|
||||
$("#answer-duo").hide()
|
||||
$("#answer-carre").hide()
|
||||
$("#answer-cache").hide()
|
||||
|
||||
$("#answer-cash").hide()
|
||||
if(qindex+1 >= qlength) {
|
||||
$("#question-text").text("Plus de questions !")
|
||||
} else {
|
||||
$("#question-text").text("Question suivante ...")
|
||||
next()
|
||||
}
|
||||
} else {
|
||||
@ -151,11 +157,11 @@
|
||||
$("#answer-select").hide()
|
||||
$("#answer-duo").hide()
|
||||
$("#answer-carre").hide()
|
||||
$("#answer-cache").hide()
|
||||
$("#answer-cash").hide()
|
||||
$("#answer-select-duo").on('click',() => answer1(2))
|
||||
$("#answer-select-carre").on('click',() => answer1(4))
|
||||
$("#answer-select-cache").on('click',() => answer1(0))
|
||||
$("#answer-cache-button").on('click',() => answer2($("#answer-cache-input").val()))
|
||||
$("#answer-select-cash").on('click',() => answer1(0))
|
||||
$("#answer-cash-button").on('click',() => answer2($("#answer-cash-input").val()))
|
||||
$("#answer-duo-button-1").on('click',() => answer2(awrs[0]))
|
||||
$("#answer-duo-button-2").on('click',() => answer2(awrs[1]))
|
||||
$("#answer-carre-button-1").on('click',() => answer2(awrs[0]))
|
||||
|
||||
@ -7,11 +7,15 @@
|
||||
<body>
|
||||
<div th:replace="~{header}"/>
|
||||
<main>
|
||||
|
||||
<h3>Formulaires terminés</h3>
|
||||
<ul>
|
||||
<li><a href="/questions/form?f=132">Formulaire de réponse au quizz numéro 1</a></li>
|
||||
<li><a href="/questions/form?f=177">Formulaire de réponse au quizz numéro 2</a></li>
|
||||
<li><a href="/questions/form?f=16818">Formulaire de réponse au quizz numéro 3</a></li>
|
||||
<li th:if="${#lists.isEmpty(finishedForms)}">Vous n'avez fini aucun formulaire !</li>
|
||||
<li th:each="qf : ${finishedForms}"><a th:href="@{/questions/showform/{id}(id=${qf.id})}">Quizz <span th:text="${qf.quizz.name}"/></a></li>
|
||||
</ul>
|
||||
<h3>Formulaires non terminés</h3>
|
||||
<ul>
|
||||
<li th:if="${#lists.isEmpty(openForms)}">Aucun formulaire ici !</li>
|
||||
<li th:each="qf : ${openForms}"><a th:href="@{/questions/form/{id}(id=${qf.quizz.id})}">Quizz <span th:text="${qf.quizz.name}"/></a></li>
|
||||
</ul>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
@ -10,10 +10,26 @@
|
||||
<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>
|
||||
<h1>Quizz
|
||||
<span id="question-name"></span>
|
||||
<input id="edit-question-name"/>
|
||||
<button id="edit-question-name-button">Edit</button>
|
||||
<button id="edit-question-name-confirm">Valider</button>
|
||||
<button id="edit-question-name-cancel">Annuler</button>
|
||||
</h1>
|
||||
|
||||
<div class="buttonbar">
|
||||
<button class="display-button" id="add-question">Ajouter</button>
|
||||
<button class="display-button" id="reorder-questions">Réordonner</button>
|
||||
<button class="reorder-button" id="reorder-questions-commit">Valider l'ordre</button>
|
||||
<button class="reorder-button" id="reorder-questions-cancel">Annuler</button>
|
||||
</div>
|
||||
|
||||
<ol id="questions-list">
|
||||
</ol>
|
||||
|
||||
<span id="error-textbox" style="color: red"></span>
|
||||
|
||||
<script th:inline="javascript">
|
||||
/*<![CDATA[*/
|
||||
var quizzid = /*[[${quizzId}]]*/ -1;
|
||||
@ -31,7 +47,16 @@
|
||||
"read": readEditBlockBCC
|
||||
}
|
||||
}
|
||||
|
||||
// question position -> question data
|
||||
questions={}
|
||||
// Map question id -> question position
|
||||
qpositions={}
|
||||
// the name of the quizz
|
||||
quizzName = ""
|
||||
|
||||
editing = new Set()
|
||||
|
||||
function getdata() {
|
||||
$.ajax({
|
||||
url: "/questions/quizz-edit/"+quizzid+"/get",
|
||||
@ -45,79 +70,127 @@
|
||||
return
|
||||
}
|
||||
questions=res["questions"]
|
||||
qname=res["name"]
|
||||
$("#question-name").text(qname)
|
||||
quizzName=res["name"]
|
||||
$("#question-name").text(quizzName)
|
||||
$("#edit-question-name").val(quizzName)
|
||||
for(var i=0;i<questions.length;i++){
|
||||
qpositions[questions[i]["id"]] = i
|
||||
internalCreateQuestion(i)
|
||||
}
|
||||
$(`.reorder-button`).hide()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function internalCreateQuestion(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"/>
|
||||
<li id="q-${qid}">
|
||||
<div class="content-box" id="q-${qid}-content"/>
|
||||
<div class="content-box" id="q-${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>
|
||||
<button class="display-button display-button-${qid}" id="q-${qid}-button-edit">Edit</button>
|
||||
<button class="display-button display-button-${qid}" id="q-${qid}-type-edit">Type</button>
|
||||
<button class="display-button display-button-${qid}" id="q-${qid}-remove">Supprimer</button>
|
||||
<button class="edit-button edit-button-${qid}" id="q-${qid}-validate-edit">Valider</button>
|
||||
<button class="edit-button edit-button-${qid}" id="q-${qid}-cancel-edit">Annuler</button>
|
||||
<button class="reorder-button" id="q-${qid}-reorder-uup">Uup</button>
|
||||
<button class="reorder-button" id="q-${qid}-reorder-up">Up</button>
|
||||
<button class="reorder-button" id="q-${qid}-reorder-down">Down</button>
|
||||
<button class="reorder-button" id="q-${qid}-reorder-ddown">DDown</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))
|
||||
$(`#q-${qid}-button-edit`).on('click',e => edit(theqid))
|
||||
$(`#q-${qid}-type-edit`).on('click',e => setType(theqid))
|
||||
$(`#q-${qid}-remove`).on('click',e => removeQuestion(theqid))
|
||||
$(`#q-${qid}-validate-edit`).on('click',e => commitEdit(theqid))
|
||||
$(`#q-${qid}-cancel-edit`).on('click',e => cancelEdit(theqid))
|
||||
$(`#q-${qid}-reorder-uup`).on('click',e => reorderMove(theqid,'uup'))
|
||||
$(`#q-${qid}-reorder-up`).on('click',e => reorderMove(theqid,'up'))
|
||||
$(`#q-${qid}-reorder-down`).on('click',e => reorderMove(theqid,'down'))
|
||||
$(`#q-${qid}-reorder-ddown`).on('click',e => reorderMove(theqid,'ddown'))
|
||||
|
||||
newcontent = QTYPES[qu["type"]]["display"](qid,qu["value"])
|
||||
$(`#question-display-${qid}-content`).replaceWith(newcontent)
|
||||
$(`#q-${qid}-content`).replaceWith(newcontent)
|
||||
newedit = QTYPES[qu["type"]]["edit"](qid,qu["value"])
|
||||
$(`#question-display-${qid}-edit`).replaceWith(newedit)
|
||||
$(`#q-${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()
|
||||
$(`#q-${qid}-content`).show()
|
||||
$(`.display-button-${qid}`).show()
|
||||
$(`#q-${qid}-edit`).hide()
|
||||
$(`.edit-button-${qid}`).hide()
|
||||
$('.reorder-button').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()
|
||||
$(`#q-${qid}-content`).hide()
|
||||
$(`.display-button-${qid}`).hide()
|
||||
$(`#q-${qid}-edit`).show()
|
||||
$(`.edit-button-${qid}`).show()
|
||||
$('.reorder-button').hide()
|
||||
}
|
||||
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)
|
||||
$(`#q-${qid}-edit *`).prop('disabled', true)
|
||||
$(`.edit-button-${qid}`).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)
|
||||
$(`#q-${qid}-edit *`).prop('disabled', false)
|
||||
$(`.edit-button-${qid}`).prop('disabled', false)
|
||||
}
|
||||
function contentNameMode() {
|
||||
$("#question-name").show()
|
||||
$(`#edit-question-name-button`).show()
|
||||
$("#edit-question-name").hide()
|
||||
$(`#edit-question-name-confirm`).hide()
|
||||
$(`#edit-question-name-cancel`).hide()
|
||||
}
|
||||
function editNameMode() {
|
||||
$("#question-name").hide()
|
||||
$(`#edit-question-name-button`).hide()
|
||||
$("#edit-question-name").show()
|
||||
$(`#edit-question-name-confirm`).show()
|
||||
$(`#edit-question-name-cancel`).show()
|
||||
}
|
||||
|
||||
function setType(i,id) {
|
||||
function reorderMode() {
|
||||
$(`.display-button`).hide()
|
||||
$(`.edit-button`).hide()
|
||||
$(`.reorder-button`).show()
|
||||
for (let qid of editing) {
|
||||
lockEdit(qid)
|
||||
}
|
||||
}
|
||||
function unReorderMode() {
|
||||
$(`.reorder-button`).hide()
|
||||
$(`.display-button`).show()
|
||||
for (let qid of editing) {
|
||||
$(`.display-button-${qid}`).hide()
|
||||
$(`.edit-button-${qid}`).show()
|
||||
editMode(qid)
|
||||
unlockEdit(qid)
|
||||
}
|
||||
}
|
||||
|
||||
function setType(qid) {
|
||||
//TODO à faire ^^
|
||||
}
|
||||
|
||||
function edit(id, qid) {
|
||||
|
||||
function edit(qid) {
|
||||
editing.add(qid)
|
||||
editMode(qid)
|
||||
}
|
||||
|
||||
function commitEdit(i,qid) {
|
||||
function commitEdit(qid) {
|
||||
const newvalue = readEditBlockBCC(qid)
|
||||
lockEdit(qid)
|
||||
$.ajax({
|
||||
@ -134,34 +207,226 @@
|
||||
unlockEdit(qid)
|
||||
return
|
||||
}
|
||||
i = qpositions[qid]
|
||||
questions[i]["value"] = newvalue
|
||||
newcontent = QTYPES[questions[i]["type"]]["display"](qid,questions[i]["value"])
|
||||
$(`#question-display-${qid}-content`).replaceWith(newcontent)
|
||||
$(`#q-${qid}-content`).replaceWith(newcontent)
|
||||
|
||||
unlockEdit(qid)
|
||||
editing.delete(qid)
|
||||
contentMode(qid)
|
||||
}
|
||||
})
|
||||
}
|
||||
function cancelEdit(i,qid) {
|
||||
function removeQuestion(qid) {
|
||||
$(`#q-${qid}-remove`).prop('disabled',true)
|
||||
if(!confirm(`Voulez-vous vraiment supprimer la question ${qpositions[qid]+1}`)) {
|
||||
$(`#q-${qid}-remove`).prop('disabled', false)
|
||||
return;
|
||||
}
|
||||
$.ajax({
|
||||
url: "/questions/quizz-edit/"+quizzid+"/remove-question/"+qid,
|
||||
type: "POST",
|
||||
contentType: 'application/json',
|
||||
success: function(res) {
|
||||
console.log(res)
|
||||
if(!res["success"]) {
|
||||
console.log(res["message"])
|
||||
error(res["message"])
|
||||
$(`#q-${qid}-remove`).prop('disabled', false)
|
||||
return
|
||||
}
|
||||
$(`#q-${qid}-remove`).prop('disabled', false)
|
||||
i = qpositions[qid]
|
||||
qpositions[qid] = null
|
||||
questions.splice(i,1)
|
||||
|
||||
$(`#q-${qid}`).remove()
|
||||
}
|
||||
})
|
||||
}
|
||||
function cancelEdit(qid) {
|
||||
i = qpositions[qid]
|
||||
newedit = QTYPES[questions[i]["type"]]["edit"](qid,questions[i]["value"])
|
||||
$(`#question-display-${qid}-edit`).replaceWith(newedit)
|
||||
$(`#q-${qid}-edit`).replaceWith(newedit)
|
||||
editing.delete(qid)
|
||||
contentMode(qid)
|
||||
}
|
||||
|
||||
function commitEditName() {
|
||||
const newname = $("#edit-question-name").val()
|
||||
$("#edit-question-name").prop('disabled', true)
|
||||
$.ajax({
|
||||
url: "/questions/quizz-edit/"+quizzid+"/set-name",
|
||||
type: "POST",
|
||||
data: newname,
|
||||
dataType: 'json',
|
||||
contentType: 'text/plain',
|
||||
success: function(res) {
|
||||
console.log(res)
|
||||
if(!res["success"]) {
|
||||
console.log(res["message"])
|
||||
error(res["message"])
|
||||
$("#edit-question-name").prop('disabled', false)
|
||||
return
|
||||
}
|
||||
quizzName = newname
|
||||
$(`#question-name`).text(quizzName)
|
||||
|
||||
$("#edit-question-name").prop('disabled', false)
|
||||
contentNameMode()
|
||||
}
|
||||
})
|
||||
}
|
||||
function cancelEditName(i,qid) {
|
||||
$("#edit-question-name").val(quizzName)
|
||||
contentNameMode()
|
||||
}
|
||||
|
||||
function addQuestion() {
|
||||
$("#add-question").prop('disabled', true)
|
||||
$.ajax({
|
||||
url: "/questions/quizz-edit/"+quizzid+"/add-question",
|
||||
type: "POST",
|
||||
contentType: 'application/json',
|
||||
success: function(res) {
|
||||
console.log(res)
|
||||
if(!res["success"]) {
|
||||
console.log(res["message"])
|
||||
error(res["message"])
|
||||
$("#add-question").prop('disabled', false)
|
||||
return
|
||||
}
|
||||
q = {id: res["id"], type: res["type"], value: res["value"]}
|
||||
qpositions[q["id"]] = questions.length
|
||||
questions.push(q)
|
||||
internalCreateQuestion(questions.length-1)
|
||||
$("#add-question").prop('disabled', false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Map newindex -> oldindex
|
||||
ordermapping = []
|
||||
|
||||
function startReorder() {
|
||||
ordermapping = new Array(questions.length)
|
||||
for (let i=0;i<questions.length;i++){
|
||||
ordermapping[i] = i
|
||||
}
|
||||
reorderMode()
|
||||
}
|
||||
|
||||
function reorderMove(qid,direction){
|
||||
// Actual position of qid
|
||||
i = ordermapping.indexOf(qpositions[qid])
|
||||
switch(direction) {
|
||||
case "uup":
|
||||
j = 0
|
||||
break
|
||||
case "up":
|
||||
j = Math.max(i-1,0)
|
||||
break
|
||||
case "down":
|
||||
j = Math.min(i+1,ordermapping.length-1)
|
||||
break
|
||||
case "ddown":
|
||||
j = ordermapping.length-1
|
||||
break
|
||||
}
|
||||
moveQuestion(i,j)
|
||||
}
|
||||
// Move question from index i to index j
|
||||
function moveQuestion(i,j) {
|
||||
console.log(`Moving ${i} to ${j}`)
|
||||
if (i == j) return
|
||||
|
||||
// The question we move
|
||||
qid = questions[ordermapping[i]]['id']
|
||||
|
||||
|
||||
oldomi = ordermapping[i]
|
||||
if (i<j){
|
||||
for (let k=i;k<j;k++){
|
||||
ordermapping[k] = ordermapping[k+1]
|
||||
}
|
||||
} else {
|
||||
for (let k=i;k>j;k--){
|
||||
ordermapping[k] = ordermapping[k-1]
|
||||
}
|
||||
}
|
||||
ordermapping[j] = oldomi
|
||||
|
||||
if(j==ordermapping.length-1) {
|
||||
$(`#q-${qid}`).appendTo($("#questions-list"))
|
||||
} else {
|
||||
// We want the one _currently_ at position j+1
|
||||
qid2 = questions[ordermapping[j+1]]['id']
|
||||
console.log(`Moving ${qid} before ${qid2}`)
|
||||
$(`#q-${qid}`).insertBefore($(`#q-${qid2}`))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function commitReorder() {
|
||||
$(".reorder-button").prop('disabled', true)
|
||||
newIdOrders = []
|
||||
for(let p=0;p<ordermapping.length;p++) {
|
||||
newIdOrders[p] = questions[ordermapping[p]]["id"]
|
||||
}
|
||||
$.ajax({
|
||||
url: "/questions/quizz-edit/"+quizzid+"/reorder-questions",
|
||||
type: "POST",
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify(newIdOrders),
|
||||
dataType: 'json',
|
||||
success: function(res) {
|
||||
console.log(res)
|
||||
if(!res["success"]) {
|
||||
console.log(res["message"])
|
||||
error(res["message"])
|
||||
$(".reorder-button").prop('disabled', false)
|
||||
return
|
||||
}
|
||||
// question position -> question data
|
||||
newquestions=new Array(questions.length)
|
||||
newqpositions={}
|
||||
for(let p=0;p<ordermapping.length;p++){
|
||||
newquestions[p] = questions[ordermapping[p]]
|
||||
newqpositions[newquestions[p]['id']] = p
|
||||
}
|
||||
questions = newquestions
|
||||
qpositions = newqpositions
|
||||
$(".reorder-button").prop('disabled', false)
|
||||
unReorderMode()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function cancelReorder() {
|
||||
questionsBlock = $("#questions-list")
|
||||
// We just put all the blocks back in order
|
||||
for (let q of questions) {
|
||||
qid = q["id"]
|
||||
$(`#q-${qid}`).appendTo(questionsBlock)
|
||||
}
|
||||
unReorderMode()
|
||||
}
|
||||
|
||||
|
||||
function makeEditBlockBCC(id,question) {
|
||||
html = `
|
||||
<div class="content-box dcc-box" id="question-display-${id}-edit">
|
||||
<h5><input id="question-display-${id}-edit-question"
|
||||
<div class="content-box dcc-box" id="q-${id}-edit">
|
||||
<h5><input id="q-${id}-edit-question"
|
||||
type="text" value="${_.escape(question["text"])}"/></h5>
|
||||
<ol>
|
||||
<li class="right">☑ <input id="question-display-${id}-edit-good"
|
||||
<li class="right">☑ <input id="q-${id}-edit-good"
|
||||
type="text" value="${_.escape(question["good"])}"/></li>
|
||||
<li class="wrong">☒ <input id="question-display-${id}-edit-wrong1"
|
||||
<li class="wrong">☒ <input id="q-${id}-edit-wrong1"
|
||||
type="text" value="${_.escape(question["wrong"][0])}"/></li>
|
||||
<li class="wrong">☒ <input id="question-display-${id}-edit-wrong2"
|
||||
<li class="wrong">☒ <input id="q-${id}-edit-wrong2"
|
||||
type="text" value="${_.escape(question["wrong"][1])}"/></li>
|
||||
<li class="wrong">☒ <input id="question-display-${id}-edit-wrong3"
|
||||
<li class="wrong">☒ <input id="q-${id}-edit-wrong3"
|
||||
type="text" value="${_.escape(question["wrong"][2])}"/></li>
|
||||
</ol>
|
||||
</div>
|
||||
@ -170,11 +435,11 @@
|
||||
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()
|
||||
questionText = $(`#q-${id}-edit-question`).val()
|
||||
goodText = $(`#q-${id}-edit-good`).val()
|
||||
wrongText1 = $(`#q-${id}-edit-wrong1`).val()
|
||||
wrongText2 = $(`#q-${id}-edit-wrong2`).val()
|
||||
wrongText3 = $(`#q-${id}-edit-wrong3`).val()
|
||||
return {
|
||||
"text": questionText,
|
||||
"good": goodText,
|
||||
@ -183,7 +448,7 @@
|
||||
}
|
||||
function makeAnswerBlockBCC(id,question) {
|
||||
html = `
|
||||
<div class="content-box dcc-box" id="question-display-${id}-content">
|
||||
<div class="content-box dcc-box" id="q-${id}-content">
|
||||
<h5>${_.escape(question["text"])}</h5>
|
||||
<ol>
|
||||
<li class="right">☑ ${_.escape(question["good"])}</li>
|
||||
@ -197,6 +462,14 @@
|
||||
return out
|
||||
}
|
||||
|
||||
contentNameMode()
|
||||
$("#reorder-questions").on("click",startReorder)
|
||||
$("#reorder-questions-commit").on("click",commitReorder)
|
||||
$("#reorder-questions-cancel").on("click",cancelReorder)
|
||||
$("#edit-question-name-button").on("click",editNameMode)
|
||||
$("#edit-question-name-confirm").on("click",commitEditName)
|
||||
$("#edit-question-name-cancel").on("click",cancelEditName)
|
||||
$("#add-question").on("click",addQuestion)
|
||||
getdata()
|
||||
</script>
|
||||
</main>
|
||||
|
||||
@ -7,10 +7,17 @@
|
||||
<body>
|
||||
<div th:replace="~{header}"/>
|
||||
<main>
|
||||
<h3>Quizz à répondre</h3>
|
||||
<ul>
|
||||
<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>
|
||||
<li th:if="${#lists.isEmpty(answerableQuizz)}">Aucun quizz de disponible malheureusement :(</li>
|
||||
<li th:each="q : ${answerableQuizz}"><a th:href="@{/questions/form/{id}(id=${q.id})}">Quizz <span th:text="${q.name}"/></a></li>
|
||||
</ul>
|
||||
<h3>Quizz à éditer</h3>
|
||||
<ul>
|
||||
<li th:if="${#lists.isEmpty(editableQuizz)}">Aucun quizz de disponible malheureusement :(</li>
|
||||
<li th:each="q : ${editableQuizz}"><a th:href="@{/questions/quizz-edit/{id}(id=${q.id})}">Quizz <span th:text="${q.name}"/></a></li>
|
||||
</ul>
|
||||
<a sec:authorize="hasAuthority('CREATE_QUIZZ')" th:href="@{/questions/new-quizz}">Nouveau Quizz</a>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
83
src/main/resources/templates/showform.html
Normal file
83
src/main/resources/templates/showform.html
Normal file
@ -0,0 +1,83 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr" dir="ltr">
|
||||
<head>
|
||||
<div th:replace="~{html-head}"/>
|
||||
<link rel="stylesheet" th:href="@{/css/showform.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>
|
||||
|
||||
<ol id="questions-list">
|
||||
</ol>
|
||||
|
||||
<span id="error-textbox" style="color: red"></span>
|
||||
|
||||
<script th:inline="javascript">
|
||||
/*<![CDATA[*/
|
||||
var formid = /*[[${formId}]]*/ -1;
|
||||
/*]]>*/
|
||||
</script>
|
||||
<script>
|
||||
function error(txt) {
|
||||
console.log(txt)
|
||||
$("#error-textbox").text(txt)
|
||||
}
|
||||
QTYPES = {
|
||||
"DCC": makeAnswerBlockBCC,
|
||||
}
|
||||
|
||||
// question position -> question data
|
||||
questiondata=[]
|
||||
|
||||
function getdata() {
|
||||
$.ajax({
|
||||
url: "/questions/getformdata/"+formid,
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
success: function(res) {
|
||||
console.log(res)
|
||||
if(!res["success"]) {
|
||||
console.log(res["message"])
|
||||
error(res["message"])
|
||||
return
|
||||
}
|
||||
questiondata=res["data"]
|
||||
|
||||
for(let qd of questiondata) {
|
||||
content = QTYPES[qd["type"]](qd["qid"],qd["qvalue"],qd["avalue"])
|
||||
$(`#questions-list`).append(content)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function makeAnswerBlockBCC(id,qvalue,avalue) {
|
||||
switch(avalue["type"]){
|
||||
case 4:
|
||||
dcc = "carré"
|
||||
break
|
||||
case 2:
|
||||
dcc = "duo"
|
||||
break
|
||||
default:
|
||||
dcc = "cash"
|
||||
}
|
||||
html = `
|
||||
<li class="dcc-box" id="q-${id}">
|
||||
<h5>${_.escape(qvalue["text"])}</h5>
|
||||
Réponse ${dcc}: ${_.escape(avalue["answer"])}
|
||||
</li>
|
||||
`
|
||||
out = $('<li/>').html(html).contents()
|
||||
return out
|
||||
}
|
||||
|
||||
getdata()
|
||||
</script>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
81
src/main/resources/templates/showformadvancements.html
Normal file
81
src/main/resources/templates/showformadvancements.html
Normal file
@ -0,0 +1,81 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr" dir="ltr">
|
||||
<head>
|
||||
<div th:replace="~{html-head}"/>
|
||||
<link rel="stylesheet" th:href="@{/css/showformadvancements.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>
|
||||
|
||||
<button id="refresh-data">Refresh</button>
|
||||
<table id="forms-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>User</th>
|
||||
<th>Position</th>
|
||||
<th>Step</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="forms-body">
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<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)
|
||||
}
|
||||
|
||||
formsdata=[]
|
||||
|
||||
function getdata() {
|
||||
$('#refresh-data').prop('disabled',true)
|
||||
$.ajax({
|
||||
url: "/questions/getformadvancements/"+quizzid,
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
success: function(res) {
|
||||
console.log(res)
|
||||
if(!res["success"]) {
|
||||
console.log(res["message"])
|
||||
error(res["message"])
|
||||
$('#refresh-data').prop('disabled',false)
|
||||
return
|
||||
}
|
||||
formsdata=res["data"]
|
||||
|
||||
$('.form-line').remove()
|
||||
for(let qf of formsdata) {
|
||||
html = `
|
||||
<tr class="form-line done-${qf["done"]}" id="q-${qf["qfid"]}">
|
||||
<td>${qf["username"]}</td>
|
||||
<td>${qf["position"]}</td>
|
||||
<td>${qf["step"]}</td>
|
||||
</tr>
|
||||
`
|
||||
content = $('<li/>').html(html).contents()
|
||||
$(`#forms-body`).append(content)
|
||||
}
|
||||
$('#refresh-data').prop('disabled',false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
$('#refresh-data').on('click',getdata)
|
||||
getdata()
|
||||
</script>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
x
Reference in New Issue
Block a user