diff --git a/flake.nix b/flake.nix index 6c0011c..44ea043 100644 --- a/flake.nix +++ b/flake.nix @@ -35,6 +35,7 @@ }; misael-launcher = pkgs.writeShellScriptBin "misael" '' + set -e echo "Checking Database Migration" ${pkgs.flyway}/bin/flyway "-url=$MISAEL_DATABASE" -user=misael "-password=$MISAEL_PASSWORD" -locations="filesystem:${misael}/resources/main/db/migration/" -schemas=misael migrate diff --git a/src/main/java/com/bernard/misael/auth/SpringSecurity.java b/src/main/java/com/bernard/misael/auth/SpringSecurity.java index 76b8848..874fdd2 100644 --- a/src/main/java/com/bernard/misael/auth/SpringSecurity.java +++ b/src/main/java/com/bernard/misael/auth/SpringSecurity.java @@ -6,7 +6,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; -import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; @@ -19,7 +18,6 @@ import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService; import org.springframework.security.oauth2.client.userinfo.OAuth2UserService; import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser; import org.springframework.security.oauth2.core.oidc.user.OidcUser; -import org.springframework.security.oauth2.core.user.DefaultOAuth2User; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; diff --git a/src/main/java/com/bernard/misael/auth/service/UserServiceImpl.java b/src/main/java/com/bernard/misael/auth/service/UserServiceImpl.java index 3eb977f..3bbf5db 100644 --- a/src/main/java/com/bernard/misael/auth/service/UserServiceImpl.java +++ b/src/main/java/com/bernard/misael/auth/service/UserServiceImpl.java @@ -41,6 +41,8 @@ public class UserServiceImpl implements UserService { String id = oidcUser.getName(); Set privileges; try { + if(oidcUser.getAttribute("resource_access") != null) + throw new RuntimeException("Oidc user has no 'resource_access'"); privileges = oidcUser .>>>getAttribute("resource_access") .getOrDefault("misael",Map.of()) diff --git a/src/main/java/com/bernard/misael/quizz/QuestionsController.java b/src/main/java/com/bernard/misael/quizz/QuestionsController.java index 12a0d26..149b585 100644 --- a/src/main/java/com/bernard/misael/quizz/QuestionsController.java +++ b/src/main/java/com/bernard/misael/quizz/QuestionsController.java @@ -57,6 +57,7 @@ public class QuestionsController { if(u!=null) { model.addAttribute("answerableQuizz",qm.answerableQuizz(u)); model.addAttribute("editableQuizz",qm.editableQuizz(u)); + model.addAttribute("completedQuizz",qm.completedQuizz(u)); } return "quizz.html"; } @@ -93,20 +94,20 @@ public class QuestionsController { } /* - * Show one (completed) form of one user + * Watch advancements of all forms for a specific quizz id */ - @GetMapping("/showformsadvancements/{id}") + @GetMapping("/watch/{id}") public Object showFormsAdvancements(@PathVariable("id") long id, Model m, Principal p) { User u = us.ofPrincipal(p); if(u==null) return "redirect:/login?restricted"; Optional oq = qm.canViewQuizzFormsOfQuizz(u, id); if (oq.isEmpty()) - //TODO Faire un mesasge d'erreur dépendant des circonstances (unatuhorized, not found, not complete ...) + //TODO Faire un message 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"; + return "watchquizz.html"; } /* @@ -134,10 +135,10 @@ public class QuestionsController { } /* - * API get the form + * API get the form advancement for every form of a quizz */ - @PostMapping("/getformadvancements/{id}") - public Object showFormAdvancements(@PathVariable("id") long id, Principal p) { + @PostMapping("/watchdata/{id}") + public Object watchData(@PathVariable("id") long id, Principal p) { User u = us.ofPrincipal(p); if(u==null) return "redirect:/login?restricted"; @@ -159,6 +160,33 @@ public class QuestionsController { return new ResponseEntity<>(HttpStatus.OK); } + /* + * API get the form + */ + @GetMapping("/mark-complete/{id}") + public Object markComplete(@PathVariable("id") long id, Principal p) { + User u = us.ofPrincipal(p); + if(u==null) + return "redirect:/login?restricted"; + if(!qm.markComplete(u, id)) + return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); + return new ResponseEntity<>(HttpStatus.OK); + } + /* + * API set the last public question + */ + @PostMapping("/set-public-question-count/{id}") + public Object setPublicQuestionCount(@PathVariable("id") long id, @RequestBody JsonNode data, Principal p) { + User u = us.ofPrincipal(p); + if(u==null) + return "redirect:/login?restricted"; + JsonNode pqc = data.get("publicQuestionCount"); + Integer pqcI = pqc.isNull() ? null : pqc.asInt(); + if(!qm.setPublicQuestionCount(u, id, pqcI)) + return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); + return new ResponseEntity<>(HttpStatus.OK); + } + @GetMapping("/form/{q}") public String formpage(@PathVariable("q") long quizzId, Principal p, Model m) { User u = us.ofPrincipal(p); diff --git a/src/main/java/com/bernard/misael/quizz/service/QuizzManager.java b/src/main/java/com/bernard/misael/quizz/service/QuizzManager.java index e42dab8..1a267e9 100644 --- a/src/main/java/com/bernard/misael/quizz/service/QuizzManager.java +++ b/src/main/java/com/bernard/misael/quizz/service/QuizzManager.java @@ -18,6 +18,7 @@ public interface QuizzManager { public boolean canAccessQuizz(User user, long quizzId); public List editableQuizz(User user); + public List completedQuizz(User user); public List answerableQuizz(User user); public boolean canEditQuizz(User user, long quizzId); @@ -37,4 +38,6 @@ public interface QuizzManager { public JsonNode getQuizzFormAdvancments(User user, long quizzId); public Quizz duplicateQuizz(User user, long quizzId); + public boolean markComplete(User user, long quizzId); + public boolean setPublicQuestionCount(User u, long quizzId, Integer publicQuestionCount); } \ No newline at end of file diff --git a/src/main/java/com/bernard/misael/quizz/service/QuizzManagerImpl.java b/src/main/java/com/bernard/misael/quizz/service/QuizzManagerImpl.java index ae14f40..9cbe9b7 100644 --- a/src/main/java/com/bernard/misael/quizz/service/QuizzManagerImpl.java +++ b/src/main/java/com/bernard/misael/quizz/service/QuizzManagerImpl.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Optional; +import java.util.Random; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -11,6 +12,7 @@ import java.util.stream.Stream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.lang.NonNull; import org.springframework.stereotype.Service; import com.bernard.misael.auth.repository.UserRepository; @@ -190,10 +192,11 @@ public class QuizzManagerImpl implements QuizzManager { return qf; } + private static final Random rand = new Random(); @Override public Quizz newQuizz(User user) { Quizz q = new Quizz(); - q.setName("Super questions de "+user.getName()+" ("+Integer.toHexString((int)(Math.random()*0xFFFFFFF))+")"); + q.setName("Super questions de "+user.getName()+" ("+Integer.toHexString(rand.nextInt(0xFFFFFFF))+")"); q.setOwner(user); q = qRepository.save(q); return q; @@ -227,7 +230,7 @@ public class QuizzManagerImpl implements QuizzManager { .sorted((q1,q2) -> q1.getName().compareTo(q2.getName())), publicQuizz.stream() .sorted((q1,q2) -> q1.getName().compareTo(q2.getName())) - ).collect(Collectors.toList()); + ).toList(); } @Override @@ -235,18 +238,25 @@ public class QuizzManagerImpl implements QuizzManager { Set ownQuizz = qRepository.findByOwnerAndIsCompleteFalse(user); return ownQuizz.stream() .sorted((q1,q2) -> q1.getName().compareTo(q2.getName())) - .collect(Collectors.toList()); + .toList(); } - Logger logger = LoggerFactory.getLogger(QuestionsController.class); + @Override + public List completedQuizz(User user) { + Set ownQuizz = qRepository.findByOwnerAndIsCompleteTrue(user); + return ownQuizz.stream() + .sorted((q1,q2) -> q1.getName().compareTo(q2.getName())) + .toList(); + } + + Logger logger = LoggerFactory.getLogger(QuizzManagerImpl.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); + logger.info("Could not find quizz of id {}",quizzId); return false; } } @@ -294,7 +304,7 @@ public class QuizzManagerImpl implements QuizzManager { Optional authCheck = checkEditQuizz(user, quizzId); if(authCheck.isPresent()) return authCheck.get(); - if(newName.isBlank() | newName.length()>255) + if(newName.isBlank() || newName.length()>255) return errorNode("Le nom est invalide"); Quizz quizz = qRepository.findById(quizzId).get(); @@ -565,6 +575,10 @@ public class QuizzManagerImpl implements QuizzManager { } ObjectNode out = JsonNodeFactory.instance.objectNode(); out.set("success", JsonNodeFactory.instance.booleanNode(true)); + out.set("publicQuestionCount", quizz.getPublicQuestionCount()==null? + JsonNodeFactory.instance.nullNode(): + JsonNodeFactory.instance.numberNode(quizz.getPublicQuestionCount())); + out.set("questionCount", JsonNodeFactory.instance.numberNode(quizz.getQuestionCount())); out.set("data", formsNode); return out; } @@ -599,6 +613,40 @@ public class QuizzManagerImpl implements QuizzManager { return nq; } + @Override + public boolean markComplete(User u, long quizzId) { + if (!canEditQuizz(u, quizzId)) + return false; + Optional oq = qRepository.findById(quizzId); + if(oq.isEmpty()) + return false; + Quizz q = oq.get(); + + q.setPublicQuestionCount(null); + q.setComplete(true); + qRepository.save(q); + + return true; + } + + @Override + public boolean setPublicQuestionCount(User u, long quizzId, Integer publicQuestionCount) { + if (!canEditQuizz(u, quizzId)) + return false; + Optional oq = qRepository.findById(quizzId); + if(oq.isEmpty()) + return false; + Quizz q = oq.get(); + if(!q.isComplete()) + return false; + if(publicQuestionCount != null && (publicQuestionCount > q.getQuestionCount() || publicQuestionCount < 0)) + return false; + q.setPublicQuestionCount(publicQuestionCount); + qRepository.save(q); + + return true; + } + } diff --git a/src/main/resources/templates/quizz-edit.html b/src/main/resources/templates/quizz-edit.html index 2f761d0..d77c53d 100644 --- a/src/main/resources/templates/quizz-edit.html +++ b/src/main/resources/templates/quizz-edit.html @@ -27,6 +27,10 @@
+ +
+ +
@@ -412,6 +416,18 @@ } unReorderMode() } + + function markComplete() { + if (confirm("Voulez-vous vraiment marquer ce quizz comme terminé ? Vous ne pourrez plus l'éditer !")) { + $.ajax({ + url: "/questions/mark-complete/"+quizzid, + type: "GET", + success: function(res) { + console.log("SUPER") + } + }) + } + } function makeEditBlockBCC(id,question) { @@ -470,6 +486,7 @@ $("#edit-question-name-confirm").on("click",commitEditName) $("#edit-question-name-cancel").on("click",cancelEditName) $("#add-question").on("click",addQuestion) + $("#mark-complete").on("click",markComplete) getdata() diff --git a/src/main/resources/templates/quizz.html b/src/main/resources/templates/quizz.html index 20ac1fe..182cb93 100644 --- a/src/main/resources/templates/quizz.html +++ b/src/main/resources/templates/quizz.html @@ -7,17 +7,23 @@
-

Quizz à répondre

+

Quizz disponibles

  • Aucun quizz de disponible malheureusement :(
  • Quizz
-

Quizz à éditer

+

Quizz en édition

  • Aucun quizz de disponible malheureusement :(
  • Quizz
Nouveau Quizz + +

Quizz terminés

+
    +
  • Aucun quizz de disponible malheureusement :(
  • +
  • Quizz
  • +
\ No newline at end of file diff --git a/src/main/resources/templates/showformadvancements.html b/src/main/resources/templates/showformadvancements.html deleted file mode 100644 index 1eed223..0000000 --- a/src/main/resources/templates/showformadvancements.html +++ /dev/null @@ -1,81 +0,0 @@ - - - -
- - - - -
- - -
- - - - - - - - - - - - - -
UserPositionStep
- - - - - -
- - \ No newline at end of file diff --git a/src/main/resources/templates/watchquizz.html b/src/main/resources/templates/watchquizz.html new file mode 100644 index 0000000..65c3ca4 --- /dev/null +++ b/src/main/resources/templates/watchquizz.html @@ -0,0 +1,172 @@ + + + +
+ + + + +
+ + +
+ +
+ + + Nombre de questions accessibles: + + + x + + +
+ + + + + + + + + + + + +
UserNombre de questions réponduesÉtape
+ + + + + +
+ + \ No newline at end of file