Compare commits
1
Commits
main
..
d6cea9649a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d6cea9649a |
@@ -11,30 +11,3 @@ And finaly we run `./gradlew bootRun`
|
|||||||
The compose file is enough.
|
The compose file is enough.
|
||||||
|
|
||||||
`launcher.sh` checks that the migrations are applied and then runs the spring boot server on port 8080
|
`launcher.sh` checks that the migrations are applied and then runs the spring boot server on port 8080
|
||||||
|
|
||||||
|
|
||||||
## OIDC
|
|
||||||
|
|
||||||
In order to connect to oidc, you need to setup the following options, if `bcom` is your provider name:
|
|
||||||
```Yaml
|
|
||||||
spring:
|
|
||||||
security:
|
|
||||||
oauth2:
|
|
||||||
client:
|
|
||||||
activate: true
|
|
||||||
registration:
|
|
||||||
bcom:
|
|
||||||
provider: bcom
|
|
||||||
client-id: misael
|
|
||||||
client-secret: "myclientsecret"
|
|
||||||
authorization-grant-type: authorization_code
|
|
||||||
scope: openid,roles
|
|
||||||
provider:
|
|
||||||
bcom:
|
|
||||||
issuer-uri: "https://auth.example.com/realms/bcom"
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## API
|
|
||||||
All API requests must be done with POST requests. They will always output a json object, with a boolean
|
|
||||||
filed `success`. If `success` is false, a field `message` can give more information on the error.
|
|
||||||
@@ -36,7 +36,6 @@ dependencies {
|
|||||||
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6:_'
|
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6:_'
|
||||||
implementation Spring.boot.web
|
implementation Spring.boot.web
|
||||||
implementation 'org.springframework:spring-jdbc:_'
|
implementation 'org.springframework:spring-jdbc:_'
|
||||||
implementation Spring.boot.oauth2Client
|
|
||||||
implementation 'org.flywaydb:flyway-core:_'
|
implementation 'org.flywaydb:flyway-core:_'
|
||||||
implementation 'org.flywaydb:flyway-database-postgresql:_'
|
implementation 'org.flywaydb:flyway-database-postgresql:_'
|
||||||
implementation Spring.session.jdbc
|
implementation Spring.session.jdbc
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ services:
|
|||||||
- 'POSTGRES_DB=misael'
|
- 'POSTGRES_DB=misael'
|
||||||
- 'POSTGRES_PASSWORD=misael-dev'
|
- 'POSTGRES_PASSWORD=misael-dev'
|
||||||
- 'POSTGRES_USER=misael'
|
- 'POSTGRES_USER=misael'
|
||||||
ports:
|
|
||||||
- 5432:5432
|
|
||||||
networks:
|
networks:
|
||||||
- misanetwork
|
- misanetwork
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,6 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
misael-launcher = pkgs.writeShellScriptBin "misael" ''
|
misael-launcher = pkgs.writeShellScriptBin "misael" ''
|
||||||
set -e
|
|
||||||
echo "Checking Database Migration"
|
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
|
${pkgs.flyway}/bin/flyway "-url=$MISAEL_DATABASE" -user=misael "-password=$MISAEL_PASSWORD" -locations="filesystem:${misael}/resources/main/db/migration/" -schemas=misael migrate
|
||||||
|
|
||||||
|
|||||||
-90
@@ -2742,95 +2742,5 @@
|
|||||||
"url": "https://repo.maven.apache.org/maven2/org/yaml/snakeyaml/2.3/snakeyaml-2.3.pom",
|
"url": "https://repo.maven.apache.org/maven2/org/yaml/snakeyaml/2.3/snakeyaml-2.3.pom",
|
||||||
"hash": "sha256-D1omWgYzGwBJ41K+MsoyLeGLF/PU27cGNdQNppLjWC8="
|
"hash": "sha256-D1omWgYzGwBJ41K+MsoyLeGLF/PU27cGNdQNppLjWC8="
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"org.springframework.boot:spring-boot-starter-oauth2-client:3.4.2": {
|
|
||||||
"spring-boot-starter-oauth2-client-3.4.2.jar": {
|
|
||||||
"url": "https://repo1.maven.org/maven2/org/springframework/boot/spring-boot-starter-oauth2-client/3.4.2/spring-boot-starter-oauth2-client-3.4.2.jar",
|
|
||||||
"hash": "sha256-EwYpk9/1ifH9keeXH6N4R5sFaMSlsXeXTd0tDlq4H2E="
|
|
||||||
},
|
|
||||||
"spring-boot-starter-oauth2-client-3.4.2.pom": {
|
|
||||||
"url": "https://repo1.maven.org/maven2/org/springframework/boot/spring-boot-starter-oauth2-client/3.4.2/spring-boot-starter-oauth2-client-3.4.2.pom",
|
|
||||||
"hash": "sha256-9v9mwu4cleFfyK8R0Us7d4nVdLZfz48/+2r3zEpQTfg="
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"org.springframework.security:spring-security-oauth2-jose:6.4.2": {
|
|
||||||
"spring-security-oauth2-jose-6.4.2.jar": {
|
|
||||||
"url": "https://repo1.maven.org/maven2/org/springframework/security/spring-security-oauth2-jose/6.4.2/spring-security-oauth2-jose-6.4.2.jar",
|
|
||||||
"hash": "sha256-ycDunqvSTIzqCYcyU9A1sffhUaZhNj2UeBtE4eP/w+Q="
|
|
||||||
},
|
|
||||||
"spring-security-oauth2-jose-6.4.2.pom": {
|
|
||||||
"url": "https://repo1.maven.org/maven2/org/springframework/security/spring-security-oauth2-jose/6.4.2/spring-security-oauth2-jose-6.4.2.pom",
|
|
||||||
"hash": "sha256-StVzW45A0Y+BBLCruCru/mw7x+qAHSv4tfVvENU/hkc="
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"com.nimbusds:nimbus-jose-jwt:9.37.3": {
|
|
||||||
"nimbus-jose-jwt-9.37.3.jar": {
|
|
||||||
"url": "https://repo1.maven.org/maven2/com/nimbusds/nimbus-jose-jwt/9.37.3/nimbus-jose-jwt-9.37.3.jar",
|
|
||||||
"hash": "sha256-Eq5KOiYAldeuuirep645bouVcNuLe0CeCagkwhnMBEQ="
|
|
||||||
},
|
|
||||||
"nimbus-jose-jwt-9.37.3.pom": {
|
|
||||||
"url": "https://repo1.maven.org/maven2/com/nimbusds/nimbus-jose-jwt/9.37.3/nimbus-jose-jwt-9.37.3.pom",
|
|
||||||
"hash": "sha256-r8Y7aJ2IFDm5XzQ7Hcp1A5HtrGO4c5K+TZDRnJTMr74="
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"com.github.stephenc.jcip:jcip-annotations:1.0-1": {
|
|
||||||
"jcip-annotations-1.0-1.jar": {
|
|
||||||
"url": "https://repo1.maven.org/maven2/com/github/stephenc/jcip/jcip-annotations/1.0-1/jcip-annotations-1.0-1.jar",
|
|
||||||
"hash": "sha256-T8z/g4Kq/FiZYsTtsmL2qlleNPHhHmEFfRxqluj8cyM="
|
|
||||||
},
|
|
||||||
"jcip-annotations-1.0-1.pom": {
|
|
||||||
"url": "https://repo1.maven.org/maven2/com/github/stephenc/jcip/jcip-annotations/1.0-1/jcip-annotations-1.0-1.pom",
|
|
||||||
"hash": "sha256-aMKlqm6PNFdDCT5StbngGQuk1aUhXApZtNfTNkcgjLs="
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"org.springframework.security:spring-security-oauth2-core:6.4.2": {
|
|
||||||
"spring-security-oauth2-core-6.4.2.jar": {
|
|
||||||
"url": "https://repo1.maven.org/maven2/org/springframework/security/spring-security-oauth2-core/6.4.2/spring-security-oauth2-core-6.4.2.jar",
|
|
||||||
"hash": "sha256-V9wL4jZ30eJzty02hne3Odleg+jXMLVqD5yz0Y/8zEE="
|
|
||||||
},
|
|
||||||
"spring-security-oauth2-core-6.4.2.pom": {
|
|
||||||
"url": "https://repo1.maven.org/maven2/org/springframework/security/spring-security-oauth2-core/6.4.2/spring-security-oauth2-core-6.4.2.pom",
|
|
||||||
"hash": "sha256-sis9eUTBHB8/oo4vNNDZB/rpFEauIVZe17YVGekUCPg="
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"org.springframework.security:spring-security-oauth2-client:6.4.2": {
|
|
||||||
"spring-security-oauth2-client-6.4.2.jar": {
|
|
||||||
"url": "https://repo1.maven.org/maven2/org/springframework/security/spring-security-oauth2-client/6.4.2/spring-security-oauth2-client-6.4.2.jar",
|
|
||||||
"hash": "sha256-N++OwQJFhito8CEoTO9RNrV4N/KQ4dJXkiWe2/2LhIo="
|
|
||||||
},
|
|
||||||
"spring-security-oauth2-client-6.4.2.pom": {
|
|
||||||
"url": "https://repo1.maven.org/maven2/org/springframework/security/spring-security-oauth2-client/6.4.2/spring-security-oauth2-client-6.4.2.pom",
|
|
||||||
"hash": "sha256-CUvCd3x9PHKtFyfEfBF4AaJyTver3gmbRafWK+hQkek="
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"com.nimbusds:oauth2-oidc-sdk:9.43.4": {
|
|
||||||
"oauth2-oidc-sdk-9.43.4.jar": {
|
|
||||||
"url": "https://repo1.maven.org/maven2/com/nimbusds/oauth2-oidc-sdk/9.43.4/oauth2-oidc-sdk-9.43.4.jar",
|
|
||||||
"hash": "sha256-qq6G2BhiRNvDVm+qHw9nzjgKz3d3QwLO0g1SL8YTsK0="
|
|
||||||
},
|
|
||||||
"oauth2-oidc-sdk-9.43.4.pom": {
|
|
||||||
"url": "https://repo1.maven.org/maven2/com/nimbusds/oauth2-oidc-sdk/9.43.4/oauth2-oidc-sdk-9.43.4.pom",
|
|
||||||
"hash": "sha256-RnCDbjnZ0P3CDuGqpf8bk3WYOptPlDzxipNYaktjH1U="
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"com.nimbusds:lang-tag:1.7": {
|
|
||||||
"lang-tag-1.7.jar": {
|
|
||||||
"url": "https://repo1.maven.org/maven2/com/nimbusds/lang-tag/1.7/lang-tag-1.7.jar",
|
|
||||||
"hash": "sha256-6MHFlOJCW9vqLYYN5Vxptp/F1ZRURSRJoPCRPCpbijE="
|
|
||||||
},
|
|
||||||
"lang-tag-1.7.pom": {
|
|
||||||
"url": "https://repo1.maven.org/maven2/com/nimbusds/lang-tag/1.7/lang-tag-1.7.pom",
|
|
||||||
"hash": "sha256-gksNfeeCer/GpH5u+UsP+qE4/vOK8IxWon9dOhUiEZ4="
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"com.nimbusds:content-type:2.2": {
|
|
||||||
"content-type-2.2.jar": {
|
|
||||||
"url": "https://repo1.maven.org/maven2/com/nimbusds/content-type/2.2/content-type-2.2.jar",
|
|
||||||
"hash": "sha256-cw8YFhlhReiCdQk8FH8ubaPD5UEges01A6GwYSm5vqk="
|
|
||||||
},
|
|
||||||
"content-type:2.2.pom": {
|
|
||||||
"url": "https://repo1.maven.org/maven2/com/nimbusds/content-type/2.2/content-type-2.2.pom",
|
|
||||||
"hash": "sha256-iznCFjD7tvBIqAflfjy/c17zDaPxHnAZ5muqa5fa8Gs="
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+17
-4
@@ -1,12 +1,18 @@
|
|||||||
package com.bernard.misael.auth;
|
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.UserDetails;
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import com.bernard.misael.auth.model.User;
|
import com.bernard.misael.model.Privilege;
|
||||||
import com.bernard.misael.auth.repository.UserRepository;
|
import com.bernard.misael.model.Role;
|
||||||
|
import com.bernard.misael.model.User;
|
||||||
|
import com.bernard.misael.repository.UserRepository;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class CustomUserDetailsService implements UserDetailsService {
|
public class CustomUserDetailsService implements UserDetailsService {
|
||||||
@@ -22,9 +28,16 @@ public class CustomUserDetailsService implements UserDetailsService {
|
|||||||
User user = userRepository.findByName(pseudo);
|
User user = userRepository.findByName(pseudo);
|
||||||
|
|
||||||
if (user != null) {
|
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(),
|
return new org.springframework.security.core.userdetails.User(user.getName(),
|
||||||
user.getPassword(),
|
user.getPassword(),
|
||||||
user.getPrivileges()
|
Stream.concat(inducedPrivileges, roles).toList()
|
||||||
);
|
);
|
||||||
}else{
|
}else{
|
||||||
throw new UsernameNotFoundException("Invalid username or password.");
|
throw new UsernameNotFoundException("Invalid username or password.");
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-4
@@ -1,14 +1,13 @@
|
|||||||
package com.bernard.misael.thecrew;
|
package com.bernard.misael;
|
||||||
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.lang.NonNull;
|
|
||||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
public class TheCrewConfig implements WebMvcConfigurer {
|
public class ThecrewConfig implements WebMvcConfigurer {
|
||||||
@Override
|
@Override
|
||||||
public void addResourceHandlers(@NonNull ResourceHandlerRegistry registry) {
|
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||||
|
|
||||||
String imgFolder = System.getenv("THECREW_IMAGES_FOLDER");
|
String imgFolder = System.getenv("THECREW_IMAGES_FOLDER");
|
||||||
registry.addResourceHandler("/thecrew/**")
|
registry.addResourceHandler("/thecrew/**")
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
package com.bernard.misael.auth;
|
|
||||||
|
|
||||||
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.core.env.Environment;
|
|
||||||
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.oauth2.client.oidc.userinfo.OidcUserRequest;
|
|
||||||
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
|
|
||||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
|
|
||||||
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
|
|
||||||
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
|
||||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
|
||||||
|
|
||||||
import com.bernard.misael.auth.model.User;
|
|
||||||
import com.bernard.misael.auth.service.UserService;
|
|
||||||
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
@EnableWebSecurity
|
|
||||||
@EnableMethodSecurity(securedEnabled = true)
|
|
||||||
public class SpringSecurity {
|
|
||||||
|
|
||||||
public static final Logger LOG = Logger.getLogger(SpringSecurity.class.getName());
|
|
||||||
|
|
||||||
public static final boolean oidcActivated(Environment env) {
|
|
||||||
String activate = env.getProperty("spring.security.oauth2.client.activate");
|
|
||||||
return (activate!=null) && activate.equalsIgnoreCase("true");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public static PasswordEncoder passwordEncoder(){
|
|
||||||
return new BCryptPasswordEncoder();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public SecurityFilterChain filterChain(HttpSecurity http, Environment env, final UserService us) throws Exception {
|
|
||||||
http.csrf(csrf -> csrf.disable());
|
|
||||||
http.authorizeHttpRequests(authorize ->
|
|
||||||
authorize
|
|
||||||
.requestMatchers("/**").permitAll()
|
|
||||||
);
|
|
||||||
if(oidcActivated(env)) {
|
|
||||||
http.oauth2Login(oauth2 -> oauth2.
|
|
||||||
userInfoEndpoint(userInfo -> userInfo.
|
|
||||||
oidcUserService(this.oidcUserService(us))
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
http.formLogin(
|
|
||||||
form -> form
|
|
||||||
.loginPage("/login")
|
|
||||||
.loginProcessingUrl("/login")
|
|
||||||
.defaultSuccessUrl("/")
|
|
||||||
.permitAll()
|
|
||||||
);
|
|
||||||
http.logout(
|
|
||||||
logout -> logout
|
|
||||||
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
|
|
||||||
.permitAll()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return http.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
public void configureGlobal(AuthenticationManagerBuilder auth, UserDetailsService userDetailsService) throws Exception {
|
|
||||||
auth
|
|
||||||
.userDetailsService(userDetailsService)
|
|
||||||
.passwordEncoder(passwordEncoder());
|
|
||||||
}
|
|
||||||
|
|
||||||
private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService(UserService us) {
|
|
||||||
final OidcUserService delegate = new OidcUserService();
|
|
||||||
|
|
||||||
return userRequest -> {
|
|
||||||
// We create the original user
|
|
||||||
OidcUser oidcUser = delegate.loadUser(userRequest);
|
|
||||||
User u = us.ensureUser(oidcUser);
|
|
||||||
return new DefaultOidcUser(u.getPrivileges(), oidcUser.getIdToken());
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package com.bernard.misael.auth.model;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
|
||||||
|
|
||||||
public enum Privilege implements GrantedAuthority {
|
|
||||||
|
|
||||||
LIST_USERS,ADD_USERS,LIST_QUIZZ,CREATE_QUIZZ,VIEW_ALL_FORMS;
|
|
||||||
|
|
||||||
public static final Map<String,Privilege> oidcRoles = Map.of(
|
|
||||||
"misael-view-all-forms",VIEW_ALL_FORMS,
|
|
||||||
"misael-list-users",LIST_USERS,
|
|
||||||
"misael-create-quizz",CREATE_QUIZZ,
|
|
||||||
"misael-list-quizz", LIST_QUIZZ
|
|
||||||
);
|
|
||||||
@Override
|
|
||||||
public String getAuthority() {
|
|
||||||
return this.name();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final Privilege ofOidcString(String s) {
|
|
||||||
return oidcRoles.getOrDefault(s, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package com.bernard.misael.auth.service;
|
|
||||||
|
|
||||||
import java.security.Principal;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
|
||||||
|
|
||||||
import com.bernard.misael.auth.model.Privilege;
|
|
||||||
import com.bernard.misael.auth.model.User;
|
|
||||||
import com.bernard.misael.auth.service.dto.UserDto;
|
|
||||||
|
|
||||||
public interface UserService {
|
|
||||||
|
|
||||||
User ofPrincipal(Principal p);
|
|
||||||
|
|
||||||
User ensureUser(OidcUser user);
|
|
||||||
|
|
||||||
void saveUser(UserDto userDto);
|
|
||||||
void changePassword(User user, String password);
|
|
||||||
|
|
||||||
User findUserByName(String name);
|
|
||||||
|
|
||||||
List<UserDto> findAllUsers();
|
|
||||||
|
|
||||||
boolean hasPrivilege(User u, Privilege p);
|
|
||||||
}
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
package com.bernard.misael.auth.service;
|
|
||||||
import java.security.Principal;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
|
||||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
|
||||||
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import com.bernard.misael.auth.repository.UserRepository;
|
|
||||||
import com.bernard.misael.auth.service.dto.UserDto;
|
|
||||||
|
|
||||||
import jakarta.validation.constraints.NotNull;
|
|
||||||
|
|
||||||
import com.bernard.misael.auth.model.Privilege;
|
|
||||||
import com.bernard.misael.auth.model.User;
|
|
||||||
|
|
||||||
@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
|
|
||||||
@NotNull
|
|
||||||
public User ensureUser(OidcUser oidcUser) {
|
|
||||||
String username = oidcUser.getPreferredUsername();
|
|
||||||
String id = oidcUser.getName();
|
|
||||||
Set<Privilege> privileges;
|
|
||||||
try {
|
|
||||||
Map<String,Map<String,List<String>>> resourceAccess = oidcUser
|
|
||||||
.<Map<String,Map<String,List<String>>>>getAttribute("resource_access");
|
|
||||||
if(resourceAccess == null)
|
|
||||||
throw new RuntimeException("Oidc user has no 'resource_access'");
|
|
||||||
privileges = resourceAccess
|
|
||||||
.getOrDefault("misael",Map.of())
|
|
||||||
.getOrDefault("roles",List.of())
|
|
||||||
.stream()
|
|
||||||
.map(Privilege::ofOidcString)
|
|
||||||
.filter(p -> p != null)
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
} catch (NullPointerException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<User> u = userRepository.findByOidcId(id);
|
|
||||||
if(u.isEmpty()) {
|
|
||||||
Optional<User> uN = Optional.ofNullable(userRepository.findByName(username));
|
|
||||||
if(uN.isEmpty()) {
|
|
||||||
User u0 = new User();
|
|
||||||
u0.setOidcId(id);
|
|
||||||
u0.setName(username);
|
|
||||||
userRepository.save(u0);
|
|
||||||
u = Optional.of(u0);
|
|
||||||
} else {
|
|
||||||
User u0 = uN.get();
|
|
||||||
u0.setOidcId(id);
|
|
||||||
userRepository.save(u0);
|
|
||||||
u = uN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
User theu = u.get();
|
|
||||||
if(!theu.getPrivileges().equals(privileges)) {
|
|
||||||
theu.setPrivileges(privileges);
|
|
||||||
userRepository.save(theu);
|
|
||||||
}
|
|
||||||
return theu;
|
|
||||||
}
|
|
||||||
|
|
||||||
@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.setPrivileges(Set.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(this::mapToUserDto)
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
private UserDto mapToUserDto(User user){
|
|
||||||
UserDto userDto = new UserDto();
|
|
||||||
userDto.setName(user.getName());
|
|
||||||
return userDto;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasPrivilege(User u, Privilege p) {
|
|
||||||
return u.getPrivileges().contains(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public User ofPrincipal(Principal p) {
|
|
||||||
User u = null;
|
|
||||||
if(p instanceof OAuth2AuthenticationToken o) {
|
|
||||||
u = userRepository.findByOidcId(o.getName()).orElseThrow(() -> new RuntimeException("Oauth2 user was not in the database"));
|
|
||||||
}
|
|
||||||
if(p instanceof UsernamePasswordAuthenticationToken o) {
|
|
||||||
u = userRepository.findByName(o.getName());
|
|
||||||
}
|
|
||||||
return u;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
package com.bernard.misael.quizz.model;
|
package com.bernard.misael.model;
|
||||||
|
|
||||||
import com.bernard.misael.quizz.service.JsonNodeConverter;
|
import com.bernard.misael.service.JsonNodeConverter;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
||||||
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+4
-4
@@ -1,8 +1,8 @@
|
|||||||
package com.bernard.misael.quizz.model;
|
package com.bernard.misael.model;
|
||||||
|
|
||||||
import com.bernard.misael.quizz.questions.QTypes;
|
import com.bernard.misael.questions.QTypes;
|
||||||
import com.bernard.misael.quizz.questions.QuestionType;
|
import com.bernard.misael.questions.QuestionType;
|
||||||
import com.bernard.misael.quizz.service.JsonNodeConverter;
|
import com.bernard.misael.service.JsonNodeConverter;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
+1
-3
@@ -1,11 +1,9 @@
|
|||||||
package com.bernard.misael.quizz.model;
|
package com.bernard.misael.model;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.hibernate.annotations.ColumnDefault;
|
import org.hibernate.annotations.ColumnDefault;
|
||||||
|
|
||||||
import com.bernard.misael.auth.model.User;
|
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
+1
-3
@@ -1,6 +1,4 @@
|
|||||||
package com.bernard.misael.quizz.model;
|
package com.bernard.misael.model;
|
||||||
|
|
||||||
import com.bernard.misael.auth.model.User;
|
|
||||||
|
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.GeneratedValue;
|
import jakarta.persistence.GeneratedValue;
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+13
-17
@@ -1,20 +1,19 @@
|
|||||||
package com.bernard.misael.auth.model;
|
package com.bernard.misael.model;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import com.bernard.misael.quizz.model.Quizz;
|
import jakarta.persistence.CascadeType;
|
||||||
|
|
||||||
import jakarta.persistence.CollectionTable;
|
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.ElementCollection;
|
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.EnumType;
|
|
||||||
import jakarta.persistence.Enumerated;
|
|
||||||
import jakarta.persistence.FetchType;
|
import jakarta.persistence.FetchType;
|
||||||
import jakarta.persistence.GeneratedValue;
|
import jakarta.persistence.GeneratedValue;
|
||||||
import jakarta.persistence.GenerationType;
|
import jakarta.persistence.GenerationType;
|
||||||
import jakarta.persistence.Id;
|
import jakarta.persistence.Id;
|
||||||
import jakarta.persistence.JoinColumn;
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.JoinTable;
|
||||||
|
import jakarta.persistence.ManyToMany;
|
||||||
import jakarta.persistence.OneToMany;
|
import jakarta.persistence.OneToMany;
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
@@ -39,18 +38,15 @@ public class User
|
|||||||
@Column(nullable=false,unique = true)
|
@Column(nullable=false,unique = true)
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
@Column(nullable=true, unique = true)
|
@Column(nullable=false)
|
||||||
private String oidcId;
|
|
||||||
|
|
||||||
@Column(nullable=true)
|
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
@ElementCollection(fetch = FetchType.EAGER)
|
@ManyToMany(fetch = FetchType.EAGER, cascade=CascadeType.ALL)
|
||||||
@Enumerated(EnumType.STRING)
|
@JoinTable(
|
||||||
@CollectionTable(name = "user_privileges",
|
name="users_roles",
|
||||||
joinColumns = @JoinColumn(name = "id"))
|
joinColumns={@JoinColumn(name="USER_ID", referencedColumnName="ID")},
|
||||||
@Column(name = "privileges", nullable = false)
|
inverseJoinColumns={@JoinColumn(name="ROLE_ID", referencedColumnName="ID")})
|
||||||
private Set<Privilege> privileges;
|
private List<Role> roles = new ArrayList<>();
|
||||||
|
|
||||||
@OneToMany(mappedBy="owner")
|
@OneToMany(mappedBy="owner")
|
||||||
private Set<Quizz> myQuizzs;
|
private Set<Quizz> myQuizzs;
|
||||||
+4
-4
@@ -1,4 +1,4 @@
|
|||||||
package com.bernard.misael.quizz.questions;
|
package com.bernard.misael.questions;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -13,9 +13,9 @@ import java.util.stream.StreamSupport;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import com.bernard.misael.quizz.service.exception.MalformedAnswerException;
|
import com.bernard.misael.service.exception.MalformedAnswerException;
|
||||||
import com.bernard.misael.quizz.service.exception.MalformedClientAnswerException;
|
import com.bernard.misael.service.exception.MalformedClientAnswerException;
|
||||||
import com.bernard.misael.quizz.service.exception.QuestionTypeException;
|
import com.bernard.misael.service.exception.QuestionTypeException;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.bernard.misael.quizz. questions;
|
package com.bernard.misael.questions;
|
||||||
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
package com.bernard.misael.quizz. questions;
|
package com.bernard.misael.questions;
|
||||||
|
|
||||||
import com.bernard.misael.quizz.service.exception.QuestionTypeException;
|
import com.bernard.misael.service.exception.QuestionTypeException;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package com.bernard.misael.quizz;
|
|
||||||
|
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.lang.NonNull;
|
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
@AllArgsConstructor
|
|
||||||
@Getter
|
|
||||||
public class APIException extends RuntimeException {
|
|
||||||
|
|
||||||
private final String message;
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private final HttpStatus status;
|
|
||||||
|
|
||||||
public APIException(String message) {
|
|
||||||
this.message = message;
|
|
||||||
this.status = HttpStatus.BAD_REQUEST;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,335 +0,0 @@
|
|||||||
package com.bernard.misael.quizz;
|
|
||||||
|
|
||||||
import java.security.Principal;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import org.springframework.http.HttpMethod;
|
|
||||||
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.auth.service.UserService;
|
|
||||||
import com.bernard.misael.quizz.model.Quizz;
|
|
||||||
import com.bernard.misael.quizz.model.QuizzForm;
|
|
||||||
import com.bernard.misael.auth.AuthController;
|
|
||||||
import com.bernard.misael.auth.model.User;
|
|
||||||
import com.bernard.misael.quizz.questions.QTypes;
|
|
||||||
import com.bernard.misael.quizz.repository.QuizzFormRepository;
|
|
||||||
import com.bernard.misael.quizz.repository.QuizzRepository;
|
|
||||||
import com.bernard.misael.quizz.service.QuizzManager;
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
|
|
||||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
|
||||||
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 {
|
|
||||||
|
|
||||||
private final UserService us;
|
|
||||||
private final QuizzManager qm;
|
|
||||||
private final QuizzRepository qrepo;
|
|
||||||
private final QuizzFormRepository qfrepo;
|
|
||||||
|
|
||||||
public QuestionsController(UserService us, QuizzManager qm, QuizzRepository qrepo, QuizzFormRepository qfrepo) {
|
|
||||||
this.us = us;
|
|
||||||
this.qm = qm;
|
|
||||||
this.qrepo = qrepo;
|
|
||||||
this.qfrepo = qfrepo;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ExceptionHandler(APIException.class)
|
|
||||||
public Object apiException(HttpServletRequest request, APIException e) {
|
|
||||||
if(request.getMethod().equals(HttpMethod.GET.toString()))
|
|
||||||
return new ResponseEntity<>(e.getMessage(), e.getStatus());
|
|
||||||
else {
|
|
||||||
ObjectNode out = JsonNodeFactory.instance.objectNode();
|
|
||||||
out.set("success", JsonNodeFactory.instance.booleanNode(false));
|
|
||||||
out.set("message", JsonNodeFactory.instance.textNode(e.getMessage()));
|
|
||||||
return new ResponseEntity<>(out, e.getStatus());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* List all quizz
|
|
||||||
*/
|
|
||||||
@GetMapping("/quizz")
|
|
||||||
public String getQuizz(Model model, Principal p) {
|
|
||||||
User u = us.ofPrincipal(p);
|
|
||||||
if(u!=null) {
|
|
||||||
model.addAttribute("answerableQuizz",qm.answerableQuizz(u));
|
|
||||||
model.addAttribute("editableQuizz",qm.editableQuizz(u));
|
|
||||||
model.addAttribute("completedQuizz",qm.completedQuizz(u));
|
|
||||||
}
|
|
||||||
return "quizz.html";
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* List all forms started or finished by the user
|
|
||||||
*/
|
|
||||||
@GetMapping("/forms")
|
|
||||||
public String getForms(Model model, Principal p) {
|
|
||||||
User u = us.ofPrincipal(p);
|
|
||||||
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, HttpServletRequest request) {
|
|
||||||
User u = us.ofPrincipal(p);
|
|
||||||
QuizzForm qf = qm.getQuizzForm(u, id);
|
|
||||||
m.addAttribute("formId", qf.getId());
|
|
||||||
return "showform.html";
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Watch advancements of all forms for a specific quizz id
|
|
||||||
*/
|
|
||||||
@GetMapping("/watch/{id}")
|
|
||||||
public Object showFormsAdvancements(@PathVariable("id") long id, Model m, Principal p, HttpServletRequest request) {
|
|
||||||
User u = us.ofPrincipal(p);
|
|
||||||
Quizz q = qm.getQuizz4Watch(u, id);
|
|
||||||
m.addAttribute("quizzId", q.getId());
|
|
||||||
return "watchquizz.html";
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* API get the form
|
|
||||||
*/
|
|
||||||
@PostMapping("/getformdata/{id}")
|
|
||||||
public Object showFormApi(@PathVariable("id") long id, Principal p, HttpServletRequest request) {
|
|
||||||
User u = us.ofPrincipal(p);
|
|
||||||
if(u==null)
|
|
||||||
return AuthController.requiresLogin(request.getRequestURI());
|
|
||||||
JsonNode out = qm.getQuizzFormData(u, id);
|
|
||||||
return new ResponseEntity<>(out, HttpStatus.OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* API get the forms for a specific quizz
|
|
||||||
*/
|
|
||||||
@PostMapping("/getallformsdata/{id}")
|
|
||||||
public Object getAllFormsData(@PathVariable("id") long id, Principal p, HttpServletRequest request) {
|
|
||||||
User u = us.ofPrincipal(p);
|
|
||||||
if(u==null)
|
|
||||||
return AuthController.requiresLogin(request.getRequestURI());
|
|
||||||
JsonNode out = qm.getAllFormsData(u, id);
|
|
||||||
return new ResponseEntity<>(out, HttpStatus.OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* API get the form advancement for every form of a quizz
|
|
||||||
*/
|
|
||||||
@PostMapping("/watchdata/{id}")
|
|
||||||
public Object watchData(@PathVariable("id") long id, Principal p, HttpServletRequest request) {
|
|
||||||
User u = us.ofPrincipal(p);
|
|
||||||
if(u==null)
|
|
||||||
return AuthController.requiresLogin(request.getRequestURI());
|
|
||||||
JsonNode out = qm.getQuizzFormAdvancments(u, id);
|
|
||||||
return new ResponseEntity<>(out, HttpStatus.OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* API get the form
|
|
||||||
*/
|
|
||||||
@PostMapping("/duplicate-quizz/{id}")
|
|
||||||
public Object duplicateQuizz(@PathVariable("id") long id, Principal p, HttpServletRequest request) {
|
|
||||||
User u = us.ofPrincipal(p);
|
|
||||||
if(u==null)
|
|
||||||
return AuthController.requiresLogin(request.getRequestURI());
|
|
||||||
Quizz q = qm.duplicateQuizz(u, id);
|
|
||||||
if(q == null)
|
|
||||||
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
|
|
||||||
return new ResponseEntity<>(HttpStatus.OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* API get the form
|
|
||||||
*/
|
|
||||||
@PostMapping("/mark-complete/{id}")
|
|
||||||
public Object markComplete(@PathVariable("id") long id, Principal p, HttpServletRequest request) {
|
|
||||||
User u = us.ofPrincipal(p);
|
|
||||||
if(u==null)
|
|
||||||
return AuthController.requiresLogin(request.getRequestURI());
|
|
||||||
if(!qm.markComplete(u, id))
|
|
||||||
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
|
|
||||||
return new ResponseEntity<>(HttpStatus.OK);
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* API set the last public question
|
|
||||||
*/
|
|
||||||
@PostMapping("/set-public-question-count/{id}")
|
|
||||||
public Object setPublicQuestionCount(@PathVariable("id") long id, @RequestBody JsonNode data, Principal p, HttpServletRequest request) {
|
|
||||||
User u = us.ofPrincipal(p);
|
|
||||||
if(u==null)
|
|
||||||
return AuthController.requiresLogin(request.getRequestURI());
|
|
||||||
JsonNode pqc = data.get("publicQuestionCount");
|
|
||||||
Integer pqcI = pqc.isNull() ? null : pqc.asInt();
|
|
||||||
if(!qm.setPublicQuestionCount(u, id, pqcI))
|
|
||||||
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
|
|
||||||
return new ResponseEntity<>(HttpStatus.OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/form/{q}")
|
|
||||||
public String formpage(@PathVariable("q") long quizzId, Principal p, Model m, HttpServletRequest request) {
|
|
||||||
User u = us.ofPrincipal(p);
|
|
||||||
if (u==null)
|
|
||||||
return AuthController.requiresLogin(request.getRequestURI());
|
|
||||||
m.addAttribute("formid", quizzId);
|
|
||||||
Quizz q = qrepo.getReferenceById(quizzId);
|
|
||||||
m.addAttribute("quizzLength",q.getQuestionCount());
|
|
||||||
|
|
||||||
return "form";
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/question/{q}")
|
|
||||||
public Object question(@PathVariable("q") long quizzId, Principal p, HttpServletRequest request) {
|
|
||||||
User u = us.ofPrincipal(p);
|
|
||||||
if(u==null)
|
|
||||||
return AuthController.requiresLogin(request.getRequestURI());
|
|
||||||
JsonNode out = qm.next(u, quizzId);
|
|
||||||
return new ResponseEntity<>(out, HttpStatus.OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/answer/{q}")
|
|
||||||
public Object answer(@PathVariable("q") long quizzId, @RequestBody JsonNode data, Principal p, HttpServletRequest request) {
|
|
||||||
User u = us.ofPrincipal(p);
|
|
||||||
if(u==null)
|
|
||||||
return AuthController.requiresLogin(request.getRequestURI());
|
|
||||||
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, HttpServletRequest request) {
|
|
||||||
User u = us.ofPrincipal(p);
|
|
||||||
if (u==null)
|
|
||||||
return AuthController.requiresLogin(request.getRequestURI());
|
|
||||||
Quizz q = qm.newQuizz(u);
|
|
||||||
|
|
||||||
return "redirect:/questions/quizz-edit/"+Long.toString(q.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/quizz-edit/{q}")
|
|
||||||
public Object quizzEdit(@PathVariable("q") long quizzId, Principal p, Model m, HttpServletRequest request) {
|
|
||||||
User u = us.ofPrincipal(p);
|
|
||||||
Quizz q = qm.getQuizz4Edit(u, quizzId);
|
|
||||||
m.addAttribute("quizzId", q.getId());
|
|
||||||
return "quizz-edit";
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/quizz-edit/{q}/get")
|
|
||||||
public Object quizzSetName(@PathVariable("q") long quizzId, Principal p, HttpServletRequest request) {
|
|
||||||
User u = us.ofPrincipal(p);
|
|
||||||
if(u==null)
|
|
||||||
return AuthController.requiresLogin(request.getRequestURI());
|
|
||||||
JsonNode out = qm.getQuizzInfo(u, quizzId);
|
|
||||||
return new ResponseEntity<>(out, HttpStatus.OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/quizz-edit/{q}/set-name")
|
|
||||||
public Object quizzSetName(@PathVariable("q") long quizzId, @RequestBody String data, Principal p, HttpServletRequest request) {
|
|
||||||
User u = us.ofPrincipal(p);
|
|
||||||
if(u==null)
|
|
||||||
return AuthController.requiresLogin(request.getRequestURI());
|
|
||||||
JsonNode out = qm.setQuizzName(u, quizzId, data);
|
|
||||||
return new ResponseEntity<>(out, HttpStatus.OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/quizz-edit/{q}/add-question")
|
|
||||||
public Object quizzAddQuestion(@PathVariable("q") long quizzId, Principal p, HttpServletRequest request) {
|
|
||||||
User u = us.ofPrincipal(p);
|
|
||||||
if(u==null)
|
|
||||||
return AuthController.requiresLogin(request.getRequestURI());
|
|
||||||
JsonNode out = qm.addQuestion(u, quizzId);
|
|
||||||
return new ResponseEntity<>(out, HttpStatus.OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/quizz-edit/{q}/remove-question/{qi}")
|
|
||||||
public Object quizzSetName(@PathVariable("q") long quizzId, @PathVariable("qi") long questionId, Principal p, HttpServletRequest request) {
|
|
||||||
User u = us.ofPrincipal(p);
|
|
||||||
if(u==null)
|
|
||||||
return AuthController.requiresLogin(request.getRequestURI());
|
|
||||||
JsonNode out = qm.removeQuestion(u, quizzId, questionId);
|
|
||||||
return new ResponseEntity<>(out, HttpStatus.OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/quizz-edit/{q}/reorder-questions")
|
|
||||||
public Object quizzReorderQuestions(@PathVariable("q") long quizzId, @RequestBody JsonNode data, Principal p, HttpServletRequest request) {
|
|
||||||
User u = us.ofPrincipal(p);
|
|
||||||
if(u==null)
|
|
||||||
return AuthController.requiresLogin(request.getRequestURI());
|
|
||||||
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 Object quizzEditQuestion(@PathVariable("q") long quizzId,
|
|
||||||
@PathVariable("qi") long questionId, @RequestBody JsonNode data, Principal p,
|
|
||||||
HttpServletRequest request) {
|
|
||||||
User u = us.ofPrincipal(p);
|
|
||||||
if(u==null)
|
|
||||||
return AuthController.requiresLogin(request.getRequestURI());
|
|
||||||
|
|
||||||
JsonNode out = qm.editQuestion(u, quizzId, questionId, data);
|
|
||||||
return new ResponseEntity<>(out, HttpStatus.OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/quizz-edit/{q}/set-question-type/{qi}")
|
|
||||||
public Object quizzSetQuestionType(@PathVariable("q") long quizzId,
|
|
||||||
@PathVariable("qi") long questionId, @RequestBody JsonNode data, Principal p,
|
|
||||||
HttpServletRequest request) {
|
|
||||||
User u = us.ofPrincipal(p);
|
|
||||||
if(u==null)
|
|
||||||
return AuthController.requiresLogin(request.getRequestURI());
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
+4
-4
@@ -1,12 +1,12 @@
|
|||||||
package com.bernard.misael.quizz.repository;
|
package com.bernard.misael.repository;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
import com.bernard.misael.quizz.model.Answer;
|
import com.bernard.misael.model.Answer;
|
||||||
import com.bernard.misael.quizz.model.Question;
|
import com.bernard.misael.model.Question;
|
||||||
import com.bernard.misael.quizz.model.QuizzForm;
|
import com.bernard.misael.model.QuizzForm;
|
||||||
|
|
||||||
public interface AnswerRepository extends JpaRepository<Answer,Long> {
|
public interface AnswerRepository extends JpaRepository<Answer,Long> {
|
||||||
|
|
||||||
+3
-3
@@ -1,12 +1,12 @@
|
|||||||
package com.bernard.misael.quizz.repository;
|
package com.bernard.misael.repository;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
import com.bernard.misael.quizz.model.Question;
|
import com.bernard.misael.model.Question;
|
||||||
import com.bernard.misael.quizz.model.Quizz;
|
import com.bernard.misael.model.Quizz;
|
||||||
|
|
||||||
|
|
||||||
public interface QuestionRepository extends JpaRepository<Question,Long> {
|
public interface QuestionRepository extends JpaRepository<Question,Long> {
|
||||||
+4
-4
@@ -1,12 +1,12 @@
|
|||||||
package com.bernard.misael.quizz.repository;
|
package com.bernard.misael.repository;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
import com.bernard.misael.quizz.model.Quizz;
|
import com.bernard.misael.model.Quizz;
|
||||||
import com.bernard.misael.quizz.model.QuizzForm;
|
import com.bernard.misael.model.QuizzForm;
|
||||||
import com.bernard.misael.auth.model.User;
|
import com.bernard.misael.model.User;
|
||||||
|
|
||||||
public interface QuizzFormRepository extends JpaRepository<QuizzForm,Long> {
|
public interface QuizzFormRepository extends JpaRepository<QuizzForm,Long> {
|
||||||
|
|
||||||
+3
-3
@@ -1,4 +1,4 @@
|
|||||||
package com.bernard.misael.quizz.repository;
|
package com.bernard.misael.repository;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -6,8 +6,8 @@ import java.util.Set;
|
|||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.lang.NonNull;
|
import org.springframework.lang.NonNull;
|
||||||
|
|
||||||
import com.bernard.misael.quizz.model.Quizz;
|
import com.bernard.misael.model.Quizz;
|
||||||
import com.bernard.misael.auth.model.User;
|
import com.bernard.misael.model.User;
|
||||||
|
|
||||||
public interface QuizzRepository extends JpaRepository<Quizz,Long> {
|
public interface QuizzRepository extends JpaRepository<Quizz,Long> {
|
||||||
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
+2
-5
@@ -1,14 +1,11 @@
|
|||||||
package com.bernard.misael.auth.repository;
|
package com.bernard.misael.repository;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
import com.bernard.misael.auth.model.User;
|
import com.bernard.misael.model.User;
|
||||||
|
|
||||||
public interface UserRepository extends JpaRepository<User, Long> {
|
public interface UserRepository extends JpaRepository<User, Long> {
|
||||||
|
|
||||||
User findByName(String pseudo);
|
User findByName(String pseudo);
|
||||||
Optional<User> findByOidcId(String oidcId);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
+30
-8
@@ -1,6 +1,7 @@
|
|||||||
package com.bernard.misael.auth.service;
|
package com.bernard.misael.service;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Arrays;
|
||||||
|
import java.util.EnumSet;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -10,9 +11,11 @@ import org.springframework.lang.NonNull;
|
|||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import com.bernard.misael.auth.model.Privilege;
|
import com.bernard.misael.model.Privilege;
|
||||||
import com.bernard.misael.auth.model.User;
|
import com.bernard.misael.model.Role;
|
||||||
import com.bernard.misael.auth.repository.UserRepository;
|
import com.bernard.misael.model.User;
|
||||||
|
import com.bernard.misael.repository.RoleRepository;
|
||||||
|
import com.bernard.misael.repository.UserRepository;
|
||||||
|
|
||||||
import jakarta.transaction.Transactional;
|
import jakarta.transaction.Transactional;
|
||||||
|
|
||||||
@@ -25,6 +28,9 @@ public class AdminMaker implements
|
|||||||
@Autowired
|
@Autowired
|
||||||
private UserRepository userRepository;
|
private UserRepository userRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RoleRepository roleRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private PasswordEncoder passwordEncoder;
|
private PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
@@ -32,21 +38,37 @@ public class AdminMaker implements
|
|||||||
@Transactional
|
@Transactional
|
||||||
public void onApplicationEvent(@NonNull ContextRefreshedEvent event) {
|
public void onApplicationEvent(@NonNull ContextRefreshedEvent event) {
|
||||||
|
|
||||||
if (alreadySetup) return;
|
if (alreadySetup)
|
||||||
|
return;
|
||||||
Logger log = Logger.getLogger("AdminMaker");
|
Logger log = Logger.getLogger("AdminMaker");
|
||||||
log.info("Checking that privileges and mysaa user exist");
|
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");
|
User mysaa = userRepository.findByName("mysaa");
|
||||||
if (mysaa == null) {
|
if (mysaa == null) {
|
||||||
User user = new User();
|
User user = new User();
|
||||||
user.setName("mysaa");
|
user.setName("mysaa");
|
||||||
user.setPassword(passwordEncoder.encode("super"));
|
user.setPassword(passwordEncoder.encode("super"));
|
||||||
user.setPrivileges(Set.of(Privilege.values()));
|
user.setRoles(Arrays.asList(adminRole));
|
||||||
userRepository.save(user);
|
userRepository.save(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
alreadySetup = true;
|
alreadySetup = true;
|
||||||
log.info("Everything needed has been created");
|
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
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.bernard.misael.quizz.service;
|
package com.bernard.misael.service;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
+10
-13
@@ -1,12 +1,12 @@
|
|||||||
package com.bernard.misael.quizz.service;
|
package com.bernard.misael.service;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import com.bernard.misael.auth.model.User;
|
import com.bernard.misael.model.Quizz;
|
||||||
import com.bernard.misael.quizz.APIException;
|
import com.bernard.misael.model.QuizzForm;
|
||||||
import com.bernard.misael.quizz.model.Quizz;
|
import com.bernard.misael.model.User;
|
||||||
import com.bernard.misael.quizz.model.QuizzForm;
|
import com.bernard.misael.questions.QTypes;
|
||||||
import com.bernard.misael.quizz.questions.QTypes;
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
|
||||||
public interface QuizzManager {
|
public interface QuizzManager {
|
||||||
@@ -16,14 +16,13 @@ public interface QuizzManager {
|
|||||||
|
|
||||||
public Quizz newQuizz(User user);
|
public Quizz newQuizz(User user);
|
||||||
|
|
||||||
|
public boolean canAccessQuizz(User user, long quizzId);
|
||||||
public List<Quizz> editableQuizz(User user);
|
public List<Quizz> editableQuizz(User user);
|
||||||
public List<Quizz> completedQuizz(User user);
|
|
||||||
public List<Quizz> answerableQuizz(User user);
|
public List<Quizz> answerableQuizz(User user);
|
||||||
|
|
||||||
public Quizz getQuizz4Edit(User user, long quizzId) throws APIException;
|
public boolean canEditQuizz(User user, long quizzId);
|
||||||
public QuizzForm getQuizzForm(User user, long quizzFormId) throws APIException;
|
public Optional<QuizzForm> canViewQuizzForm(User user, long quizzFormId);
|
||||||
public Quizz getQuizz4Watch(User user, long quizzId) throws APIException;
|
public Optional<Quizz> canViewQuizzFormsOfQuizz(User user, long quizzId);
|
||||||
public Quizz getQuizz4Answer(User user, long quizzId) throws APIException;
|
|
||||||
|
|
||||||
public JsonNode getQuizzInfo(User user, long quizzId);
|
public JsonNode getQuizzInfo(User user, long quizzId);
|
||||||
public JsonNode setQuizzName(User user, long quizzId, String newName);
|
public JsonNode setQuizzName(User user, long quizzId, String newName);
|
||||||
@@ -38,6 +37,4 @@ public interface QuizzManager {
|
|||||||
public JsonNode getQuizzFormAdvancments(User user, long quizzId);
|
public JsonNode getQuizzFormAdvancments(User user, long quizzId);
|
||||||
|
|
||||||
public Quizz duplicateQuizz(User user, long quizzId);
|
public Quizz duplicateQuizz(User user, long quizzId);
|
||||||
public boolean markComplete(User user, long quizzId);
|
|
||||||
public boolean setPublicQuestionCount(User u, long quizzId, Integer publicQuestionCount);
|
|
||||||
}
|
}
|
||||||
+232
-257
@@ -1,113 +1,112 @@
|
|||||||
package com.bernard.misael.quizz.service;
|
package com.bernard.misael.service;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Random;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.springframework.http.HttpStatus;
|
import org.slf4j.Logger;
|
||||||
import org.springframework.lang.NonNull;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import com.bernard.misael.auth.model.Privilege;
|
import com.bernard.misael.model.Answer;
|
||||||
import com.bernard.misael.auth.model.User;
|
import com.bernard.misael.model.Privilege;
|
||||||
import com.bernard.misael.auth.service.UserService;
|
import com.bernard.misael.model.Question;
|
||||||
import com.bernard.misael.quizz.APIException;
|
import com.bernard.misael.model.Quizz;
|
||||||
import com.bernard.misael.quizz.model.Answer;
|
import com.bernard.misael.model.QuizzForm;
|
||||||
import com.bernard.misael.quizz.model.Question;
|
import com.bernard.misael.model.User;
|
||||||
import com.bernard.misael.quizz.model.Quizz;
|
import com.bernard.misael.questions.QTypes;
|
||||||
import com.bernard.misael.quizz.model.QuizzForm;
|
import com.bernard.misael.questions.QuestionType.AnswerResult;
|
||||||
import com.bernard.misael.quizz.questions.QTypes;
|
import com.bernard.misael.repository.*;
|
||||||
import com.bernard.misael.quizz.questions.QuestionType.AnswerResult;
|
import com.bernard.misael.service.exception.MalformedAnswerException;
|
||||||
import com.bernard.misael.quizz.repository.AnswerRepository;
|
import com.bernard.misael.service.exception.MalformedClientAnswerException;
|
||||||
import com.bernard.misael.quizz.repository.QuestionRepository;
|
import com.bernard.misael.service.exception.QuestionTypeException;
|
||||||
import com.bernard.misael.quizz.repository.QuizzFormRepository;
|
import com.bernard.misael.web.QuestionsController;
|
||||||
import com.bernard.misael.quizz.repository.QuizzRepository;
|
|
||||||
import com.bernard.misael.quizz.service.exception.MalformedAnswerException;
|
|
||||||
import com.bernard.misael.quizz.service.exception.MalformedClientAnswerException;
|
|
||||||
import com.bernard.misael.quizz.service.exception.QuestionTypeException;
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.persistence.EntityNotFoundException;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class QuizzManagerImpl implements QuizzManager {
|
public class QuizzManagerImpl implements QuizzManager {
|
||||||
|
|
||||||
private final QuizzFormRepository qfRepository;
|
@Autowired
|
||||||
private final QuizzRepository qRepository;
|
UserRepository uRepository;
|
||||||
private final QuestionRepository questionRepository;
|
|
||||||
private final AnswerRepository answerRepository;
|
@Autowired
|
||||||
private final UserService uService;
|
QuizzFormRepository qfRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
QuizzRepository qRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
QuestionRepository questionRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
AnswerRepository answerRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
UserService uService;
|
||||||
|
|
||||||
public QuizzManagerImpl(
|
|
||||||
QuizzFormRepository qfRepository,
|
|
||||||
QuizzRepository qRepository,
|
|
||||||
QuestionRepository questionRepository,
|
|
||||||
AnswerRepository answerRepository,
|
|
||||||
UserService uService
|
|
||||||
) {
|
|
||||||
this.qfRepository = qfRepository;
|
|
||||||
this.qRepository = qRepository;
|
|
||||||
this.questionRepository = questionRepository;
|
|
||||||
this.answerRepository = answerRepository;
|
|
||||||
this.uService = uService;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final APIException LOGIN_REQUIRED_EXCEPTION =
|
|
||||||
new APIException("Must be logged in to do this action", HttpStatus.UNAUTHORIZED);
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JsonNode answer(User user, long quizzId, JsonNode data) {
|
public JsonNode answer(User user, long quizzId, JsonNode data) {
|
||||||
Quizz quizz = getQuizz4Answer(user, quizzId);
|
|
||||||
if(!data.has("index") || !data.get("index").isInt())
|
if(!data.has("index") || !data.get("index").isInt())
|
||||||
throw new APIException("Request should contain the question index");
|
return errorNode("Request should contain the question index");
|
||||||
if(!data.has("step") || !data.get("step").isInt())
|
if(!data.has("step") || !data.get("step").isInt())
|
||||||
throw new APIException("Request should contain the answer step");
|
return errorNode("Request should contain the answer step");
|
||||||
if(!data.has("data"))
|
if(!data.has("data"))
|
||||||
throw new APIException("Request should contain the answer 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);
|
QuizzForm qf = qfRepository.findByUserAndQuizz(user, quizz);
|
||||||
if(qf == null)
|
if(qf == null)
|
||||||
throw new APIException("The quizzform does not exist, ask the question first", HttpStatus.NOT_FOUND);
|
return errorNode("The quizzform does not exist, ask the question first");
|
||||||
if(qf.isDone())
|
if(qf.isDone())
|
||||||
throw new APIException("You're done with the quizz, you cannot answer anymore", HttpStatus.CONFLICT);
|
return errorNode("You're done with the quizz, you cannot answer anymore");
|
||||||
int qindex = qf.getCurrentQuestion();
|
int qindex = qf.getCurrentQuestion();
|
||||||
if(qindex != data.get("index").intValue())
|
if(qindex != data.get("index").intValue())
|
||||||
throw new APIException(
|
return errorNode("You are not answering the right question (you answer question "+data.get("index").intValue()
|
||||||
"You are not answering the right question (you answer question %d, but you should answer question %d)"
|
+" where you should answer question "+qindex+")");
|
||||||
.formatted(data.get("index").intValue(), qindex), HttpStatus.CONFLICT);
|
|
||||||
if(qindex >= Optional.ofNullable(quizz.getPublicQuestionCount()).orElse(Integer.MAX_VALUE))
|
if(qindex >= Optional.ofNullable(quizz.getPublicQuestionCount()).orElse(Integer.MAX_VALUE))
|
||||||
throw new APIException("La question suivante est encore bloquée", HttpStatus.CONFLICT);
|
return errorNode("La question suivante est encore bloquée");
|
||||||
Question q = questionRepository.findByQuizzAndIndex(quizz,qindex);
|
Question q = questionRepository.findByQuizzAndIndex(quizz,qindex);
|
||||||
if(q == null)
|
if(q == null)
|
||||||
throw new APIException("Could not find question %d".formatted(qindex), HttpStatus.NOT_FOUND);
|
return errorNode("Could not find question "+qindex);
|
||||||
int step = qf.getAnswerStep();
|
int step = qf.getAnswerStep();
|
||||||
if(step != data.get("step").intValue())
|
if(step != data.get("step").intValue())
|
||||||
throw new APIException(
|
return errorNode("You are not answering the right step of the question (you answer step "+data.get("step").intValue()
|
||||||
"You are not answering the right step of the question (you answer step %d where you should be answering step %d)"
|
+" where you should step question "+step+")");
|
||||||
.formatted(data.get("step").intValue(),step));
|
|
||||||
|
|
||||||
Answer answer = answerRepository.findByFormAndQuestion(qf, q);
|
Answer answer = answerRepository.findByFormAndQuestion(qf, q);
|
||||||
if(answer == null)
|
if(answer == null)
|
||||||
throw new APIException("The database answer object does not exist, ask the question first", HttpStatus.CONFLICT);
|
return errorNode("The database answer object does not exist, ask the question first");
|
||||||
|
|
||||||
JsonNode answerData = answer.getValue();
|
JsonNode answerData = answer.getValue();
|
||||||
AnswerResult result;
|
AnswerResult result;
|
||||||
try {
|
try {
|
||||||
result = q.getQT().clientAnswers(step, answerData, data.get("data"));
|
result = q.getQT().clientAnswers(step, answerData, data.get("data"));
|
||||||
} catch (MalformedAnswerException e) {
|
} catch (MalformedAnswerException e) {
|
||||||
throw new APIException("The previous answer stored in database is invalid", HttpStatus.INTERNAL_SERVER_ERROR);
|
return errorNode("The previous answer stored in database is invalid");
|
||||||
} catch (MalformedClientAnswerException e) {
|
} catch (MalformedClientAnswerException e) {
|
||||||
throw new APIException("This answer is not valid here");
|
return errorNode("This answer is not valid here");
|
||||||
} catch (QuestionTypeException e) {
|
} catch (QuestionTypeException e) {
|
||||||
throw new APIException("Unknown error from the QuestionType", HttpStatus.INTERNAL_SERVER_ERROR);
|
return errorNode("Unknown error from the QuestionType");
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
throw new APIException("The QuestionType did not recognize the step of the question");
|
return errorNode("The QuestionType did not recognize the step of the question");
|
||||||
}
|
}
|
||||||
if(result.isNextQuestion()) {
|
if(result.isNextQuestion()) {
|
||||||
qf.setCurrentQuestion(qindex+1);
|
qf.setCurrentQuestion(qindex+1);
|
||||||
@@ -128,20 +127,27 @@ public class QuizzManagerImpl implements QuizzManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JsonNode next(User user, long quizzId) {
|
public JsonNode next(User user, long quizzId) {
|
||||||
Quizz quizz = getQuizz4Answer(user, 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);
|
QuizzForm qf = qfRepository.findByUserAndQuizz(user, quizz);
|
||||||
if(qf == null){
|
if(qf == null){
|
||||||
// We should create the quizzform
|
// We should create the quizzform
|
||||||
qf = newQuizzForm(user, quizz);
|
qf = newQuizzForm(user, quizz);
|
||||||
}
|
}
|
||||||
if(qf.isDone())
|
if(qf.isDone())
|
||||||
throw new APIException("No more questions", HttpStatus.OK);
|
return errorNode("No more questions");
|
||||||
int qindex = qf.getCurrentQuestion();
|
int qindex = qf.getCurrentQuestion();
|
||||||
if(qindex >= Optional.ofNullable(quizz.getPublicQuestionCount()).orElse(Integer.MAX_VALUE))
|
if(qindex >= Optional.ofNullable(quizz.getPublicQuestionCount()).orElse(Integer.MAX_VALUE))
|
||||||
throw new APIException("La question suivante est encore bloquée", HttpStatus.OK);
|
return errorNode("La question suivante est encore bloquée");
|
||||||
Question q = questionRepository.findByQuizzAndIndex(quizz,qindex);
|
Question q = questionRepository.findByQuizzAndIndex(quizz,qindex);
|
||||||
if(q == null)
|
if(q == null)
|
||||||
throw new APIException("Could not find question %d".formatted(qindex), HttpStatus.NOT_FOUND);
|
return errorNode("Could not find question "+qindex);
|
||||||
int step = qf.getAnswerStep();
|
int step = qf.getAnswerStep();
|
||||||
Answer answer = answerRepository.findByFormAndQuestion(qf, q);
|
Answer answer = answerRepository.findByFormAndQuestion(qf, q);
|
||||||
if(answer==null){
|
if(answer==null){
|
||||||
@@ -156,11 +162,11 @@ public class QuizzManagerImpl implements QuizzManager {
|
|||||||
try {
|
try {
|
||||||
qdata = q.getQT().clientQuestionData(step, answerData);
|
qdata = q.getQT().clientQuestionData(step, answerData);
|
||||||
} catch (MalformedAnswerException e) {
|
} catch (MalformedAnswerException e) {
|
||||||
throw new APIException("The previous answer stored in database is invalid", HttpStatus.CONFLICT);
|
return errorNode("The previous answer stored in database is invalid");
|
||||||
} catch (QuestionTypeException e) {
|
} catch (QuestionTypeException e) {
|
||||||
throw new APIException("Unknown error from the QuestionType", HttpStatus.INTERNAL_SERVER_ERROR);
|
return errorNode("Unknown error from the QuestionType");
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
throw new APIException("The QuestionType did not recognize the step of the question");
|
return errorNode("The QuestionType did not recognize the step of the question");
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectNode out = JsonNodeFactory.instance.objectNode();
|
ObjectNode out = JsonNodeFactory.instance.objectNode();
|
||||||
@@ -171,7 +177,7 @@ public class QuizzManagerImpl implements QuizzManager {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
private QuizzForm newQuizzForm(@NonNull User user, Quizz quizz) {
|
private QuizzForm newQuizzForm(User user, Quizz quizz) {
|
||||||
QuizzForm qf = new QuizzForm();
|
QuizzForm qf = new QuizzForm();
|
||||||
qf.setUser(user);
|
qf.setUser(user);
|
||||||
qf.setQuizz(quizz);
|
qf.setQuizz(quizz);
|
||||||
@@ -182,16 +188,32 @@ public class QuizzManagerImpl implements QuizzManager {
|
|||||||
return qf;
|
return qf;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Random rand = new Random();
|
|
||||||
@Override
|
@Override
|
||||||
public Quizz newQuizz(User user) {
|
public Quizz newQuizz(User user) {
|
||||||
Quizz q = new Quizz();
|
Quizz q = new Quizz();
|
||||||
q.setName("Super questions de %s (%X)".formatted(user.getName(),rand.nextInt(0xFFFFFFF)));
|
q.setName("Super questions de "+user.getName()+" ("+Integer.toHexString((int)(Math.random()*0xFFFFFFF))+")");
|
||||||
q.setOwner(user);
|
q.setOwner(user);
|
||||||
q = qRepository.save(q);
|
q = qRepository.save(q);
|
||||||
return 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
|
@Override
|
||||||
public List<Quizz> answerableQuizz(User user) {
|
public List<Quizz> answerableQuizz(User user) {
|
||||||
Set<Quizz> ownQuizz = qRepository.findByOwnerAndIsCompleteTrue(user);
|
Set<Quizz> ownQuizz = qRepository.findByOwnerAndIsCompleteTrue(user);
|
||||||
@@ -203,7 +225,7 @@ public class QuizzManagerImpl implements QuizzManager {
|
|||||||
.sorted((q1,q2) -> q1.getName().compareTo(q2.getName())),
|
.sorted((q1,q2) -> q1.getName().compareTo(q2.getName())),
|
||||||
publicQuizz.stream()
|
publicQuizz.stream()
|
||||||
.sorted((q1,q2) -> q1.getName().compareTo(q2.getName()))
|
.sorted((q1,q2) -> q1.getName().compareTo(q2.getName()))
|
||||||
).toList();
|
).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -211,141 +233,42 @@ public class QuizzManagerImpl implements QuizzManager {
|
|||||||
Set<Quizz> ownQuizz = qRepository.findByOwnerAndIsCompleteFalse(user);
|
Set<Quizz> ownQuizz = qRepository.findByOwnerAndIsCompleteFalse(user);
|
||||||
return ownQuizz.stream()
|
return ownQuizz.stream()
|
||||||
.sorted((q1,q2) -> q1.getName().compareTo(q2.getName()))
|
.sorted((q1,q2) -> q1.getName().compareTo(q2.getName()))
|
||||||
.toList();
|
.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
|
@Override
|
||||||
public List<Quizz> completedQuizz(User user) {
|
public JsonNode getQuizzInfo(User user, long quizzId) {
|
||||||
Set<Quizz> ownQuizz = qRepository.findByOwnerAndIsCompleteTrue(user);
|
Optional<JsonNode> authCheck = checkEditQuizz(user, quizzId);
|
||||||
return ownQuizz.stream()
|
if(authCheck.isPresent()) return authCheck.get();
|
||||||
.sorted((q1,q2) -> q1.getName().compareTo(q2.getName()))
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
Quizz quizz = qRepository.findById(quizzId).get();
|
||||||
* Checks that the given user can edit the quizz whose id is provided.
|
|
||||||
* If not, throws an APIException
|
|
||||||
* @param user The user that makes the request
|
|
||||||
* @param quizzId The ID of the quizz we want to get
|
|
||||||
* @return The Quizz database object
|
|
||||||
* @throws APIException If something isn't right
|
|
||||||
*/
|
|
||||||
@NotNull
|
|
||||||
public Quizz getQuizz4Edit(User user, long quizzId) throws APIException {
|
|
||||||
if (user == null)
|
|
||||||
throw LOGIN_REQUIRED_EXCEPTION;
|
|
||||||
Optional<Quizz> oquizz = qRepository.findById(quizzId);
|
|
||||||
if (!oquizz.isPresent())
|
|
||||||
throw new APIException("Could not find quizz with id %d".formatted(quizzId), HttpStatus.NOT_FOUND);
|
|
||||||
Quizz q = oquizz.get();
|
|
||||||
if (!q.getOwner().equals(user))
|
|
||||||
throw new APIException("User has no right to edit quizz (must be author)", HttpStatus.FORBIDDEN);
|
|
||||||
if (q.isComplete())
|
|
||||||
throw new APIException("Quizz is complete, cannot edit, answers might have already been cast", HttpStatus.CONFLICT);
|
|
||||||
return q;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks that the given user can answer the quizz whose id is provided.
|
|
||||||
* If not, throws an APIException
|
|
||||||
* @param user The user that makes the request
|
|
||||||
* @param quizzId The ID of the quizz we want to get
|
|
||||||
* @return The Quizz database object
|
|
||||||
* @throws APIException If something isn't right
|
|
||||||
*/
|
|
||||||
@NotNull
|
|
||||||
public Quizz getQuizz4Answer(User user, long quizzId) throws APIException {
|
|
||||||
if (user == null)
|
|
||||||
throw LOGIN_REQUIRED_EXCEPTION;
|
|
||||||
Optional<Quizz> oquizz = qRepository.findById(quizzId);
|
|
||||||
if (!oquizz.isPresent())
|
|
||||||
throw new APIException("Could not find quizz with id %d".formatted(quizzId), HttpStatus.NOT_FOUND);
|
|
||||||
Quizz q = oquizz.get();
|
|
||||||
if(!q.isComplete())
|
|
||||||
throw new APIException("Cannot answer a quizz that is not marked as complete", HttpStatus.CONFLICT);
|
|
||||||
if (!(
|
|
||||||
q.getOwner().equals(user) ||
|
|
||||||
q.getPublicQuestionCount() != null
|
|
||||||
))
|
|
||||||
throw new APIException("User has no right to answer quizz (must be author, or quizz must be public)", HttpStatus.FORBIDDEN);
|
|
||||||
return q;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the specified quizz if user has the right to access all of its forms
|
|
||||||
* @param user The requesting user
|
|
||||||
* @param quizzFormId The ID of the quizz we want to get
|
|
||||||
* @return The Quizz database object
|
|
||||||
* @throws APIException If something isn't right
|
|
||||||
*/
|
|
||||||
@NotNull
|
|
||||||
public Quizz getQuizz4Watch(User user, long quizzId) throws APIException {
|
|
||||||
if (user == null)
|
|
||||||
throw LOGIN_REQUIRED_EXCEPTION;
|
|
||||||
Optional<Quizz> oquizz = qRepository.findById(quizzId);
|
|
||||||
if (!oquizz.isPresent())
|
|
||||||
throw new APIException("Could not find quizz with id %d".formatted(quizzId), HttpStatus.NOT_FOUND);
|
|
||||||
Quizz q = oquizz.get();
|
|
||||||
if (!(q.getOwner().equals(user) || uService.hasPrivilege(user, Privilege.VIEW_ALL_FORMS)))
|
|
||||||
throw new APIException((
|
|
||||||
"User %s cannot access all forms of quizz %d." +
|
|
||||||
"Must be either author of the quizz, or have VIEW_ALL_FORMS privilege")
|
|
||||||
.formatted(user.getName(), q.getId()));
|
|
||||||
return q;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the specified quizz form if user has right to access it and if the quizz form is complete
|
|
||||||
* @param user The requesting user
|
|
||||||
* @param quizzFormId The ID of the quizz form we want to get
|
|
||||||
* @return The QuizzForm database object
|
|
||||||
* @throws APIException If something isn't right
|
|
||||||
*/
|
|
||||||
@NotNull
|
|
||||||
public QuizzForm getQuizzForm(User user, long quizzFormId) throws APIException {
|
|
||||||
if (user == null)
|
|
||||||
throw LOGIN_REQUIRED_EXCEPTION;
|
|
||||||
Optional<QuizzForm> oqf = qfRepository.findById(quizzFormId);
|
|
||||||
if(oqf.isEmpty())
|
|
||||||
throw new APIException("Could not find quizz form with id %d".formatted(quizzFormId), HttpStatus.NOT_FOUND);
|
|
||||||
QuizzForm qf = oqf.get();
|
|
||||||
if(!qf.isDone())
|
|
||||||
throw new APIException("Cannot access a quizz form that is not done");
|
|
||||||
if(!(
|
|
||||||
qf.getUser().equals(user) ||
|
|
||||||
qf.getQuizz().getOwner().equals(user) ||
|
|
||||||
uService.hasPrivilege(user, Privilege.VIEW_ALL_FORMS)
|
|
||||||
)) throw new APIException((
|
|
||||||
"User %s cannot access quizz form %d." +
|
|
||||||
"Must be either author of the form or author of the quizz, or have VIEW_ALL_FORMS privilege")
|
|
||||||
.formatted(user.getName(), qf.getId()));
|
|
||||||
return qf;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the question and checks it is associated with the given quizz
|
|
||||||
* @param quizz The given quizz to check the question against
|
|
||||||
* @param questionId The ID of the question we want to get
|
|
||||||
* @return The Question database object
|
|
||||||
* @throws APIException If something isn't right
|
|
||||||
*/
|
|
||||||
@NotNull
|
|
||||||
private Question getQuestion(Quizz quizz, long questionId) throws APIException{
|
|
||||||
Optional<Question> oq = questionRepository.findById(questionId);
|
|
||||||
if(oq.isEmpty())
|
|
||||||
throw new APIException("Could not find question with id %d".formatted(questionId));
|
|
||||||
Question q = oq.get();
|
|
||||||
if(!q.getQuizz().equals(quizz))
|
|
||||||
throw new APIException("Question is not associated with the right quizzId (got %d, expected %d)"
|
|
||||||
.formatted(q.getQuizz().getId(), quizz.getId()));
|
|
||||||
return q;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JsonNode getQuizzInfo(User user, long quizzId) throws APIException{
|
|
||||||
if(user == null)
|
|
||||||
throw LOGIN_REQUIRED_EXCEPTION;
|
|
||||||
Quizz quizz = getQuizz4Edit(user, quizzId);
|
|
||||||
|
|
||||||
ArrayNode n = JsonNodeFactory.instance.arrayNode(quizz.getQuestionCount());
|
ArrayNode n = JsonNodeFactory.instance.arrayNode(quizz.getQuestionCount());
|
||||||
for(int i = 0;i<quizz.getQuestionCount();i++)n.add(JsonNodeFactory.instance.nullNode());
|
for(int i = 0;i<quizz.getQuestionCount();i++)n.add(JsonNodeFactory.instance.nullNode());
|
||||||
@@ -366,11 +289,13 @@ public class QuizzManagerImpl implements QuizzManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JsonNode setQuizzName(User user, long quizzId, String newName) {
|
public JsonNode setQuizzName(User user, long quizzId, String newName) {
|
||||||
|
Optional<JsonNode> authCheck = checkEditQuizz(user, quizzId);
|
||||||
|
if(authCheck.isPresent()) return authCheck.get();
|
||||||
|
|
||||||
Quizz quizz = getQuizz4Edit(user, quizzId);
|
if(newName.isBlank() | newName.length()>255)
|
||||||
|
return errorNode("Le nom est invalide");
|
||||||
|
|
||||||
if(newName.isBlank() || newName.length()>255)
|
Quizz quizz = qRepository.findById(quizzId).get();
|
||||||
throw new APIException("Le nom est invalide");
|
|
||||||
|
|
||||||
quizz.setName(newName);
|
quizz.setName(newName);
|
||||||
ObjectNode out = JsonNodeFactory.instance.objectNode();
|
ObjectNode out = JsonNodeFactory.instance.objectNode();
|
||||||
@@ -381,8 +306,11 @@ public class QuizzManagerImpl implements QuizzManager {
|
|||||||
public static final QTypes DEFAULT_QTYPE = QTypes.DCC;
|
public static final QTypes DEFAULT_QTYPE = QTypes.DCC;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JsonNode addQuestion(User user, long quizzId) throws APIException {
|
public JsonNode addQuestion(User user, long quizzId) {
|
||||||
Quizz quizz = getQuizz4Edit(user, quizzId);
|
Optional<JsonNode> authCheck = checkEditQuizz(user, quizzId);
|
||||||
|
if(authCheck.isPresent()) return authCheck.get();
|
||||||
|
|
||||||
|
Quizz quizz = qRepository.findById(quizzId).get();
|
||||||
|
|
||||||
Question q = new Question();
|
Question q = new Question();
|
||||||
q.setType(DEFAULT_QTYPE);
|
q.setType(DEFAULT_QTYPE);
|
||||||
@@ -402,9 +330,18 @@ public class QuizzManagerImpl implements QuizzManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JsonNode removeQuestion(User user, long quizzId, long questionId) throws APIException {
|
public JsonNode removeQuestion(User user, long quizzId, long questionId) {
|
||||||
Quizz quizz = getQuizz4Edit(user, quizzId);
|
Optional<JsonNode> authCheck = checkEditQuizz(user, quizzId);
|
||||||
Question q = getQuestion(quizz, questionId);
|
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 -> {
|
questionRepository.findByQuizz(quizz).forEach(qq -> {
|
||||||
if(qq.getIndex()>q.getIndex())
|
if(qq.getIndex()>q.getIndex())
|
||||||
@@ -419,21 +356,30 @@ public class QuizzManagerImpl implements QuizzManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JsonNode reorderQuestions(User user, long quizzId, List<Long> newOrder) throws APIException {
|
public JsonNode reorderQuestions(User user, long quizzId, List<Long> newOrder) {
|
||||||
Quizz quizz = getQuizz4Edit(user, quizzId);
|
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
|
// We need that the base set of neworder is the list of ids
|
||||||
// 1) The length is right
|
// 1) The length is right
|
||||||
if(quizz.getQuestionCount() != newOrder.size())
|
if(quizz.getQuestionCount() != newOrder.size())
|
||||||
throw new APIException("You must put every question in order");
|
return errorNode("You must put every question in order");
|
||||||
// 2) There is no duplicates
|
// 2) There is no duplicates
|
||||||
if(new HashSet<Long>(newOrder).size() != newOrder.size())
|
if(new HashSet<Long>(newOrder).size() != newOrder.size())
|
||||||
throw new APIException("You shouldn't put duplicates in the new order");
|
return errorNode("You shouldn't put duplicates in the new order");
|
||||||
// 3) All ids correspond to an actual question of the right quizz
|
// 3) All ids correspond to an actual question of the right quizz
|
||||||
List<Question> questions = new ArrayList<>(newOrder.size());
|
List<Question> questions = new ArrayList<>(newOrder.size());
|
||||||
for(int i = 0;i<newOrder.size();i++){
|
for(int i = 0;i<newOrder.size();i++){
|
||||||
Question q = getQuestion(quizz, newOrder.get(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);
|
questions.add(q);
|
||||||
|
} catch (EntityNotFoundException e){
|
||||||
|
return errorNode("Could not find question with id "+newOrder.get(i));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// All Checks passed
|
// All Checks passed
|
||||||
@@ -448,11 +394,23 @@ public class QuizzManagerImpl implements QuizzManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JsonNode editQuestion(User user, long quizzId, long questionId, JsonNode value) {
|
public JsonNode editQuestion(User user, long quizzId, long questionId, JsonNode value) {
|
||||||
Quizz quizz = getQuizz4Edit(user, quizzId);
|
Optional<JsonNode> authCheck = checkEditQuizz(user, quizzId);
|
||||||
Question q = getQuestion(quizz, questionId);
|
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))
|
if(!q.getType().validate(value))
|
||||||
throw new APIException("Invalid question value");
|
return errorNode("Invalid question value");
|
||||||
q.setValue(value);
|
q.setValue(value);
|
||||||
|
|
||||||
ObjectNode out = JsonNodeFactory.instance.objectNode();
|
ObjectNode out = JsonNodeFactory.instance.objectNode();
|
||||||
@@ -462,8 +420,20 @@ public class QuizzManagerImpl implements QuizzManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JsonNode setQuestionType(User user, long quizzId, long questionId, QTypes type) {
|
public JsonNode setQuestionType(User user, long quizzId, long questionId, QTypes type) {
|
||||||
Quizz quizz = getQuizz4Edit(user, quizzId);
|
Optional<JsonNode> authCheck = checkEditQuizz(user, quizzId);
|
||||||
Question q = getQuestion(quizz, questionId);
|
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)
|
// If type is the same, we don't change (and dont reset the value)
|
||||||
JsonNode n;
|
JsonNode n;
|
||||||
@@ -482,9 +452,33 @@ public class QuizzManagerImpl implements QuizzManager {
|
|||||||
return out;
|
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
|
@Override
|
||||||
public JsonNode getQuizzFormData(User user, long quizzFormId) {
|
public JsonNode getQuizzFormData(User user, long quizzFormId) {
|
||||||
QuizzForm form = getQuizzForm(user, 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<Question> questions = questionRepository.findByQuizzOrderByIndexAsc(form.getQuizz());
|
||||||
List<Answer> answers = answerRepository.findByFormAndQuestionIn(form, questions);
|
List<Answer> answers = answerRepository.findByFormAndQuestionIn(form, questions);
|
||||||
assert questions.size() == answers.size();
|
assert questions.size() == answers.size();
|
||||||
@@ -506,14 +500,16 @@ public class QuizzManagerImpl implements QuizzManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JsonNode getAllFormsData(User u, long quizzId) {
|
public JsonNode getAllFormsData(User u, long quizzId) {
|
||||||
Quizz quizz = getQuizz4Watch(u, 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();
|
ObjectNode out = JsonNodeFactory.instance.objectNode();
|
||||||
out.set("id",JsonNodeFactory.instance.numberNode(quizz.getId()));
|
out.set("id",JsonNodeFactory.instance.numberNode(quizz.getId()));
|
||||||
out.set("name",JsonNodeFactory.instance.textNode(quizz.getName()));
|
out.set("name",JsonNodeFactory.instance.textNode(quizz.getName()));
|
||||||
|
|
||||||
ArrayNode questionNode = JsonNodeFactory.instance.arrayNode(quizz.getQuestionCount());
|
ArrayNode questionNode = JsonNodeFactory.instance.arrayNode(quizz.getQuestionCount());
|
||||||
List<Question> questions = quizz.getQuestions().stream().sorted((p,q) -> Integer.compare(p.getIndex(),q.getIndex())).toList();
|
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++) {
|
for(int i=0;i<questions.size();i++) {
|
||||||
ObjectNode qNode = JsonNodeFactory.instance.objectNode();
|
ObjectNode qNode = JsonNodeFactory.instance.objectNode();
|
||||||
qNode.set("id", JsonNodeFactory.instance.numberNode(questions.get(i).getId()));
|
qNode.set("id", JsonNodeFactory.instance.numberNode(questions.get(i).getId()));
|
||||||
@@ -532,7 +528,6 @@ public class QuizzManagerImpl implements QuizzManager {
|
|||||||
qfNode.set("done", JsonNodeFactory.instance.booleanNode(qf.isDone()));
|
qfNode.set("done", JsonNodeFactory.instance.booleanNode(qf.isDone()));
|
||||||
|
|
||||||
List<Answer> answers = answerRepository.findByFormAndQuestionIn(qf, questions);
|
List<Answer> answers = answerRepository.findByFormAndQuestionIn(qf, questions);
|
||||||
answers.sort((a1,a2) -> Integer.compare(a1.getQuestion().getIndex(),a2.getQuestion().getIndex()));
|
|
||||||
ArrayNode answersNode = JsonNodeFactory.instance.arrayNode();
|
ArrayNode answersNode = JsonNodeFactory.instance.arrayNode();
|
||||||
for(int i=0;i<answers.size();i++) {
|
for(int i=0;i<answers.size();i++) {
|
||||||
answersNode.add(answers.get(i).getValue());
|
answersNode.add(answers.get(i).getValue());
|
||||||
@@ -548,7 +543,10 @@ public class QuizzManagerImpl implements QuizzManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JsonNode getQuizzFormAdvancments(User user, long quizzId) {
|
public JsonNode getQuizzFormAdvancments(User user, long quizzId) {
|
||||||
Quizz quizz = getQuizz4Watch(user, 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);
|
List<QuizzForm> quizzForms = qfRepository.findByQuizz(quizz);
|
||||||
quizzForms.sort((qfa,qfb) -> qfa.getUser().getName().compareTo(qfb.getUser().getName()));
|
quizzForms.sort((qfa,qfb) -> qfa.getUser().getName().compareTo(qfb.getUser().getName()));
|
||||||
|
|
||||||
@@ -564,20 +562,21 @@ public class QuizzManagerImpl implements QuizzManager {
|
|||||||
}
|
}
|
||||||
ObjectNode out = JsonNodeFactory.instance.objectNode();
|
ObjectNode out = JsonNodeFactory.instance.objectNode();
|
||||||
out.set("success", JsonNodeFactory.instance.booleanNode(true));
|
out.set("success", JsonNodeFactory.instance.booleanNode(true));
|
||||||
out.set("publicQuestionCount", quizz.getPublicQuestionCount()==null?
|
|
||||||
JsonNodeFactory.instance.nullNode():
|
|
||||||
JsonNodeFactory.instance.numberNode(quizz.getPublicQuestionCount()));
|
|
||||||
out.set("questionCount", JsonNodeFactory.instance.numberNode(quizz.getQuestionCount()));
|
|
||||||
out.set("data", formsNode);
|
out.set("data", formsNode);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Quizz duplicateQuizz(User u, long quizzId) {
|
public Quizz duplicateQuizz(User u, long quizzId) {
|
||||||
Quizz q = getQuizz4Edit(u, 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();
|
Quizz nq = new Quizz();
|
||||||
nq.setName(q.getName() + "("+ Integer.toHexString(rand.nextInt(0xFFFFFFF)) +")");
|
nq.setName(q.getName() + "("+ Integer.toHexString((int)(Math.random()*0xFFFFFFF)) +")");
|
||||||
nq.setOwner(q.getOwner());
|
nq.setOwner(q.getOwner());
|
||||||
nq.setQuestionCount(q.getQuestionCount());
|
nq.setQuestionCount(q.getQuestionCount());
|
||||||
nq.setPublicQuestionCount(q.getPublicQuestionCount());
|
nq.setPublicQuestionCount(q.getPublicQuestionCount());
|
||||||
@@ -597,30 +596,6 @@ public class QuizzManagerImpl implements QuizzManager {
|
|||||||
return nq;
|
return nq;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean markComplete(User u, long quizzId) {
|
|
||||||
Quizz q = getQuizz4Edit(u, quizzId);
|
|
||||||
|
|
||||||
q.setPublicQuestionCount(null);
|
|
||||||
q.setComplete(true);
|
|
||||||
qRepository.save(q);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean setPublicQuestionCount(User u, long quizzId, Integer publicQuestionCount) {
|
|
||||||
Quizz q = getQuizz4Watch(u, quizzId);
|
|
||||||
if(!q.isComplete())
|
|
||||||
return false;
|
|
||||||
if(publicQuestionCount != null && (publicQuestionCount > q.getQuestionCount() || publicQuestionCount < 0))
|
|
||||||
return false;
|
|
||||||
q.setPublicQuestionCount(publicQuestionCount);
|
|
||||||
qRepository.save(q);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
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
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.bernard.misael.auth.service.dto;
|
package com.bernard.misael.service.dto;
|
||||||
|
|
||||||
import jakarta.validation.constraints.NotEmpty;
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.bernard.misael.quizz.service.exception;
|
package com.bernard.misael.service.exception;
|
||||||
|
|
||||||
public class MalformedAnswerException extends QuestionTypeException{
|
public class MalformedAnswerException extends QuestionTypeException{
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.bernard.misael.quizz.service.exception;
|
package com.bernard.misael.service.exception;
|
||||||
|
|
||||||
public class MalformedClientAnswerException extends QuestionTypeException {
|
public class MalformedClientAnswerException extends QuestionTypeException {
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.bernard.misael.quizz.service.exception;
|
package com.bernard.misael.service.exception;
|
||||||
|
|
||||||
public abstract class QuestionTypeException extends Exception{
|
public abstract class QuestionTypeException extends Exception{
|
||||||
|
|
||||||
+13
-32
@@ -1,9 +1,10 @@
|
|||||||
package com.bernard.misael.auth;
|
package com.bernard.misael.web;
|
||||||
|
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.security.access.annotation.Secured;
|
import org.springframework.security.access.annotation.Secured;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
@@ -13,11 +14,11 @@ import org.springframework.web.bind.annotation.ModelAttribute;
|
|||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
|
||||||
import com.bernard.misael.auth.repository.UserRepository;
|
import com.bernard.misael.model.Role;
|
||||||
import com.bernard.misael.auth.service.UserService;
|
import com.bernard.misael.model.User;
|
||||||
import com.bernard.misael.auth.model.Privilege;
|
import com.bernard.misael.repository.UserRepository;
|
||||||
import com.bernard.misael.auth.model.User;
|
import com.bernard.misael.service.UserService;
|
||||||
import com.bernard.misael.auth.service.dto.UserDto;
|
import com.bernard.misael.service.dto.UserDto;
|
||||||
|
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
@@ -25,12 +26,13 @@ import lombok.Getter;
|
|||||||
@Controller
|
@Controller
|
||||||
public class AuthController {
|
public class AuthController {
|
||||||
|
|
||||||
private final UserService userService;
|
@Autowired
|
||||||
private final UserRepository urepo;
|
private UserService userService;
|
||||||
|
@Autowired
|
||||||
|
private UserRepository urepo;
|
||||||
|
|
||||||
public AuthController(UserService userService, UserRepository urepo) {
|
public AuthController(UserService userService) {
|
||||||
this.userService = userService;
|
this.userService = userService;
|
||||||
this.urepo = urepo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/login")
|
@GetMapping("/login")
|
||||||
@@ -92,40 +94,19 @@ public class AuthController {
|
|||||||
return "redirect:/change-password?success";
|
return "redirect:/change-password?success";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String requiresLogin(String redirect) {
|
|
||||||
//TODO Make it so it really redirects after login
|
|
||||||
return "redirect:/login?restricted";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public static class UserInfo implements Comparable<UserInfo> {
|
public static class UserInfo implements Comparable<UserInfo> {
|
||||||
private long id;
|
private long id;
|
||||||
private String oidcId;
|
|
||||||
private String pseudo;
|
private String pseudo;
|
||||||
private String roles;
|
private String roles;
|
||||||
|
|
||||||
public UserInfo(User u){
|
public UserInfo(User u){
|
||||||
this.id = u.getId();
|
this.id = u.getId();
|
||||||
this.oidcId = u.getOidcId();
|
|
||||||
this.pseudo = u.getName();
|
this.pseudo = u.getName();
|
||||||
this.roles = u.getPrivileges().stream().map(Privilege::name).collect(Collectors.joining(";"));
|
this.roles = u.getRoles().stream().map(Role::getName).collect(Collectors.joining(";"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(UserInfo other) {
|
public int compareTo(UserInfo other) {
|
||||||
return this.pseudo.compareTo(other.pseudo);
|
return this.pseudo.compareTo(other.pseudo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if(obj instanceof UserInfo other)
|
|
||||||
return this.pseudo.equals(other.pseudo);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return this.pseudo.hashCode();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,344 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+3
-2
@@ -1,9 +1,10 @@
|
|||||||
package com.bernard.misael.thecrew;
|
package com.bernard.misael.web;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
import java.io.FileReader;
|
import java.io.FileReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import org.springframework.core.io.InputStreamResource;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
create table user_privileges (id bigint not null, privileges varchar(255) not null check (privileges in ('LIST_USERS','ADD_USERS','LIST_QUIZZ','CREATE_QUIZZ','VIEW_ALL_FORMS')), primary key (id, privileges));
|
|
||||||
drop table role_privileges cascade;
|
|
||||||
drop table roles cascade;
|
|
||||||
alter table if exists users alter column password drop not null;
|
|
||||||
alter table if exists users add column oidc_id varchar(255);
|
|
||||||
alter table if exists users drop constraint if exists UKs3fyv3dn1pq639igamsrq5vhj;
|
|
||||||
alter table if exists users add constraint UKs3fyv3dn1pq639igamsrq5vhj unique (oidc_id);
|
|
||||||
alter table if exists user_privileges add constraint FKqltalsck5q21fw7vcpsriacst foreign key (id) references users;
|
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
function next() {
|
function next() {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "/questions/question/"+qid,
|
url: "/questions/question/"+qid,
|
||||||
type: "POST",
|
type: "GET",
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
success: function(res) {
|
success: function(res) {
|
||||||
console.log(res)
|
console.log(res)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<nav>
|
<nav>
|
||||||
<div class="navbar">
|
<div class="navbar">
|
||||||
<div class="logo"><a href="/">Misael</a></div>
|
<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">
|
<ul class="menu">
|
||||||
<li><a href="/questions/quizz">Quizz</a></li>
|
<li><a href="/questions/quizz">Quizz</a></li>
|
||||||
<li><a href="/questions/forms">Forms</a></li>
|
<li><a href="/questions/forms">Forms</a></li>
|
||||||
|
|||||||
@@ -28,10 +28,6 @@
|
|||||||
<ol id="questions-list">
|
<ol id="questions-list">
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<div class="buttonbar">
|
|
||||||
<button id="mark-complete">Marquer le quizz comme terminé</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<span id="error-textbox" style="color: red"></span>
|
<span id="error-textbox" style="color: red"></span>
|
||||||
|
|
||||||
<script th:inline="javascript">
|
<script th:inline="javascript">
|
||||||
@@ -417,19 +413,6 @@
|
|||||||
unReorderMode()
|
unReorderMode()
|
||||||
}
|
}
|
||||||
|
|
||||||
function markComplete() {
|
|
||||||
if (confirm("Voulez-vous vraiment marquer ce quizz comme terminé ? Vous ne pourrez plus l'éditer !")) {
|
|
||||||
$.ajax({
|
|
||||||
url: "/questions/mark-complete/"+quizzid,
|
|
||||||
type: "POST",
|
|
||||||
success: function(res) {
|
|
||||||
console.log("SUPER")
|
|
||||||
//TODO redirect to the right page
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function makeEditBlockBCC(id,question) {
|
function makeEditBlockBCC(id,question) {
|
||||||
html = `
|
html = `
|
||||||
@@ -487,7 +470,6 @@
|
|||||||
$("#edit-question-name-confirm").on("click",commitEditName)
|
$("#edit-question-name-confirm").on("click",commitEditName)
|
||||||
$("#edit-question-name-cancel").on("click",cancelEditName)
|
$("#edit-question-name-cancel").on("click",cancelEditName)
|
||||||
$("#add-question").on("click",addQuestion)
|
$("#add-question").on("click",addQuestion)
|
||||||
$("#mark-complete").on("click",markComplete)
|
|
||||||
getdata()
|
getdata()
|
||||||
</script>
|
</script>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -7,23 +7,17 @@
|
|||||||
<body>
|
<body>
|
||||||
<div th:replace="~{header}"/>
|
<div th:replace="~{header}"/>
|
||||||
<main>
|
<main>
|
||||||
<h3>Quizz disponibles</h3>
|
<h3>Quizz à répondre</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li th:if="${#lists.isEmpty(answerableQuizz)}">Aucun quizz de disponible malheureusement :(</li>
|
<li th:if="${#lists.isEmpty(answerableQuizz)}">Aucun quizz de disponible malheureusement :(</li>
|
||||||
<li th:each="q : ${answerableQuizz}"><a th:href="@{/questions/form/{id}(id=${q.id})}">Quizz <span th:text="${q.name}"/></a></li>
|
<li th:each="q : ${answerableQuizz}"><a th:href="@{/questions/form/{id}(id=${q.id})}">Quizz <span th:text="${q.name}"/></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<h3>Quizz en édition</h3>
|
<h3>Quizz à éditer</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li th:if="${#lists.isEmpty(editableQuizz)}">Aucun quizz de disponible malheureusement :(</li>
|
<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>
|
<li th:each="q : ${editableQuizz}"><a th:href="@{/questions/quizz-edit/{id}(id=${q.id})}">Quizz <span th:text="${q.name}"/></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<a sec:authorize="hasAuthority('CREATE_QUIZZ')" th:href="@{/questions/new-quizz}">Nouveau Quizz</a>
|
<a sec:authorize="hasAuthority('CREATE_QUIZZ')" th:href="@{/questions/new-quizz}">Nouveau Quizz</a>
|
||||||
|
|
||||||
<h3>Quizz terminés</h3>
|
|
||||||
<ul>
|
|
||||||
<li th:if="${#lists.isEmpty(completedQuizz)}">Aucun quizz de disponible malheureusement :(</li>
|
|
||||||
<li th:each="q : ${completedQuizz}"><a th:href="@{/questions/watch/{id}(id=${q.id})}">Quizz <span th:text="${q.name}"/></a></li>
|
|
||||||
</ul>
|
|
||||||
</main>
|
</main>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="fr" dir="ltr">
|
||||||
|
<head>
|
||||||
|
<div th:replace="~{html-head}"/>
|
||||||
|
<link rel="stylesheet" th:href="@{/css/showformadvancements.css}"/>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div th:replace="~{header}"/>
|
||||||
|
<script th:src="@{/webjars/jquery/1.9.1/jquery.min.js}" type="text/javascript"></script>
|
||||||
|
<script th:src="@{/webjars/lodash/4.17.21/lodash.js}" type="text/javascript"></script>
|
||||||
|
<main>
|
||||||
|
|
||||||
|
<button id="refresh-data">Refresh</button>
|
||||||
|
<table id="forms-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>User</th>
|
||||||
|
<th>Position</th>
|
||||||
|
<th>Step</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="forms-body">
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<span id="error-textbox" style="color: red"></span>
|
||||||
|
|
||||||
|
<script th:inline="javascript">
|
||||||
|
/*<![CDATA[*/
|
||||||
|
var quizzid = /*[[${quizzId}]]*/ -1;
|
||||||
|
/*]]>*/
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
function error(txt) {
|
||||||
|
console.log(txt)
|
||||||
|
$("#error-textbox").text(txt)
|
||||||
|
}
|
||||||
|
|
||||||
|
formsdata=[]
|
||||||
|
|
||||||
|
function getdata() {
|
||||||
|
$('#refresh-data').prop('disabled',true)
|
||||||
|
$.ajax({
|
||||||
|
url: "/questions/getformadvancements/"+quizzid,
|
||||||
|
type: "POST",
|
||||||
|
dataType: "json",
|
||||||
|
success: function(res) {
|
||||||
|
console.log(res)
|
||||||
|
if(!res["success"]) {
|
||||||
|
console.log(res["message"])
|
||||||
|
error(res["message"])
|
||||||
|
$('#refresh-data').prop('disabled',false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
formsdata=res["data"]
|
||||||
|
|
||||||
|
$('.form-line').remove()
|
||||||
|
for(let qf of formsdata) {
|
||||||
|
html = `
|
||||||
|
<tr class="form-line done-${qf["done"]}" id="q-${qf["qfid"]}">
|
||||||
|
<td>${qf["username"]}</td>
|
||||||
|
<td>${qf["position"]}</td>
|
||||||
|
<td>${qf["step"]}</td>
|
||||||
|
</tr>
|
||||||
|
`
|
||||||
|
content = $('<li/>').html(html).contents()
|
||||||
|
$(`#forms-body`).append(content)
|
||||||
|
}
|
||||||
|
$('#refresh-data').prop('disabled',false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#refresh-data').on('click',getdata)
|
||||||
|
getdata()
|
||||||
|
</script>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,175 +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>
|
|
||||||
|
|
||||||
<div class="button-bar">
|
|
||||||
<button id="make-public">Rendre public</button>
|
|
||||||
<button id="make-private">Rendre privé</button>
|
|
||||||
<span id="question-count-number">Nombre de questions accessibles:</span>
|
|
||||||
<button id="set-question-begin"><<</button>
|
|
||||||
<button id="set-question-decrement"><</button>
|
|
||||||
<span id="last-question-number">x</span>
|
|
||||||
<button id="set-question-increment">></button>
|
|
||||||
<button id="set-question-last">>></button>
|
|
||||||
</div>
|
|
||||||
<button id="refresh-data">Refresh</button>
|
|
||||||
<table id="forms-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>User</th>
|
|
||||||
<th>Nombre de questions répondues</th>
|
|
||||||
<th>Étape</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 = []
|
|
||||||
pqc = null
|
|
||||||
qCount = null
|
|
||||||
|
|
||||||
allButtons = [
|
|
||||||
$('#refresh-data'),
|
|
||||||
$('#make-public'),
|
|
||||||
$('#make-private'),
|
|
||||||
$('#set-question-begin'),
|
|
||||||
$('#set-question-decrement'),
|
|
||||||
$('#question-count-number'),
|
|
||||||
$('#last-question-number'),
|
|
||||||
$('#set-question-increment'),
|
|
||||||
$('#set-question-last')
|
|
||||||
]
|
|
||||||
|
|
||||||
function activateButtons() {
|
|
||||||
if(pqc === null) {
|
|
||||||
$('#make-public').show()
|
|
||||||
$('#make-private').hide()
|
|
||||||
$('#set-question-begin').hide()
|
|
||||||
$('#set-question-decrement').hide()
|
|
||||||
$('#question-count-number').hide()
|
|
||||||
$('#last-question-number').hide()
|
|
||||||
$('#set-question-increment').hide()
|
|
||||||
$('#set-question-last').hide()
|
|
||||||
} else {
|
|
||||||
$('#make-public').hide()
|
|
||||||
$('#make-private').show()
|
|
||||||
$('#question-count-number').show()
|
|
||||||
$('#set-question-begin').show()
|
|
||||||
$('#set-question-decrement').show()
|
|
||||||
if(pqc == 0) {
|
|
||||||
$('#set-question-begin').prop('disabled', true)
|
|
||||||
$('#set-question-decrement').prop('disabled', true)
|
|
||||||
} else {
|
|
||||||
$('#set-question-begin').prop('disabled', false)
|
|
||||||
$('#set-question-decrement').prop('disabled', false)
|
|
||||||
}
|
|
||||||
$('#last-question-number').show()
|
|
||||||
$('#last-question-number').text(pqc)
|
|
||||||
$('#set-question-last').show()
|
|
||||||
$('#set-question-increment').show()
|
|
||||||
if(pqc == qCount) {
|
|
||||||
$('#set-question-last').prop('disabled', true)
|
|
||||||
$('#set-question-increment').prop('disabled', true)
|
|
||||||
} else {
|
|
||||||
$('#set-question-last').prop('disabled', false)
|
|
||||||
$('#set-question-increment').prop('disabled', false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getdata() {
|
|
||||||
$('#refresh-data').prop('disabled',true)
|
|
||||||
$.ajax({
|
|
||||||
url: "/questions/watchdata/"+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"]
|
|
||||||
pqc = res["publicQuestionCount"]
|
|
||||||
qCount = res["questionCount"]
|
|
||||||
|
|
||||||
$('.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)
|
|
||||||
}
|
|
||||||
|
|
||||||
for(const b of allButtons) b.prop('disabled', false)
|
|
||||||
activateButtons()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function setPQC(n) {
|
|
||||||
for(const b of allButtons) b.prop('disabled', true)
|
|
||||||
$.ajax({
|
|
||||||
url: "/questions/set-public-question-count/"+quizzid,
|
|
||||||
type: "POST",
|
|
||||||
data: JSON.stringify({'publicQuestionCount': n}),
|
|
||||||
contentType: 'application/json',
|
|
||||||
success: function(res) {
|
|
||||||
getdata()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
function setPublicQuestionCount(n) {
|
|
||||||
return function (e) { setPQC(n) }
|
|
||||||
}
|
|
||||||
function setPublicQuestionCountLast(n) {
|
|
||||||
return function (e) { setPQC(qCount) }
|
|
||||||
}
|
|
||||||
|
|
||||||
function setPublicQuestionCountRelative(n) {
|
|
||||||
return function (e) { setPQC(pqc + n) }
|
|
||||||
}
|
|
||||||
|
|
||||||
$('#refresh-data').on('click',getdata)
|
|
||||||
$('#make-private').on('click',setPublicQuestionCount(null))
|
|
||||||
$('#make-public').on('click',setPublicQuestionCount(0))
|
|
||||||
$('#set-question-begin').on('click',setPublicQuestionCount(0))
|
|
||||||
$('#set-question-decrement').on('click',setPublicQuestionCountRelative(-1))
|
|
||||||
$('#set-question-increment').on('click',setPublicQuestionCountRelative(+1))
|
|
||||||
$('#set-question-last').on('click',setPublicQuestionCountLast())
|
|
||||||
getdata()
|
|
||||||
</script>
|
|
||||||
</main>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -3,7 +3,7 @@ package com.bernard.misael;
|
|||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
import com.bernard.misael.quizz.questions.QTypes;
|
import com.bernard.misael.questions.QTypes;
|
||||||
|
|
||||||
@SpringBootTest
|
@SpringBootTest
|
||||||
class MisaelApplicationTests {
|
class MisaelApplicationTests {
|
||||||
|
|||||||
@@ -17,8 +17,6 @@ version.org.springframework..spring-jdbc=6.2.3
|
|||||||
|
|
||||||
version.org.springframework.boot..spring-boot-docker-compose=3.4.3
|
version.org.springframework.boot..spring-boot-docker-compose=3.4.3
|
||||||
|
|
||||||
version.org.springframework.boot..spring-boot-starter-oauth2-client=4.1.0
|
|
||||||
|
|
||||||
version.org.webjars..jquery=1.9.1
|
version.org.webjars..jquery=1.9.1
|
||||||
## # available=1.10.1
|
## # available=1.10.1
|
||||||
## # available=1.10.2
|
## # available=1.10.2
|
||||||
|
|||||||
Reference in New Issue
Block a user