Compare commits
No commits in common. "main" and "master" have entirely different histories.
3
.gitattributes
vendored
3
.gitattributes
vendored
@ -1,3 +0,0 @@
|
||||
/gradlew text eol=lf
|
||||
*.bat text eol=crlf
|
||||
*.jar binary
|
||||
38
.gitignore
vendored
38
.gitignore
vendored
@ -1,37 +1 @@
|
||||
HELP.md
|
||||
.gradle
|
||||
build/
|
||||
!gradle/wrapper/gradle-wrapper.jar
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
bin/
|
||||
!**/src/main/**/bin/
|
||||
!**/src/test/**/bin/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
out/
|
||||
!**/src/main/**/out/
|
||||
!**/src/test/**/out/
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
__pycache__/
|
||||
|
||||
23
Dockerfile
23
Dockerfile
@ -1,23 +0,0 @@
|
||||
FROM gradle:jdk21 AS builder
|
||||
|
||||
WORKDIR /build/
|
||||
ADD src /build/src/
|
||||
ADD build.gradle /build/
|
||||
ADD settings.gradle /build/
|
||||
ADD versions.properties /build/
|
||||
|
||||
|
||||
RUN gradle assemble
|
||||
|
||||
FROM eclipse-temurin:21-noble
|
||||
WORKDIR /opt/
|
||||
ADD "https://download.red-gate.com/maven/release/com/redgate/flyway/flyway-commandline/11.9.1/flyway-commandline-11.9.1-linux-x64.tar.gz" /opt/flyway.tar.gz
|
||||
RUN tar -xvzf /opt/flyway.tar.gz -C /opt/
|
||||
RUN ln -s /opt/flyway-11.9.1/flyway /usr/local/bin
|
||||
|
||||
WORKDIR /app/
|
||||
COPY --from=builder /build/build/libs/misael-beta.jar /app/misael.jar
|
||||
COPY --from=builder /build/build/resources/ /app/resources/
|
||||
ADD launcher.sh /app/
|
||||
|
||||
ENTRYPOINT ["bash","./launcher.sh"]
|
||||
@ -1,44 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<diagram version="1" name="ERD-Misael.erd">
|
||||
<entities>
|
||||
<data-source id="postgres-jdbc-195261858a3-67acab6d92533937">
|
||||
<entity id="1" name="answers" fq-name="misael.answers" order="0" font="Segoe UI:9:0" x="40" y="50">
|
||||
<path name="misael"/>
|
||||
<path name="misael"/>
|
||||
</entity>
|
||||
<entity id="2" name="questions" fq-name="misael.questions" order="1" font="Segoe UI:9:0" x="279" y="40">
|
||||
<path name="misael"/>
|
||||
<path name="misael"/>
|
||||
</entity>
|
||||
<entity id="3" name="quizz" fq-name="misael.quizz" order="2" font="Segoe UI:9:0" x="510" y="127">
|
||||
<path name="misael"/>
|
||||
<path name="misael"/>
|
||||
</entity>
|
||||
<entity id="4" name="quizzf" fq-name="misael.quizzf" order="3" font="Segoe UI:9:0" x="279" y="224">
|
||||
<path name="misael"/>
|
||||
<path name="misael"/>
|
||||
</entity>
|
||||
<entity id="5" name="roles" fq-name="misael.roles" order="4" font="Segoe UI:9:0" x="709" y="418">
|
||||
<path name="misael"/>
|
||||
<path name="misael"/>
|
||||
</entity>
|
||||
<entity id="6" name="users" fq-name="misael.users" order="5" font="Segoe UI:9:0" x="709" y="254">
|
||||
<path name="misael"/>
|
||||
<path name="misael"/>
|
||||
</entity>
|
||||
<entity id="7" name="users_roles" fq-name="misael.users_roles" order="6" font="Segoe UI:9:0" x="510" y="352">
|
||||
<path name="misael"/>
|
||||
<path name="misael"/>
|
||||
</entity>
|
||||
</data-source>
|
||||
</entities>
|
||||
<relations>
|
||||
<relation name="fk54dobrdq2u51m4u8s7kg0as8v" fq-name="misael.answers.fk54dobrdq2u51m4u8s7kg0as8v" type="fk" pk-ref="2" fk-ref="1"/>
|
||||
<relation name="fkq12h25ynjok1m497gwos511te" fq-name="misael.questions.fkq12h25ynjok1m497gwos511te" type="fk" pk-ref="3" fk-ref="2"/>
|
||||
<relation name="fk4ukbg2yaa93gs5nx1s5d9rqu4" fq-name="misael.quizzf.fk4ukbg2yaa93gs5nx1s5d9rqu4" type="fk" pk-ref="3" fk-ref="4"/>
|
||||
<relation name="fkj6m8fwv7oqv74fcehir1a9ffy" fq-name="misael.users_roles.fkj6m8fwv7oqv74fcehir1a9ffy" type="fk" pk-ref="5" fk-ref="7"/>
|
||||
<relation name="fkfeoogns8m4m4hvno1ttqb30wm" fq-name="misael.quizz.fkfeoogns8m4m4hvno1ttqb30wm" type="fk" pk-ref="6" fk-ref="3"/>
|
||||
<relation name="fkuj3h8r3i4lnxukdqobapwbuq" fq-name="misael.quizzf.fkuj3h8r3i4lnxukdqobapwbuq" type="fk" pk-ref="6" fk-ref="4"/>
|
||||
<relation name="fk2o0jvgh89lemvvo17cbqvdxaa" fq-name="misael.users_roles.fk2o0jvgh89lemvvo17cbqvdxaa" type="fk" pk-ref="6" fk-ref="7"/>
|
||||
</relations>
|
||||
</diagram>
|
||||
13
README.md
13
README.md
@ -1,13 +0,0 @@
|
||||
# Misael
|
||||
|
||||
## For dev
|
||||
You need to setup a postgres server at localhost:5432, with user misael with password misael-dev
|
||||
|
||||
Then, you run the database migration `./gradlew flywayMigrate`
|
||||
|
||||
And finaly we run `./gradlew bootRun`
|
||||
|
||||
## For deployment
|
||||
The compose file is enough.
|
||||
|
||||
`launcher.sh` checks that the migrations are applied and then runs the spring boot server on port 8080
|
||||
71
build.gradle
71
build.gradle
@ -1,71 +0,0 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'org.flywaydb:flyway-database-postgresql:_'
|
||||
classpath 'org.postgresql:postgresql:_'
|
||||
}
|
||||
}
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'org.springframework.boot'
|
||||
id 'org.flywaydb.flyway'
|
||||
id 'io.spring.dependency-management'
|
||||
}
|
||||
|
||||
|
||||
group = 'com.bernard'
|
||||
version = 'beta'
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(21)
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation Spring.boot.data.jpa
|
||||
implementation Spring.boot.jdbc
|
||||
implementation Spring.boot.thymeleaf
|
||||
implementation Spring.boot.security
|
||||
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6:_'
|
||||
implementation Spring.boot.web
|
||||
implementation 'org.springframework:spring-jdbc:_'
|
||||
implementation 'org.flywaydb:flyway-core:_'
|
||||
implementation 'org.flywaydb:flyway-database-postgresql:_'
|
||||
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
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher:_'
|
||||
|
||||
implementation 'com.fasterxml.jackson.core:jackson-databind:_'
|
||||
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-hibernate5:_'
|
||||
|
||||
//Lombok
|
||||
compileOnly 'org.projectlombok:lombok:_'
|
||||
annotationProcessor 'org.projectlombok:lombok:_'
|
||||
|
||||
testCompileOnly 'org.projectlombok:lombok:_'
|
||||
testAnnotationProcessor 'org.projectlombok:lombok:_'
|
||||
}
|
||||
|
||||
flyway {
|
||||
url = "jdbc:postgresql://127.0.0.1:5432/misael"
|
||||
user = 'misael'
|
||||
password = 'misael-dev'
|
||||
driver = 'org.postgresql.Driver'
|
||||
schemas = ['misael']
|
||||
}
|
||||
|
||||
tasks.named('test') {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
24
compose.yaml
24
compose.yaml
@ -1,24 +0,0 @@
|
||||
services:
|
||||
postgres:
|
||||
image: 'postgres:latest'
|
||||
environment:
|
||||
- 'POSTGRES_DB=misael'
|
||||
- 'POSTGRES_PASSWORD=misael-dev'
|
||||
- 'POSTGRES_USER=misael'
|
||||
networks:
|
||||
- misanetwork
|
||||
|
||||
misael:
|
||||
image: 'mysaa/misael:latest'
|
||||
build: .
|
||||
environment:
|
||||
- 'MISAEL_DATABASE=jdbc:postgresql://postgres:5432/misael'
|
||||
- 'MISAEL_PASSWORD=misael-dev'
|
||||
ports:
|
||||
- 8080:8080
|
||||
networks:
|
||||
- misanetwork
|
||||
|
||||
networks:
|
||||
misanetwork:
|
||||
driver: bridge
|
||||
147
flake.lock
generated
147
flake.lock
generated
@ -1,147 +0,0 @@
|
||||
{
|
||||
"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
84
flake.nix
@ -1,84 +0,0 @@
|
||||
{
|
||||
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.writeShellScriptBin "misael" ''
|
||||
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
|
||||
|
||||
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
2746
gradle.lock
File diff suppressed because it is too large
Load Diff
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
7
gradle/wrapper/gradle-wrapper.properties
vendored
7
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,7 +0,0 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
251
gradlew
vendored
251
gradlew
vendored
@ -1,251 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
94
gradlew.bat
vendored
94
gradlew.bat
vendored
@ -1,94 +0,0 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@ -1,6 +0,0 @@
|
||||
|
||||
echo "Checking Database Migration"
|
||||
flyway "-url=$MISAEL_DATABASE" -user=misael "-password=$MISAEL_PASSWORD" -locations="filesystem:resources/main/db/migration/" -schemas=misael migrate
|
||||
|
||||
echo "Running" java -jar misael.jar --spring.datasource.url=$MISAEL_DATABASE --spring.datasource.password=$MISAEL_PASSWORD
|
||||
java -jar misael.jar --spring.datasource.url=$MISAEL_DATABASE --spring.datasource.password=$MISAEL_PASSWORD
|
||||
0
lists/__init__.py
Normal file
0
lists/__init__.py
Normal file
11
lists/admin.py
Normal file
11
lists/admin.py
Normal file
@ -0,0 +1,11 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import *
|
||||
|
||||
admin.site.register(Oeuvre)
|
||||
admin.site.register(JeuVideo)
|
||||
admin.site.register(Film)
|
||||
admin.site.register(Serie)
|
||||
admin.site.register(Livre)
|
||||
admin.site.register(Avis)
|
||||
admin.site.register(Todo)
|
||||
6
lists/apps.py
Normal file
6
lists/apps.py
Normal file
@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ListsConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'lists'
|
||||
22
lists/migrations/0001_initial.py
Normal file
22
lists/migrations/0001_initial.py
Normal file
@ -0,0 +1,22 @@
|
||||
# Generated by Django 5.0.1 on 2024-01-16 01:18
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='JeuVideo',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=200)),
|
||||
('sortie', models.DateTimeField(verbose_name='Date de publication')),
|
||||
],
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,75 @@
|
||||
# Generated by Django 5.0.1 on 2024-01-16 13:25
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('lists', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Oeuvre',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=200)),
|
||||
],
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='jeuvideo',
|
||||
name='id',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='jeuvideo',
|
||||
name='name',
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Film',
|
||||
fields=[
|
||||
('oeuvre_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='lists.oeuvre')),
|
||||
('sortie', models.DateTimeField(verbose_name='Date de publication')),
|
||||
],
|
||||
bases=('lists.oeuvre',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Livre',
|
||||
fields=[
|
||||
('oeuvre_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='lists.oeuvre')),
|
||||
('sortie', models.DateTimeField(verbose_name='Date de publication')),
|
||||
],
|
||||
bases=('lists.oeuvre',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Serie',
|
||||
fields=[
|
||||
('oeuvre_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='lists.oeuvre')),
|
||||
('sortie', models.DateTimeField(verbose_name='Date de publication')),
|
||||
],
|
||||
bases=('lists.oeuvre',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Avis',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('texte', models.CharField()),
|
||||
('oeuvre', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='lists.oeuvre')),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='jeuvideo',
|
||||
name='oeuvre_ptr',
|
||||
field=models.OneToOneField(auto_created=True, default=0, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='lists.oeuvre'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Todo',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('rank', models.IntegerField()),
|
||||
('oeuvre', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='lists.oeuvre')),
|
||||
],
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,45 @@
|
||||
# Generated by Django 5.0.1 on 2024-01-16 15:27
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('lists', '0002_oeuvre_remove_jeuvideo_id_remove_jeuvideo_name_film_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='film',
|
||||
name='sortie',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='jeuvideo',
|
||||
name='sortie',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='livre',
|
||||
name='sortie',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='serie',
|
||||
name='sortie',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='oeuvre',
|
||||
name='sortie',
|
||||
field=models.IntegerField(default=0, verbose_name='Année de sortie'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='avis',
|
||||
name='texte',
|
||||
field=models.TextField(),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='oeuvre',
|
||||
name='name',
|
||||
field=models.CharField(max_length=200, verbose_name='Nom'),
|
||||
),
|
||||
]
|
||||
0
lists/migrations/__init__.py
Normal file
0
lists/migrations/__init__.py
Normal file
29
lists/models.py
Normal file
29
lists/models.py
Normal file
@ -0,0 +1,29 @@
|
||||
from django.db import models
|
||||
|
||||
class Oeuvre(models.Model):
|
||||
name = models.CharField("Nom",max_length=200)
|
||||
sortie = models.IntegerField("Année de sortie")
|
||||
|
||||
def __str__(self):
|
||||
return self.name + (" (" + str(self.sortie) + ")" if self.sortie else "")
|
||||
|
||||
class JeuVideo(Oeuvre):
|
||||
pass
|
||||
|
||||
class Film(Oeuvre):
|
||||
pass
|
||||
|
||||
class Serie(Oeuvre):
|
||||
pass
|
||||
|
||||
class Livre(Oeuvre):
|
||||
pass
|
||||
|
||||
class Avis(models.Model):
|
||||
oeuvre = models.ForeignKey(Oeuvre, on_delete=models.CASCADE)
|
||||
texte = models.TextField()
|
||||
|
||||
class Todo(models.Model):
|
||||
oeuvre = models.ForeignKey(Oeuvre, on_delete=models.CASCADE)
|
||||
rank = models.IntegerField()
|
||||
|
||||
3
lists/tests.py
Normal file
3
lists/tests.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
8
lists/urls.py
Normal file
8
lists/urls.py
Normal file
@ -0,0 +1,8 @@
|
||||
from django.urls import path
|
||||
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path("", views.index, name="index"),
|
||||
]
|
||||
|
||||
6
lists/views.py
Normal file
6
lists/views.py
Normal file
@ -0,0 +1,6 @@
|
||||
from django.shortcuts import render
|
||||
from django.http import HttpResponse
|
||||
|
||||
|
||||
def index(request):
|
||||
return HttpResponse("Hello, world. You're at the polls index.")
|
||||
22
manage.py
Executable file
22
manage.py
Executable file
@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env python
|
||||
"""Django's command-line utility for administrative tasks."""
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
"""Run administrative tasks."""
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'misael.settings')
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"Couldn't import Django. Are you sure it's installed and "
|
||||
"available on your PYTHONPATH environment variable? Did you "
|
||||
"forget to activate a virtual environment?"
|
||||
) from exc
|
||||
execute_from_command_line(sys.argv)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
0
misael/__init__.py
Normal file
0
misael/__init__.py
Normal file
16
misael/asgi.py
Normal file
16
misael/asgi.py
Normal file
@ -0,0 +1,16 @@
|
||||
"""
|
||||
ASGI config for misael project.
|
||||
|
||||
It exposes the ASGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/5.0/howto/deployment/asgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.asgi import get_asgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'misael.settings')
|
||||
|
||||
application = get_asgi_application()
|
||||
128
misael/settings.py
Normal file
128
misael/settings.py
Normal file
@ -0,0 +1,128 @@
|
||||
"""
|
||||
Django settings for misael project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 5.0.1.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/5.0/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/5.0/ref/settings/
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'django-insecure-xww0xj_dh_2xtex&q%y6zlsa_$anz&4ijs(94skyhu_ijt43m@'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'lists.apps.ListsConfig',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'misael.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'misael.wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/5.0/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.postgresql_psycopg2',
|
||||
'NAME': 'misael',
|
||||
'USER': 'misael',
|
||||
'PASSWORD': 'UCmRhUBXS6HX9NeUs15oKxLZAZvixE7N+ONLHWRk1ag=',
|
||||
'HOST': 'localhost',
|
||||
'PORT': '5432',
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/5.0/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'Europe/Paris'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/5.0/howto/static-files/
|
||||
|
||||
STATIC_URL = 'static/'
|
||||
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
23
misael/urls.py
Normal file
23
misael/urls.py
Normal file
@ -0,0 +1,23 @@
|
||||
"""
|
||||
URL configuration for misael project.
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/5.0/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the include() function: from django.urls import include, path
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.urls import include,path
|
||||
|
||||
urlpatterns = [
|
||||
path("lists/", include("lists.urls")),
|
||||
path('admin/', admin.site.urls),
|
||||
]
|
||||
16
misael/wsgi.py
Normal file
16
misael/wsgi.py
Normal file
@ -0,0 +1,16 @@
|
||||
"""
|
||||
WSGI config for misael project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/5.0/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'misael.settings')
|
||||
|
||||
application = get_wsgi_application()
|
||||
@ -1,4 +0,0 @@
|
||||
plugins {
|
||||
id 'de.fayard.refreshVersions' version '0.60.5'
|
||||
}
|
||||
rootProject.name = 'misael'
|
||||
@ -1,46 +0,0 @@
|
||||
package com.bernard.misael;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.bernard.misael.model.Privilege;
|
||||
import com.bernard.misael.model.Role;
|
||||
import com.bernard.misael.model.User;
|
||||
import com.bernard.misael.repository.UserRepository;
|
||||
|
||||
@Service
|
||||
public class CustomUserDetailsService implements UserDetailsService {
|
||||
|
||||
private UserRepository userRepository;
|
||||
|
||||
public CustomUserDetailsService(UserRepository userRepository) {
|
||||
this.userRepository = userRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String pseudo) throws UsernameNotFoundException {
|
||||
User user = userRepository.findByName(pseudo);
|
||||
|
||||
if (user != null) {
|
||||
Stream<Privilege> inducedPrivileges = user.getRoles().stream()
|
||||
.map(Role::getPrivileges)
|
||||
.map(Set<Privilege>::stream)
|
||||
.flatMap(Function.identity())
|
||||
.sorted()
|
||||
.distinct();
|
||||
Stream<Role> roles = user.getRoles().stream();
|
||||
return new org.springframework.security.core.userdetails.User(user.getName(),
|
||||
user.getPassword(),
|
||||
Stream.concat(inducedPrivileges, roles).toList()
|
||||
);
|
||||
}else{
|
||||
throw new UsernameNotFoundException("Invalid username or password.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
package com.bernard.misael;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class MisaelApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(MisaelApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,60 +0,0 @@
|
||||
package com.bernard.misael;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
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;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableMethodSecurity(securedEnabled = true)
|
||||
public class SpringSecurity {
|
||||
|
||||
public static final Logger LOG = Logger.getLogger(SpringSecurity.class.getName());
|
||||
|
||||
@Autowired
|
||||
private UserDetailsService userDetailsService;
|
||||
|
||||
@Bean
|
||||
public static PasswordEncoder passwordEncoder(){
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http.csrf(csrf -> csrf.disable())
|
||||
.authorizeHttpRequests((authorize) ->
|
||||
authorize
|
||||
.requestMatchers("/**").permitAll()
|
||||
).formLogin(
|
||||
form -> form
|
||||
.loginPage("/login")
|
||||
.loginProcessingUrl("/login")
|
||||
.defaultSuccessUrl("/")
|
||||
.permitAll()
|
||||
).logout(
|
||||
logout -> logout
|
||||
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
|
||||
.permitAll()
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.userDetailsService(userDetailsService)
|
||||
.passwordEncoder(passwordEncoder());
|
||||
}
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
package com.bernard.misael;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.thymeleaf.extras.springsecurity6.dialect.SpringSecurityDialect;
|
||||
|
||||
@Configuration
|
||||
public class ThymeleafConfig {
|
||||
|
||||
@Bean
|
||||
public SpringSecurityDialect springSecurityDialect(){
|
||||
return new SpringSecurityDialect();
|
||||
}
|
||||
}
|
||||
@ -1,52 +0,0 @@
|
||||
package com.bernard.misael.model;
|
||||
|
||||
import com.bernard.misael.service.JsonNodeConverter;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Convert;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Entity
|
||||
@Table(name="answers")
|
||||
public class Answer {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy=GenerationType.IDENTITY)
|
||||
@Column(name="id")
|
||||
private long id;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "question",nullable = false)
|
||||
private Question question;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "form",nullable = false)
|
||||
private QuizzForm form;
|
||||
|
||||
@Convert(converter = JsonNodeConverter.class)
|
||||
private JsonNode value;
|
||||
|
||||
public Answer(QuizzForm qf, Question q) {
|
||||
super();
|
||||
this.setQuestion(q);
|
||||
this.setForm(qf);
|
||||
this.setValue(JsonNodeFactory.instance.objectNode());
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
package com.bernard.misael.model;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
public enum Privilege implements GrantedAuthority {
|
||||
|
||||
LIST_USERS,ADD_USERS,LIST_QUIZZ,CREATE_QUIZZ,VIEW_ALL_FORMS;
|
||||
|
||||
@Override
|
||||
public String getAuthority() {
|
||||
return this.name();
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,49 +0,0 @@
|
||||
package com.bernard.misael.model;
|
||||
|
||||
import com.bernard.misael.questions.QTypes;
|
||||
import com.bernard.misael.questions.QuestionType;
|
||||
import com.bernard.misael.service.JsonNodeConverter;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
@Entity
|
||||
@Table(name="questions")
|
||||
public class Question {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy=GenerationType.IDENTITY)
|
||||
@Column(name="id")
|
||||
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
|
||||
@Convert(converter = JsonNodeConverter.class)
|
||||
private JsonNode value;
|
||||
|
||||
transient QuestionType qtype = null;
|
||||
public QuestionType getQT() {
|
||||
if(qtype==null){
|
||||
qtype = type.construct(value);
|
||||
}
|
||||
return qtype;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,45 +0,0 @@
|
||||
package com.bernard.misael.model;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.annotations.ColumnDefault;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Entity
|
||||
@Table(name="quizz")
|
||||
public class Quizz {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable=false, unique=true)
|
||||
private String name;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name="owner")
|
||||
private User owner;
|
||||
|
||||
@Column(nullable=false)
|
||||
@ColumnDefault("false")
|
||||
private boolean isComplete;
|
||||
|
||||
@Column(nullable=false)
|
||||
@ColumnDefault("0")
|
||||
private int questionCount;
|
||||
|
||||
@OneToMany(mappedBy="quizz")
|
||||
private Set<Question> questions;
|
||||
|
||||
@Column(nullable=true)
|
||||
private Integer publicQuestionCount;
|
||||
}
|
||||
@ -1,38 +0,0 @@
|
||||
package com.bernard.misael.model;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Entity
|
||||
@Table(name="quizzf")
|
||||
public class QuizzForm {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "answerer",nullable = false)
|
||||
private User user;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "quizz",nullable=false)
|
||||
private Quizz quizz;
|
||||
|
||||
private boolean done;
|
||||
private int currentQuestion;
|
||||
private int answerStep;
|
||||
}
|
||||
@ -1,62 +0,0 @@
|
||||
package com.bernard.misael.model;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
import jakarta.persistence.CollectionTable;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.ElementCollection;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.EnumType;
|
||||
import jakarta.persistence.Enumerated;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToMany;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Entity
|
||||
@Table(name="roles")
|
||||
public class Role implements GrantedAuthority
|
||||
{
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable=false, unique=true)
|
||||
private String name;
|
||||
|
||||
@ManyToMany(mappedBy="roles")
|
||||
private List<User> users;
|
||||
|
||||
@ElementCollection(fetch = FetchType.EAGER)
|
||||
@Enumerated(EnumType.STRING)
|
||||
@CollectionTable(name = "role_privileges"
|
||||
, joinColumns = @JoinColumn(name = "id"))
|
||||
@Column(name = "privileges", nullable = false)
|
||||
private Set<Privilege> privileges;
|
||||
|
||||
public Role(String name){
|
||||
super();
|
||||
this.setName(name);
|
||||
this.setPrivileges(EnumSet.noneOf(Privilege.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthority() {
|
||||
return "ROLE_"+name;
|
||||
}
|
||||
}
|
||||
@ -1,54 +0,0 @@
|
||||
package com.bernard.misael.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.JoinTable;
|
||||
import jakarta.persistence.ManyToMany;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString
|
||||
@Entity
|
||||
@Table(name="users")
|
||||
public class User
|
||||
{
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable=false,unique = true)
|
||||
private String name;
|
||||
|
||||
@Column(nullable=false)
|
||||
private String password;
|
||||
|
||||
@ManyToMany(fetch = FetchType.EAGER, cascade=CascadeType.ALL)
|
||||
@JoinTable(
|
||||
name="users_roles",
|
||||
joinColumns={@JoinColumn(name="USER_ID", referencedColumnName="ID")},
|
||||
inverseJoinColumns={@JoinColumn(name="ROLE_ID", referencedColumnName="ID")})
|
||||
private List<Role> roles = new ArrayList<>();
|
||||
|
||||
@OneToMany(mappedBy="owner")
|
||||
private Set<Quizz> myQuizzs;
|
||||
|
||||
}
|
||||
@ -1,117 +0,0 @@
|
||||
package com.bernard.misael.questions;
|
||||
|
||||
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;
|
||||
|
||||
public class DccQuestion implements QuestionType{
|
||||
|
||||
String text;
|
||||
String goodAnswer;
|
||||
String[] wrongAnswers;
|
||||
|
||||
public DccQuestion(JsonNode data){
|
||||
text = data.get("text").asText();
|
||||
goodAnswer = data.get("good").asText();
|
||||
JsonNode wrong = data.get("wrong");
|
||||
wrongAnswers = new String[wrong.size()];
|
||||
for(int i = 0; i<wrong.size();i++)
|
||||
wrongAnswers[i] = wrong.get(i).asText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonNode clientQuestionData(int step, JsonNode answer) throws QuestionTypeException{
|
||||
ObjectNode o = JsonNodeFactory.instance.objectNode();
|
||||
o.put("text", this.text);
|
||||
switch (step) {
|
||||
case 0:
|
||||
return o;
|
||||
case 1:
|
||||
Random r = new Random();
|
||||
if(!answer.has("type") || !answer.get("type").isInt()) throw new MalformedAnswerException();
|
||||
int k = answer.get("type").asInt();
|
||||
switch(k) {
|
||||
case 0:
|
||||
return o;
|
||||
case 2:
|
||||
case 4:
|
||||
List<String> answers = new ArrayList<>(k);
|
||||
answers.add(goodAnswer);
|
||||
for(int i = 0;i<k-1;i++)
|
||||
answers.add(wrongAnswers[i]);
|
||||
Collections.shuffle(answers, r);
|
||||
o.putArray("answers").addAll(answers.stream().map(JsonNodeFactory.instance::textNode).collect(Collectors.toList()));
|
||||
return o;
|
||||
default:
|
||||
throw new MalformedAnswerException();
|
||||
}
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Question step invalid : "+step);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnswerResult clientAnswers(int step, JsonNode oldAnswer, JsonNode clientAnswer) throws QuestionTypeException {
|
||||
ObjectNode o = JsonNodeFactory.instance.objectNode();
|
||||
switch (step) {
|
||||
case 0:
|
||||
if(!clientAnswer.isInt()) throw new MalformedClientAnswerException();
|
||||
int answerType = clientAnswer.asInt();
|
||||
if(answerType != 0 && answerType != 2 && answerType != 4)
|
||||
throw new IllegalArgumentException("Illegal answer type: "+answerType);
|
||||
o.put("type", answerType);
|
||||
return new AnswerResult(o, false, 1);
|
||||
case 1:
|
||||
if(!clientAnswer.isTextual()) throw new MalformedClientAnswerException();
|
||||
String answer = clientAnswer.asText();
|
||||
o = oldAnswer.deepCopy();
|
||||
o.put("answer", answer);
|
||||
return new AnswerResult(o, true, -1);
|
||||
default:
|
||||
throw new IllegalArgumentException("Question step invalid : "+step);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -1,38 +0,0 @@
|
||||
package com.bernard.misael.questions;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@AllArgsConstructor
|
||||
public enum QTypes {
|
||||
|
||||
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())
|
||||
if (value.id == id)
|
||||
return value;
|
||||
return null;
|
||||
}
|
||||
|
||||
public QuestionType construct(JsonNode data){
|
||||
return this.constructor.apply(data);
|
||||
}
|
||||
|
||||
public boolean validate(JsonNode data){
|
||||
return this.validator.apply(data);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
package com.bernard.misael.questions;
|
||||
|
||||
import com.bernard.misael.service.exception.QuestionTypeException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
public interface QuestionType {
|
||||
|
||||
JsonNode clientQuestionData(int step, JsonNode answer) throws QuestionTypeException;
|
||||
|
||||
AnswerResult clientAnswers(int step, JsonNode oldAnswer, JsonNode clientAnswer) throws QuestionTypeException;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public static class AnswerResult {
|
||||
private JsonNode newAnswer;
|
||||
private boolean nextQuestion;
|
||||
private int nextStep;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
package com.bernard.misael.repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import com.bernard.misael.model.Answer;
|
||||
import com.bernard.misael.model.Question;
|
||||
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,18 +0,0 @@
|
||||
package com.bernard.misael.repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import com.bernard.misael.model.Question;
|
||||
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);
|
||||
public List<Question> findByQuizzOrderByIndexAsc(Quizz quizz);
|
||||
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
package com.bernard.misael.repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import com.bernard.misael.model.Quizz;
|
||||
import com.bernard.misael.model.QuizzForm;
|
||||
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);
|
||||
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
package com.bernard.misael.repository;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
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> findByPublicQuestionCountIsNotNullAndIsCompleteTrue();
|
||||
public @NonNull Set<Quizz> findByOwnerAndIsCompleteTrue(@NonNull User owner);
|
||||
public @NonNull Set<Quizz> findByOwnerAndIsCompleteFalse(@NonNull User owner);
|
||||
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
package com.bernard.misael.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import com.bernard.misael.model.Role;
|
||||
|
||||
public interface RoleRepository extends JpaRepository<Role, Long> {
|
||||
|
||||
Role findByName(String name);
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
package com.bernard.misael.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import com.bernard.misael.model.User;
|
||||
|
||||
public interface UserRepository extends JpaRepository<User, Long> {
|
||||
|
||||
User findByName(String pseudo);
|
||||
|
||||
}
|
||||
@ -1,74 +0,0 @@
|
||||
package com.bernard.misael.service;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.bernard.misael.model.Privilege;
|
||||
import com.bernard.misael.model.Role;
|
||||
import com.bernard.misael.model.User;
|
||||
import com.bernard.misael.repository.RoleRepository;
|
||||
import com.bernard.misael.repository.UserRepository;
|
||||
|
||||
import jakarta.transaction.Transactional;
|
||||
|
||||
@Component
|
||||
public class AdminMaker implements
|
||||
ApplicationListener<ContextRefreshedEvent> {
|
||||
|
||||
boolean alreadySetup = false;
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
@Autowired
|
||||
private RoleRepository roleRepository;
|
||||
|
||||
@Autowired
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void onApplicationEvent(@NonNull ContextRefreshedEvent event) {
|
||||
|
||||
if (alreadySetup)
|
||||
return;
|
||||
Logger log = Logger.getLogger("AdminMaker");
|
||||
log.info("Checking that privileges and mysaa user exist");
|
||||
|
||||
Role adminRole = createRoleIfNotFound("ADMIN", EnumSet.allOf(Privilege.class));
|
||||
createRoleIfNotFound("USER", EnumSet.noneOf(Privilege.class));
|
||||
|
||||
User mysaa = userRepository.findByName("mysaa");
|
||||
if (mysaa == null) {
|
||||
User user = new User();
|
||||
user.setName("mysaa");
|
||||
user.setPassword(passwordEncoder.encode("super"));
|
||||
user.setRoles(Arrays.asList(adminRole));
|
||||
userRepository.save(user);
|
||||
}
|
||||
|
||||
alreadySetup = true;
|
||||
log.info("Everything needed has been created");
|
||||
}
|
||||
|
||||
@Transactional
|
||||
private Role createRoleIfNotFound(
|
||||
String name, EnumSet<Privilege> privileges) {
|
||||
|
||||
Role role = roleRepository.findByName(name);
|
||||
if (role == null) {
|
||||
role = new Role(name);
|
||||
role.setPrivileges(privileges);
|
||||
roleRepository.save(role);
|
||||
}
|
||||
return role;
|
||||
}
|
||||
}
|
||||
@ -1,32 +0,0 @@
|
||||
package com.bernard.misael.service;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import jakarta.persistence.AttributeConverter;
|
||||
|
||||
|
||||
public class JsonNodeConverter implements AttributeConverter<JsonNode, String> {
|
||||
|
||||
private ObjectMapper om;
|
||||
|
||||
public JsonNodeConverter() {
|
||||
this.om = new ObjectMapper();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String convertToDatabaseColumn(JsonNode attribute) {
|
||||
return attribute.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonNode convertToEntityAttribute(String dbData) {
|
||||
try {
|
||||
return om.readTree(dbData);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,40 +0,0 @@
|
||||
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;
|
||||
|
||||
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> 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);
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
@ -1,601 +0,0 @@
|
||||
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;
|
||||
|
||||
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.*;
|
||||
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.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
||||
import jakarta.persistence.EntityNotFoundException;
|
||||
|
||||
@Service
|
||||
public class QuizzManagerImpl implements QuizzManager {
|
||||
|
||||
@Autowired
|
||||
UserRepository uRepository;
|
||||
|
||||
@Autowired
|
||||
QuizzFormRepository qfRepository;
|
||||
|
||||
@Autowired
|
||||
QuizzRepository qRepository;
|
||||
|
||||
@Autowired
|
||||
QuestionRepository questionRepository;
|
||||
|
||||
@Autowired
|
||||
AnswerRepository answerRepository;
|
||||
|
||||
@Autowired
|
||||
UserService uService;
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public JsonNode answer(User user, long quizzId, JsonNode data) {
|
||||
if(!data.has("index") || !data.get("index").isInt())
|
||||
return errorNode("Request should contain the question index");
|
||||
if(!data.has("step") || !data.get("step").isInt())
|
||||
return errorNode("Request should contain the answer step");
|
||||
if(!data.has("data"))
|
||||
return errorNode("Request should contain the answer data");
|
||||
if(user == null)
|
||||
return errorNode("You must be logged in to answer");
|
||||
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)
|
||||
return errorNode("The quizzform does not exist, ask the question first");
|
||||
if(qf.isDone())
|
||||
return errorNode("You're done with the quizz, you cannot answer anymore");
|
||||
int qindex = qf.getCurrentQuestion();
|
||||
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);
|
||||
int step = qf.getAnswerStep();
|
||||
if(step != data.get("step").intValue())
|
||||
return errorNode("You are not answering the right step of the question (you answer step "+data.get("step").intValue()
|
||||
+" where you should step question "+step+")");
|
||||
|
||||
Answer answer = answerRepository.findByFormAndQuestion(qf, q);
|
||||
if(answer == null)
|
||||
return errorNode("The database answer object does not exist, ask the question first");
|
||||
|
||||
JsonNode answerData = answer.getValue();
|
||||
AnswerResult result;
|
||||
try {
|
||||
result = q.getQT().clientAnswers(step, answerData, data.get("data"));
|
||||
} catch (MalformedAnswerException e) {
|
||||
return errorNode("The previous answer stored in database is invalid");
|
||||
} catch (MalformedClientAnswerException e) {
|
||||
return errorNode("This answer is not valid here");
|
||||
} catch (QuestionTypeException e) {
|
||||
return errorNode("Unknown error from the QuestionType");
|
||||
} catch (IllegalArgumentException e) {
|
||||
return errorNode("The QuestionType did not recognize the step of the question");
|
||||
}
|
||||
if(result.isNextQuestion()) {
|
||||
qf.setCurrentQuestion(qindex+1);
|
||||
qf.setAnswerStep(0);
|
||||
if(qf.getCurrentQuestion() >= quizz.getQuestionCount()){
|
||||
// The quizz is done
|
||||
qf.setDone(true);
|
||||
}
|
||||
} else {
|
||||
qf.setAnswerStep(result.getNextStep());
|
||||
}
|
||||
answer.setValue(result.getNewAnswer());
|
||||
answerRepository.save(answer);
|
||||
ObjectNode out = JsonNodeFactory.instance.objectNode();
|
||||
out.set("success", JsonNodeFactory.instance.booleanNode(true));
|
||||
return out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonNode next(User user, long quizzId) {
|
||||
if(user == null)
|
||||
return errorNode("You need to be logged in to discover the questions");
|
||||
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){
|
||||
// We should create the quizzform
|
||||
qf = newQuizzForm(user, quizz);
|
||||
}
|
||||
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);
|
||||
int step = qf.getAnswerStep();
|
||||
Answer answer = answerRepository.findByFormAndQuestion(qf, q);
|
||||
if(answer==null){
|
||||
// We construct the blank answer
|
||||
answer = new Answer(qf,q);
|
||||
answerRepository.save(answer);
|
||||
}
|
||||
|
||||
JsonNode answerData;
|
||||
answerData = answer.getValue();
|
||||
JsonNode qdata;
|
||||
try {
|
||||
qdata = q.getQT().clientQuestionData(step, answerData);
|
||||
} catch (MalformedAnswerException e) {
|
||||
return errorNode("The previous answer stored in database is invalid");
|
||||
} catch (QuestionTypeException e) {
|
||||
return errorNode("Unknown error from the QuestionType");
|
||||
} catch (IllegalArgumentException e) {
|
||||
return errorNode("The QuestionType did not recognize the step of the question");
|
||||
}
|
||||
|
||||
ObjectNode out = JsonNodeFactory.instance.objectNode();
|
||||
out.set("success", JsonNodeFactory.instance.booleanNode(true));
|
||||
out.set("index", JsonNodeFactory.instance.numberNode(qindex));
|
||||
out.set("step", JsonNodeFactory.instance.numberNode(step));
|
||||
out.set("data", qdata);
|
||||
return out;
|
||||
}
|
||||
|
||||
private QuizzForm newQuizzForm(User user, Quizz quizz) {
|
||||
QuizzForm qf = new QuizzForm();
|
||||
qf.setUser(user);
|
||||
qf.setQuizz(quizz);
|
||||
qf.setDone(false);
|
||||
qf.setCurrentQuestion(0);
|
||||
qf.setAnswerStep(0);
|
||||
qfRepository.save(qf);
|
||||
return qf;
|
||||
}
|
||||
|
||||
@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));
|
||||
return out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canAccessQuizz(User user, long quizzId) {
|
||||
try{
|
||||
Quizz quizz = qRepository.getReferenceById(quizzId);
|
||||
return quizz.getPublicQuestionCount()!=null || quizz.getOwner().equals(user);
|
||||
} catch (EntityNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Quizz> answerableQuizz(User user) {
|
||||
Set<Quizz> ownQuizz = qRepository.findByOwnerAndIsCompleteTrue(user);
|
||||
Set<Quizz> publicQuizz = qRepository.findByPublicQuestionCountIsNotNullAndIsCompleteTrue();
|
||||
publicQuizz.removeAll(ownQuizz);
|
||||
|
||||
return Stream.concat(
|
||||
ownQuizz.stream()
|
||||
.sorted((q1,q2) -> q1.getName().compareTo(q2.getName())),
|
||||
publicQuizz.stream()
|
||||
.sorted((q1,q2) -> q1.getName().compareTo(q2.getName()))
|
||||
).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) {
|
||||
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));
|
||||
if(oquizz.get().isComplete())
|
||||
return Optional.of(errorNode("Quizz is complete, cannot edit, answers might have already been cast"));
|
||||
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()));
|
||||
nn.set("value",q.getValue());
|
||||
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);
|
||||
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("id", JsonNodeFactory.instance.numberNode(q.getId()));
|
||||
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);
|
||||
|
||||
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);
|
||||
} else {
|
||||
n =q.getValue();
|
||||
}
|
||||
|
||||
ObjectNode out = JsonNodeFactory.instance.objectNode();
|
||||
out.set("success", JsonNodeFactory.instance.booleanNode(true));
|
||||
out.set("new", n);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
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;
|
||||
|
||||
public interface UserService {
|
||||
|
||||
void saveUser(UserDto userDto);
|
||||
void changePassword(User user, String password);
|
||||
|
||||
User findUserByName(String name);
|
||||
|
||||
List<UserDto> findAllUsers();
|
||||
|
||||
boolean hasPrivilege(User u, Privilege p);
|
||||
}
|
||||
@ -1,64 +0,0 @@
|
||||
package com.bernard.misael.service;
|
||||
import java.util.List;
|
||||
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;
|
||||
|
||||
@Service
|
||||
public class UserServiceImpl implements UserService {
|
||||
|
||||
private UserRepository userRepository;
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
public UserServiceImpl(UserRepository userRepository,
|
||||
PasswordEncoder passwordEncoder) {
|
||||
this.userRepository = userRepository;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveUser(UserDto userDto) {
|
||||
User user = new User();
|
||||
user.setName(userDto.getName());
|
||||
// encrypt the password using spring security
|
||||
user.setPassword(passwordEncoder.encode(userDto.getPassword()));
|
||||
|
||||
user.setRoles(List.of());
|
||||
userRepository.save(user);
|
||||
}
|
||||
@Override
|
||||
public void changePassword(User user, String password) {
|
||||
user.setPassword(passwordEncoder.encode(password));
|
||||
}
|
||||
|
||||
@Override
|
||||
public User findUserByName(String name) {
|
||||
return userRepository.findByName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserDto> findAllUsers() {
|
||||
List<User> users = userRepository.findAll();
|
||||
return users.stream()
|
||||
.map((user) -> mapToUserDto(user))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private UserDto mapToUserDto(User user){
|
||||
UserDto userDto = new UserDto();
|
||||
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));
|
||||
}
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
package com.bernard.misael.service.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class UserDto
|
||||
{
|
||||
private Long id;
|
||||
@NotEmpty(message = "User name should not be empty")
|
||||
private String name;
|
||||
@NotEmpty(message = "Password should not be empty")
|
||||
private String password;
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
package com.bernard.misael.service.exception;
|
||||
|
||||
public class MalformedAnswerException extends QuestionTypeException{
|
||||
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
package com.bernard.misael.service.exception;
|
||||
|
||||
public class MalformedClientAnswerException extends QuestionTypeException {
|
||||
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
package com.bernard.misael.service.exception;
|
||||
|
||||
public abstract class QuestionTypeException extends Exception{
|
||||
|
||||
}
|
||||
@ -1,112 +0,0 @@
|
||||
package com.bernard.misael.web;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import com.bernard.misael.model.Role;
|
||||
import com.bernard.misael.model.User;
|
||||
import com.bernard.misael.repository.UserRepository;
|
||||
import com.bernard.misael.service.UserService;
|
||||
import com.bernard.misael.service.dto.UserDto;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.Getter;
|
||||
|
||||
@Controller
|
||||
public class AuthController {
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
@Autowired
|
||||
private UserRepository urepo;
|
||||
|
||||
public AuthController(UserService userService) {
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@GetMapping("/login")
|
||||
public String loginPage() {
|
||||
return "login";
|
||||
}
|
||||
|
||||
@GetMapping("/")
|
||||
public String index(Model model) {
|
||||
return "index";
|
||||
}
|
||||
|
||||
@GetMapping("/users")
|
||||
@Secured("LIST_USERS")
|
||||
public String listUsers(Model model, Principal p){
|
||||
// User should have MANAGE_USERS now
|
||||
UserDto newUser = new UserDto();
|
||||
model.addAttribute("newuser",newUser);
|
||||
List<UserInfo> userz = urepo.findAll().stream().map(UserInfo::new).sorted().toList();
|
||||
model.addAttribute("users", userz);
|
||||
return "users";
|
||||
}
|
||||
|
||||
@PostMapping("/adduser")
|
||||
@Secured("ADD_USERS")
|
||||
public String registration(@Valid @ModelAttribute("newuser") UserDto userDto,
|
||||
BindingResult result,
|
||||
Model model,
|
||||
Principal p){
|
||||
User existingUser = userService.findUserByName(userDto.getName());
|
||||
|
||||
if(existingUser != null){
|
||||
result.reject("User is already registered");
|
||||
}
|
||||
|
||||
if(result.hasErrors()){
|
||||
List<UserInfo> userz = urepo.findAll().stream().map(UserInfo::new).sorted().toList();
|
||||
model.addAttribute("users", userz);
|
||||
model.addAttribute("newuser",userDto);
|
||||
return "redirect:/users?duplicate";
|
||||
}
|
||||
userService.saveUser(userDto);
|
||||
return "redirect:/users?success";
|
||||
}
|
||||
|
||||
@GetMapping("/change-password")
|
||||
public String showChangePassword(){
|
||||
return "change-password";
|
||||
}
|
||||
|
||||
@PostMapping("/change-password/change")
|
||||
public String changePassword(@RequestParam("new-password") String newPassword, Principal p) {
|
||||
User u = null;
|
||||
if (p==null)
|
||||
return "redirect:/login?restricted";
|
||||
u = userService.findUserByName(p.getName());
|
||||
userService.changePassword(u, newPassword);
|
||||
|
||||
return "redirect:/change-password?success";
|
||||
}
|
||||
|
||||
@Getter
|
||||
public static class UserInfo implements Comparable<UserInfo> {
|
||||
private long id;
|
||||
private String pseudo;
|
||||
private String roles;
|
||||
public UserInfo(User u){
|
||||
this.id = u.getId();
|
||||
this.pseudo = u.getName();
|
||||
this.roles = u.getRoles().stream().map(Role::getName).collect(Collectors.joining(";"));
|
||||
}
|
||||
@Override
|
||||
public int compareTo(UserInfo other) {
|
||||
return this.pseudo.compareTo(other.pseudo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,344 +0,0 @@
|
||||
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;
|
||||
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;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
|
||||
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/questions")
|
||||
public class QuestionsController {
|
||||
|
||||
@Autowired
|
||||
UserRepository ur;
|
||||
|
||||
@Autowired
|
||||
QuizzManager qm;
|
||||
|
||||
@Autowired
|
||||
QuizzRepository qrepo;
|
||||
|
||||
@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("answerableQuizz",qm.answerableQuizz(u));
|
||||
model.addAttribute("editableQuizz",qm.editableQuizz(u));
|
||||
}
|
||||
|
||||
return "quizz.html";
|
||||
}
|
||||
|
||||
/*
|
||||
* List all forms started or finished by the user
|
||||
*/
|
||||
@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)
|
||||
return "redirect:/login?restricted";
|
||||
m.addAttribute("formid", quizzId);
|
||||
Quizz q = qrepo.getReferenceById(quizzId);
|
||||
m.addAttribute("quizzLength",q.getQuestionCount());
|
||||
|
||||
return "form";
|
||||
}
|
||||
|
||||
@GetMapping("/question/{q}")
|
||||
public ResponseEntity<JsonNode> question(@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.next(u, quizzId);
|
||||
return new ResponseEntity<>(out, HttpStatus.OK);
|
||||
}
|
||||
|
||||
@PostMapping("/answer/{q}")
|
||||
public ResponseEntity<JsonNode> answer(@PathVariable("q") long quizzId, @RequestBody JsonNode data, Principal p) {
|
||||
User u = null;
|
||||
if (p!=null)
|
||||
u = ur.findByName(p.getName());
|
||||
JsonNode out = qm.answer(u, quizzId, data);
|
||||
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}")
|
||||
public Object quizzEdit(@PathVariable("q") long quizzId, Principal p, Model m) {
|
||||
if (p==null)
|
||||
return "redirect:/login?restricted";
|
||||
User u = ur.findByName(p.getName());
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -1,39 +0,0 @@
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:postgresql://127.0.0.1:5432/misael
|
||||
username: misael
|
||||
password: misael-dev
|
||||
driver-class-name: org.postgresql.Driver
|
||||
hikari:
|
||||
schema: misael
|
||||
flyway:
|
||||
enabled: true
|
||||
locations: classpath:db/migration/structure, classpath:db/migration/data
|
||||
validate-on-migrate: true
|
||||
default-schema: misael
|
||||
create-schemas: true
|
||||
session:
|
||||
jdbc:
|
||||
initialize-schema: always
|
||||
security:
|
||||
user:
|
||||
name: mysaa
|
||||
|
||||
jpa:
|
||||
properties:
|
||||
javax:
|
||||
persistence:
|
||||
schema-generation:
|
||||
create-source: metadata
|
||||
scripts:
|
||||
action: update
|
||||
create-target: db-migration.sql
|
||||
database:
|
||||
action: none
|
||||
hibernate:
|
||||
ddl-auto: none
|
||||
generate-ddl: true
|
||||
|
||||
logging:
|
||||
level:
|
||||
root: INFO
|
||||
@ -1,7 +0,0 @@
|
||||
create table roles (id bigint generated by default as identity, name varchar(255) not null, primary key (id));
|
||||
create table users (id bigint generated by default as identity, name varchar(255) not null, password varchar(255) not null, primary key (id));
|
||||
create table users_roles (user_id bigint not null, role_id bigint not null);
|
||||
alter table if exists roles drop constraint if exists UKofx66keruapi6vyqpv6f2or37;
|
||||
alter table if exists roles add constraint UKofx66keruapi6vyqpv6f2or37 unique (name);
|
||||
alter table if exists users_roles add constraint FKj6m8fwv7oqv74fcehir1a9ffy foreign key (role_id) references roles;
|
||||
alter table if exists users_roles add constraint FK2o0jvgh89lemvvo17cbqvdxaa foreign key (user_id) references users;
|
||||
@ -1,12 +0,0 @@
|
||||
create table answers (id bigint generated by default as identity, value varchar(255), form bigint not null, question bigint not null, primary key (id));
|
||||
create table questions (id bigint generated by default as identity, index integer not null, type smallint not null check (type between 0 and 0), value varchar(255), quizz bigint not null, primary key (id));
|
||||
create table quizz (id bigint generated by default as identity, is_public boolean default false not null, name varchar(255) not null, question_count integer default 0 not null, owner bigint, primary key (id));
|
||||
create table quizzf (id bigint generated by default as identity, answer_step integer not null, current_question integer not null, done boolean not null, quizz bigint not null, answerer bigint not null, primary key (id));
|
||||
alter table if exists quizz drop constraint if exists UKc1plspc0ecmwqqpfaf24avb4c;
|
||||
alter table if exists quizz add constraint UKc1plspc0ecmwqqpfaf24avb4c unique (name);
|
||||
alter table if exists answers add constraint FKjtutlsv5n10071rq261lcjovm foreign key (form) references quizzf;
|
||||
alter table if exists answers add constraint FK54dobrdq2u51m4u8s7kg0as8v foreign key (question) references questions;
|
||||
alter table if exists questions add constraint FKq12h25ynjok1m497gwos511te foreign key (quizz) references quizz;
|
||||
alter table if exists quizz add constraint FKfeoogns8m4m4hvno1ttqb30wm foreign key (owner) references users;
|
||||
alter table if exists quizzf add constraint FK4ukbg2yaa93gs5nx1s5d9rqu4 foreign key (quizz) references quizz;
|
||||
alter table if exists quizzf add constraint FK7gvxodb5t2n68ot577ig9u3w6 foreign key (answerer) references users;
|
||||
@ -1,2 +0,0 @@
|
||||
create table role_privileges (id bigint not null, privileges varchar(255) not null check (privileges in ('LIST_USERS','ADD_USERS','LIST_QUIZZ')), primary key (id, privileges));
|
||||
alter table if exists role_privileges add constraint FK7xfa4foqs58j7rhk6ex78hpf3 foreign key (id) references roles;
|
||||
@ -1 +0,0 @@
|
||||
alter table quizz add column "is_complete" BOOLEAN NOT NULL DEFAULT FALSE;
|
||||
@ -1,8 +0,0 @@
|
||||
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');
|
||||
@ -1,2 +0,0 @@
|
||||
alter table if exists quizz add column public_question_count integer default NULL;
|
||||
alter table if exists quizz drop column is_public;
|
||||
@ -1,98 +0,0 @@
|
||||
/* Button box */
|
||||
div.buttonbox {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
textarea.squared-input {
|
||||
border-radius: 5px;
|
||||
padding: 4px;
|
||||
font-size: 1rem;
|
||||
|
||||
margin-bottom: 12px;
|
||||
width: 400px;
|
||||
min-height: 65px;
|
||||
}
|
||||
/* Common to all the answer buttons*/
|
||||
button.squared-button {
|
||||
border: 0;
|
||||
border-radius: 5px;
|
||||
color: #1F1A25;
|
||||
font-family: Arial, sans-serif;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
padding: 4px;
|
||||
font-size: 1rem;
|
||||
|
||||
margin-bottom: 12px;
|
||||
width: 400px;
|
||||
min-height: 65px;
|
||||
}
|
||||
|
||||
button.squared-button:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button.squared-button:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
button.squared-button:active {
|
||||
transform: translateY(4px);
|
||||
}
|
||||
|
||||
/*@import url("https://fonts.googleapis.com/css?family=Muli:400,700,800&display=swap");
|
||||
|
||||
body {
|
||||
background: #1F1A25;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100vh;
|
||||
}
|
||||
*/
|
||||
|
||||
/* Color 1 */
|
||||
button.squared-button.bcol1 {
|
||||
background-color: #78E0DC;
|
||||
box-shadow: 0 8px #2dc5bf;
|
||||
}
|
||||
button.squared-button.bcol1:hover, button.squared-button.bcol1:focus {
|
||||
background-color: #4fd6d1;
|
||||
}
|
||||
|
||||
button.squared-button.bcol1:active {
|
||||
box-shadow: 0 3px #2dc5bf;
|
||||
}
|
||||
/* Color 2 */
|
||||
button.squared-button.bcol2 {
|
||||
background-color: #FDE74C;
|
||||
box-shadow: 0 8px #d7bd02;
|
||||
}
|
||||
button.squared-button.bcol2:hover, button.squared-button.bcol2:focus {
|
||||
background-color: #fcde0d;
|
||||
}
|
||||
button.squared-button.bcol2:active {
|
||||
box-shadow: 0 3px #d7bd02;
|
||||
}
|
||||
/* Color 3 */
|
||||
button.squared-button.bcol3 {
|
||||
background-color: #B8B3E9;
|
||||
box-shadow: 0 8px #867ec8;
|
||||
}
|
||||
button.squared-button.bcol3:hover, button.squared-button.bcol3:focus {
|
||||
background-color: #b0acd9;
|
||||
}
|
||||
button.squared-button.bcol3:active {
|
||||
box-shadow: 0 3px #867ec8;
|
||||
}
|
||||
/* Color 4 */
|
||||
button.squared-button.bcol4 {
|
||||
background-color: #4fd337;
|
||||
box-shadow: 0 8px #339721;
|
||||
}
|
||||
button.squared-button.bcol4:hover, button.squared-button.bcol4:focus {
|
||||
background-color: #40be29;
|
||||
}
|
||||
button.squared-button.bcol4:active {
|
||||
box-shadow: 0 3px #339721;
|
||||
}
|
||||
@ -1,60 +0,0 @@
|
||||
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;
|
||||
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%;
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
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%;
|
||||
}
|
||||
@ -1,33 +0,0 @@
|
||||
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;
|
||||
}
|
||||
@ -1,81 +0,0 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap');
|
||||
*{
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
}
|
||||
nav{
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 75px;
|
||||
background: #2980b9;
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
nav .navbar{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 100%;
|
||||
max-width: 90%;
|
||||
background: #2980b9;
|
||||
margin: auto;
|
||||
}
|
||||
nav .navbar .logo a{
|
||||
color: #fff;
|
||||
font-size: 27px;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
}
|
||||
nav .navbar .menu{
|
||||
display: flex;
|
||||
}
|
||||
.navbar .menu li{
|
||||
list-style: none;
|
||||
margin: 0 15px;
|
||||
}
|
||||
.navbar .menu li a{
|
||||
color: #fff;
|
||||
font-size: 17px;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
}
|
||||
main{
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #96c7e8;
|
||||
font-size: 24px;
|
||||
flex-direction: column;
|
||||
margin-top: 115px;
|
||||
}
|
||||
.button a{
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
color: #fff;
|
||||
background: #2980b9;
|
||||
padding: 7px 12px;;
|
||||
font-size: 18px;
|
||||
border-radius: 6px;
|
||||
box-shadow: rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
div.alert {
|
||||
background: #FFDDEE;
|
||||
font-size: 20px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
div.welcome{
|
||||
color: #fff;
|
||||
font-size: 20px;
|
||||
font-style: italic;
|
||||
}
|
||||
table, th, td {
|
||||
border: 1px solid black;
|
||||
border-collapse: collapse;
|
||||
padding: 4px;
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB |
@ -1,25 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr" dir="ltr">
|
||||
<head>
|
||||
<div th:replace="~{html-head}"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div th:replace="~{header}"/>
|
||||
<main>
|
||||
|
||||
<div th:if="${param.success}">
|
||||
<div class="alert alert-success"> Votre mot de passe a été changé</div><br/>
|
||||
</div>
|
||||
<form
|
||||
method="post"
|
||||
role="form"
|
||||
th:action="@{/change-password/change}">
|
||||
<label for="new-password">Nouveau mot de passe :</label>
|
||||
<input type="password" id="new-password" name="new-password"/>
|
||||
<br/>
|
||||
<input id="connect" type="submit" value="Changer de mot de passe"/>
|
||||
</form>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,175 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr" dir="ltr">
|
||||
<head>
|
||||
<div th:replace="~{html-head}"/>
|
||||
<link rel="stylesheet" th:href="@{/css/form.css}"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div th:replace="~{header}"/>
|
||||
<script th:src="@{/webjars/jquery/1.9.1/jquery.min.js}"></script>
|
||||
<main>
|
||||
<span id="question-text"></span>
|
||||
<span id="question-counter">0/0</span>
|
||||
<div id="answers">
|
||||
<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-cash">Cash</button>
|
||||
</div>
|
||||
<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>
|
||||
<button class="squared-button bcol2" id="answer-duo-button-2"></button>
|
||||
</div>
|
||||
<div class="buttonbox" id="answer-carre">
|
||||
<button class="squared-button bcol1" id="answer-carre-button-1"></button>
|
||||
<button class="squared-button bcol2" id="answer-carre-button-2"></button>
|
||||
<button class="squared-button bcol3" id="answer-carre-button-3"></button>
|
||||
<button class="squared-button bcol4" id="answer-carre-button-4"></button>
|
||||
</div>
|
||||
</div>
|
||||
<span id="error-textbox" style="color: red"></span>
|
||||
<button id="question-button">Poser la question</button>
|
||||
<script th:inline="javascript">
|
||||
/*<![CDATA[*/
|
||||
var qid = /*[[${formid}]]*/ -1;
|
||||
var qlength = /*[[${quizzLength}]]*/ -1;
|
||||
/*]]>*/
|
||||
</script>
|
||||
<script>
|
||||
qindex = 0
|
||||
qstep = 0
|
||||
function error(txt) {
|
||||
console.log(txt)
|
||||
$("#error-textbox").text(txt)
|
||||
}
|
||||
function clearerror() {
|
||||
$("#error-textbox").text("")
|
||||
}
|
||||
function next() {
|
||||
$.ajax({
|
||||
url: "/questions/question/"+qid,
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
success: function(res) {
|
||||
console.log(res)
|
||||
qindex=res["index"]
|
||||
qstep=res["step"]
|
||||
if(!res["success"]) {
|
||||
console.log(res["message"])
|
||||
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-cash").hide()
|
||||
}else if(qstep==1){
|
||||
awrs = res["data"]["answers"]
|
||||
|
||||
$("#answer-select").hide()
|
||||
$("#answer-duo").hide()
|
||||
$("#answer-carre").hide()
|
||||
$("#answer-cash").hide()
|
||||
if(typeof awrs === 'undefined'){
|
||||
$("#answer-cash").show()
|
||||
$("#answer-cash-input").val("")
|
||||
} else if (awrs.length == 2) {
|
||||
$("#answer-duo").show()
|
||||
$("#answer-duo-button-1").text(res.data.answers[0])
|
||||
$("#answer-duo-button-2").text(res.data.answers[1])
|
||||
} else if (awrs.length == 4) {
|
||||
$("#answer-carre").show()
|
||||
$("#answer-carre-button-1").text(res.data.answers[0])
|
||||
$("#answer-carre-button-2").text(res.data.answers[1])
|
||||
$("#answer-carre-button-3").text(res.data.answers[2])
|
||||
$("#answer-carre-button-4").text(res.data.answers[3])
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
function answer1(q) {
|
||||
asw={
|
||||
index: qindex,
|
||||
step: 0,
|
||||
data: q
|
||||
}
|
||||
$.ajax({
|
||||
contentType: 'application/json',
|
||||
type: "POST",
|
||||
url: "/questions/answer/"+qid,
|
||||
data: JSON.stringify(asw),
|
||||
dataType: "json",
|
||||
success: function(res) {
|
||||
console.log(res)
|
||||
if(res["success"]){
|
||||
clearerror()
|
||||
next()
|
||||
} else {
|
||||
error(res["message"])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
function answer2(value) {
|
||||
asw={
|
||||
index: qindex,
|
||||
step: 1,
|
||||
data: value
|
||||
}
|
||||
$.ajax({
|
||||
contentType: 'application/json',
|
||||
type: "POST",
|
||||
url: "/questions/answer/"+qid,
|
||||
data: JSON.stringify(asw),
|
||||
dataType: "json",
|
||||
success: function(res) {
|
||||
console.log(res)
|
||||
if(res["success"]){
|
||||
clearerror()
|
||||
$("#answer-select").hide()
|
||||
$("#answer-duo").hide()
|
||||
$("#answer-carre").hide()
|
||||
$("#answer-cash").hide()
|
||||
if(qindex+1 >= qlength) {
|
||||
$("#question-text").text("Plus de questions !")
|
||||
} else {
|
||||
$("#question-text").text("Question suivante ...")
|
||||
next()
|
||||
}
|
||||
} else {
|
||||
isdone = false
|
||||
error(res["message"])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
$("#answer-select").hide()
|
||||
$("#answer-duo").hide()
|
||||
$("#answer-carre").hide()
|
||||
$("#answer-cash").hide()
|
||||
$("#answer-select-duo").on('click',() => answer1(2))
|
||||
$("#answer-select-carre").on('click',() => answer1(4))
|
||||
$("#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]))
|
||||
$("#answer-carre-button-2").on('click',() => answer2(awrs[1]))
|
||||
$("#answer-carre-button-3").on('click',() => answer2(awrs[2]))
|
||||
$("#answer-carre-button-4").on('click',() => answer2(awrs[3]))
|
||||
$("#question-button").on('click',next)
|
||||
</script>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,22 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr" dir="ltr">
|
||||
<head>
|
||||
<div th:replace="~{html-head}"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div th:replace="~{header}"/>
|
||||
<main>
|
||||
<h3>Formulaires terminés</h3>
|
||||
<ul>
|
||||
<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>
|
||||
</html>
|
||||
@ -1,13 +0,0 @@
|
||||
<nav>
|
||||
<div class="navbar">
|
||||
<div class="logo"><a href="/">Misael</a></div>
|
||||
<div class="welcome" sec:authorize="isAuthenticated()">Logged in as <span sec:authentication="name"/></div>
|
||||
<ul class="menu">
|
||||
<li><a href="/questions/quizz">Quizz</a></li>
|
||||
<li><a href="/questions/forms">Forms</a></li>
|
||||
<li sec:authorize="isAnonymous()"><a href="/login">Log in</a></li>
|
||||
<li sec:authorize="isAuthenticated()"><a href="/logout">Log out</div></a></li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
@ -1,6 +0,0 @@
|
||||
<meta charset="UTF-8">
|
||||
<title>Misael</title>
|
||||
<link rel="stylesheet" th:href="@{/css/style.css}"/>
|
||||
<!-- Fontawesome CDN Link -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.2/css/all.min.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
@ -1,13 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr" dir="ltr">
|
||||
<head>
|
||||
<div th:replace="~{html-head}"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div th:replace="~{header}"/>
|
||||
<main>
|
||||
Youhou ! (Y a rien ici)
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,35 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr" dir="ltr">
|
||||
<head>
|
||||
<div th:replace="~{html-head}"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div th:replace="~{header}"/>
|
||||
<main>
|
||||
|
||||
<div th:if="${param.error}">
|
||||
<div class="alert alert-danger">Invalid Email or Password</div><br/>
|
||||
</div>
|
||||
<div th:if="${param.logout}">
|
||||
<div class="alert alert-success"> You have been logged out.</div><br/>
|
||||
</div>
|
||||
<div th:if="${param.restricted}">
|
||||
<div class="alert alert-success"> Vous devez vous connecter pour vous accéder à cette page</div><br/>
|
||||
</div>
|
||||
|
||||
<form
|
||||
method="post"
|
||||
role="form"
|
||||
th:action="@{/login}">
|
||||
<label for="username">Pseudo :</label>
|
||||
<input type="text" id="username" name="username"/>
|
||||
<br/>
|
||||
<label for="password">Mot de passe :</label>
|
||||
<input type="password" id="password" name="password"/>
|
||||
<br/>
|
||||
<input id="connect" type="submit" value="Se connecter"/>
|
||||
</form>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,477 +0,0 @@
|
||||
<!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>
|
||||
<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;
|
||||
/*]]>*/
|
||||
</script>
|
||||
<script>
|
||||
function error(txt) {
|
||||
console.log(txt)
|
||||
$("#error-textbox").text(txt)
|
||||
}
|
||||
QTYPES = {
|
||||
"DCC": {
|
||||
"display" : makeAnswerBlockBCC,
|
||||
"edit": makeEditBlockBCC,
|
||||
"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",
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
success: function(res) {
|
||||
console.log(res)
|
||||
if(!res["success"]) {
|
||||
console.log(res["message"])
|
||||
error(res["message"])
|
||||
return
|
||||
}
|
||||
questions=res["questions"]
|
||||
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="q-${qid}">
|
||||
<div class="content-box" id="q-${qid}-content"/>
|
||||
<div class="content-box" id="q-${qid}-edit"/>
|
||||
<div class="button-box">
|
||||
<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 theqid = qid
|
||||
$(`#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"])
|
||||
$(`#q-${qid}-content`).replaceWith(newcontent)
|
||||
newedit = QTYPES[qu["type"]]["edit"](qid,qu["value"])
|
||||
$(`#q-${qid}-edit`).replaceWith(newedit)
|
||||
|
||||
contentMode(qid)
|
||||
}
|
||||
|
||||
function contentMode(qid) {
|
||||
$(`#q-${qid}-content`).show()
|
||||
$(`.display-button-${qid}`).show()
|
||||
$(`#q-${qid}-edit`).hide()
|
||||
$(`.edit-button-${qid}`).hide()
|
||||
$('.reorder-button').hide()
|
||||
}
|
||||
function editMode(qid) {
|
||||
$(`#q-${qid}-content`).hide()
|
||||
$(`.display-button-${qid}`).hide()
|
||||
$(`#q-${qid}-edit`).show()
|
||||
$(`.edit-button-${qid}`).show()
|
||||
$('.reorder-button').hide()
|
||||
}
|
||||
function lockEdit(qid) {
|
||||
$(`#q-${qid}-edit *`).prop('disabled', true)
|
||||
$(`.edit-button-${qid}`).prop('disabled', true)
|
||||
}
|
||||
function unlockEdit(qid) {
|
||||
$(`#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 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(qid) {
|
||||
editing.add(qid)
|
||||
editMode(qid)
|
||||
}
|
||||
|
||||
function commitEdit(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
|
||||
}
|
||||
i = qpositions[qid]
|
||||
questions[i]["value"] = newvalue
|
||||
newcontent = QTYPES[questions[i]["type"]]["display"](qid,questions[i]["value"])
|
||||
$(`#q-${qid}-content`).replaceWith(newcontent)
|
||||
|
||||
unlockEdit(qid)
|
||||
editing.delete(qid)
|
||||
contentMode(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"])
|
||||
$(`#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="q-${id}-edit">
|
||||
<h5><input id="q-${id}-edit-question"
|
||||
type="text" value="${_.escape(question["text"])}"/></h5>
|
||||
<ol>
|
||||
<li class="right">☑ <input id="q-${id}-edit-good"
|
||||
type="text" value="${_.escape(question["good"])}"/></li>
|
||||
<li class="wrong">☒ <input id="q-${id}-edit-wrong1"
|
||||
type="text" value="${_.escape(question["wrong"][0])}"/></li>
|
||||
<li class="wrong">☒ <input id="q-${id}-edit-wrong2"
|
||||
type="text" value="${_.escape(question["wrong"][1])}"/></li>
|
||||
<li class="wrong">☒ <input id="q-${id}-edit-wrong3"
|
||||
type="text" value="${_.escape(question["wrong"][2])}"/></li>
|
||||
</ol>
|
||||
</div>
|
||||
`
|
||||
out = $('<div/>').html(html).contents()
|
||||
return out
|
||||
}
|
||||
function readEditBlockBCC(id) {
|
||||
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,
|
||||
"wrong": [wrongText1,wrongText2,wrongText3]
|
||||
}
|
||||
}
|
||||
function makeAnswerBlockBCC(id,question) {
|
||||
html = `
|
||||
<div class="content-box dcc-box" id="q-${id}-content">
|
||||
<h5>${_.escape(question["text"])}</h5>
|
||||
<ol>
|
||||
<li class="right">☑ ${_.escape(question["good"])}</li>
|
||||
<li class="wrong">☒ ${_.escape(question["wrong"][0])}</li>
|
||||
<li class="wrong">☒ ${_.escape(question["wrong"][1])}</li>
|
||||
<li class="wrong">☒ ${_.escape(question["wrong"][2])}</li>
|
||||
</ol>
|
||||
</div>
|
||||
`
|
||||
out = $('<div/>').html(html).contents()
|
||||
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>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,23 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr" dir="ltr">
|
||||
<head>
|
||||
<div th:replace="~{html-head}"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div th:replace="~{header}"/>
|
||||
<main>
|
||||
<h3>Quizz à répondre</h3>
|
||||
<ul>
|
||||
<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>
|
||||
@ -1,83 +0,0 @@
|
||||
<!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>
|
||||
@ -1,81 +0,0 @@
|
||||
<!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>
|
||||
@ -1,61 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr" dir="ltr">
|
||||
<head>
|
||||
<div th:replace="~{html-head}"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div th:replace="~{header}"/>
|
||||
<main>
|
||||
|
||||
<div th:if="${param.success}">
|
||||
Utilisteurice ajouté·e avec succès !
|
||||
</div>
|
||||
<div th:if="${param.duplicate}">
|
||||
Le nom d'utilisateur·ice est déjà utilisé
|
||||
</div>
|
||||
<form
|
||||
method="post"
|
||||
role="form"
|
||||
th:action="@{/adduser}"
|
||||
th:object="${newuser}"
|
||||
>
|
||||
<label for="name">Nom</label>
|
||||
<input
|
||||
id="name"
|
||||
name="name"
|
||||
placeholder="Le pseudo"
|
||||
th:field="*{name}"
|
||||
type="text"
|
||||
/>
|
||||
<p th:errors = "*{name}" th:if="${#fields.hasErrors('name')}"></p>
|
||||
|
||||
<label for="password">Password</label>
|
||||
<input
|
||||
id="password"
|
||||
name="password"
|
||||
placeholder="Enter password"
|
||||
th:field="*{password}"
|
||||
type="password"
|
||||
/>
|
||||
<p th:errors = "*{password}" class="text-danger"
|
||||
th:if="${#fields.hasErrors('password')}"></p>
|
||||
|
||||
<button class="btn btn-primary" type="submit">Register</button>
|
||||
</form>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Pseudo</th>
|
||||
<th>Roles</th>
|
||||
</tr>
|
||||
<tr th:each="u : ${users}">
|
||||
<td th:text="${u.id}"></td>
|
||||
<td th:text="${u.pseudo}"></td>
|
||||
<td th:text="${u.roles}"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,22 +0,0 @@
|
||||
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 {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void validateDefaultQuestions() {
|
||||
for(QTypes qt : QTypes.values()){
|
||||
assert qt.validate(qt.getDefaultQuestion());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,100 +0,0 @@
|
||||
#### Dependencies and Plugin versions with their available updates.
|
||||
#### Generated by `./gradlew refreshVersions` version 0.60.5
|
||||
####
|
||||
#### Don't manually edit or split the comments that start with four hashtags (####),
|
||||
#### they will be overwritten by refreshVersions.
|
||||
####
|
||||
#### suppress inspection "SpellCheckingInspection" for whole file
|
||||
#### suppress inspection "UnusedProperty" for whole file
|
||||
|
||||
version.com.fasterxml.jackson.core..jackson-databind=2.18.3
|
||||
|
||||
version.org.junit.platform..junit-platform-launcher=1.12.0
|
||||
|
||||
version.org.springframework..spring-jdbc=6.2.3
|
||||
## # available=7.0.0-M1
|
||||
## # available=7.0.0-M2
|
||||
|
||||
version.org.springframework.boot..spring-boot-docker-compose=3.4.3
|
||||
|
||||
version.org.webjars..jquery=1.9.1
|
||||
## # available=1.10.1
|
||||
## # available=1.10.2
|
||||
## # available=1.10.2-1
|
||||
## # available=1.11.0
|
||||
## # available=1.11.0-1
|
||||
## # available=1.11.1
|
||||
## # available=1.11.2
|
||||
## # available=1.12.0
|
||||
## # available=1.12.1
|
||||
## # available=1.12.2
|
||||
## # available=1.12.3
|
||||
## # available=1.12.4
|
||||
## # available=2.0.0
|
||||
## # available=2.0.2
|
||||
## # available=2.0.3
|
||||
## # available=2.0.3-1
|
||||
## # available=2.1.0
|
||||
## # available=2.1.0-1
|
||||
## # available=2.1.0-2
|
||||
## # available=2.1.0-3
|
||||
## # available=2.1.1
|
||||
## # available=2.1.3
|
||||
## # available=2.2.0
|
||||
## # available=2.2.1
|
||||
## # available=2.2.2
|
||||
## # available=2.2.3
|
||||
## # available=2.2.4
|
||||
## # available=3.0.0-alpha1
|
||||
## # available=3.0.0-rc1
|
||||
## # available=3.0.0
|
||||
## # available=3.1.0
|
||||
## # available=3.1.1
|
||||
## # available=3.1.1-1
|
||||
## # available=3.2.0
|
||||
## # available=3.2.1
|
||||
## # available=3.3.0
|
||||
## # available=3.3.1
|
||||
## # available=3.3.1-1
|
||||
## # available=3.3.1-2
|
||||
## # available=3.4.0
|
||||
## # available=3.4.1
|
||||
## # available=3.5.0
|
||||
## # available=3.5.1
|
||||
## # available=3.6.0
|
||||
## # available=3.6.1
|
||||
## # available=3.6.2
|
||||
## # available=3.6.3
|
||||
## # available=3.6.4
|
||||
## # available=3.7.0
|
||||
## # available=3.7.1
|
||||
|
||||
version.org.thymeleaf.extras..thymeleaf-extras-springsecurity6=3.1.3.RELEASE
|
||||
|
||||
version.org.projectlombok..lombok=1.18.36
|
||||
|
||||
version.org.postgresql..postgresql=42.7.5
|
||||
|
||||
version.org.flywaydb..flyway-database-postgresql=11.3.2
|
||||
## # available=11.3.3
|
||||
## # available=11.3.4
|
||||
|
||||
version.org.flywaydb..flyway-core=11.3.2
|
||||
## # available=11.3.3
|
||||
## # available=11.3.4
|
||||
|
||||
version.jakarta.validation..jakarta.validation-api=3.1.1
|
||||
|
||||
version.com.fasterxml.jackson.datatype..jackson-datatype-hibernate5=2.18.2
|
||||
## # available=2.18.3
|
||||
|
||||
plugin.org.springframework.boot=3.4.2
|
||||
## # available=3.4.3
|
||||
|
||||
plugin.org.flywaydb.flyway=11.3.2
|
||||
## # available=11.3.3
|
||||
## # available=11.3.4
|
||||
|
||||
plugin.io.spring.dependency-management=1.1.7
|
||||
|
||||
version.org.webjars..lodash=4.17.21
|
||||
Loading…
x
Reference in New Issue
Block a user