diff --git a/subato-web/package.json b/subato-web/package.json index 285c8338ccfa09e988b1cc53adb82ddf48675aa3..9e4f62287d9464092dcf28de0dbeb79dc2fb8d2a 100755 --- a/subato-web/package.json +++ b/subato-web/package.json @@ -1,6 +1,6 @@ { "name": "subato-web", - "version": "2.1.0", + "version": "2.1.1", "license": "MIT", "scripts": { "ng": "ng", diff --git a/subato-web/src/app/exercises/pages/submit-solution/components/intellij-import/intellij-import.component.ts b/subato-web/src/app/exercises/pages/submit-solution/components/intellij-import/intellij-import.component.ts index 0217d6917a6b3fad9c221930926a6776a1d0dd82..3463962a4f946c20024fccbc5b90274749ab58a1 100644 --- a/subato-web/src/app/exercises/pages/submit-solution/components/intellij-import/intellij-import.component.ts +++ b/subato-web/src/app/exercises/pages/submit-solution/components/intellij-import/intellij-import.component.ts @@ -24,8 +24,7 @@ export class IntellijImportComponent implements OnInit { } generateImportUrl() { - let rootUrl = - window.location.protocol + '//' + window.location.host + '/'; + let rootUrl = window.location.protocol + '//' + window.location.host; this.importUrl = `${rootUrl}/exercises/${this.taskInstance.exercise.id}/tasks/${this.taskInstance.task.id}`; } diff --git a/subato-web/src/app/misconceptions/models/concept.ts b/subato-web/src/app/misconceptions/models/concept.ts index f8b2b41a942c2b9a28cc82a246ff7b953fcf1d6a..79a027b6f145663372a659a6271c58b49b8d7bea 100644 --- a/subato-web/src/app/misconceptions/models/concept.ts +++ b/subato-web/src/app/misconceptions/models/concept.ts @@ -1,5 +1,5 @@ export class Concept { - id: number; + id: string; name: string; constructor(dto: any) { diff --git a/subato-web/src/app/profile/profile-store.ts b/subato-web/src/app/profile/profile-store.ts index 9536a1f08232367e88237f1ef2492ece04004068..af3c167589ffa81ce40ff12f63d0aa56906e9a8a 100644 --- a/subato-web/src/app/profile/profile-store.ts +++ b/subato-web/src/app/profile/profile-store.ts @@ -82,9 +82,13 @@ export class ProfileStore extends ComponentStore<ProfileState> { this.globalErrorHandler.handleError(err) ), catchError((error) => { + this.patchState({ + saving: false, + loading: false, + }); + if (isValidationError(error)) { this.patchState({ - saving: false, validationError: error, }); } diff --git a/subato-web/src/app/tasks/models/task.ts b/subato-web/src/app/tasks/models/task.ts index e57623d4a0518467641bfdfb952a78cd3e6289cc..c256b346c8deb9cd16cfd428eecdaf8d1491e2d3 100755 --- a/subato-web/src/app/tasks/models/task.ts +++ b/subato-web/src/app/tasks/models/task.ts @@ -3,6 +3,7 @@ import { SubmissionMode } from './submission-mode'; import { TaskFeedbackStats } from './task-feedback-stats'; import { TaskPool } from './task-pool'; import { Evaluator } from './evaluator'; +import { Concept } from 'app/misconceptions/models/concept'; export class Task { id: number; @@ -20,6 +21,7 @@ export class Task { feedbackStats: TaskFeedbackStats; acceptanceTestAvailable: boolean; evaluator: Evaluator; + concepts: Concept[]; repositoryUrl: string; archived: boolean; allowedExtensions: string[]; @@ -43,5 +45,8 @@ export class Task { this.repositoryUrl = task.repositoryUrl; this.allowedExtensions = task.allowedExtensions; this.archived = task.archived; + this.concepts = task.concepts + ? task.concepts.map((c) => new Concept(c)) + : []; } } diff --git a/subato-web/src/app/tasks/pages/task/components/concepts/concepts.component.html b/subato-web/src/app/tasks/pages/task/components/concepts/concepts.component.html new file mode 100644 index 0000000000000000000000000000000000000000..18af3478288208fca0992951dbf60980e720ecc0 --- /dev/null +++ b/subato-web/src/app/tasks/pages/task/components/concepts/concepts.component.html @@ -0,0 +1,13 @@ +<nb-card> + <nb-card-header>Konzepte</nb-card-header> + <nb-card-body> + <nb-tag-list *ngIf="concepts.length != 0"> + <nb-tag *ngFor="let concept of concepts" class="no-hover" + appearance="filled" [status]="'basic'" + [text]="concept.name"> + </nb-tag> + </nb-tag-list> + + <span *ngIf="concepts.length == 0">Keine vorhanden.</span> + </nb-card-body> +</nb-card> diff --git a/subato-web/src/app/tasks/pages/task/components/concepts/concepts.component.scss b/subato-web/src/app/tasks/pages/task/components/concepts/concepts.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subato-web/src/app/tasks/pages/task/components/concepts/concepts.component.ts b/subato-web/src/app/tasks/pages/task/components/concepts/concepts.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..94fde0375471280999970a81386112cc53c7c97e --- /dev/null +++ b/subato-web/src/app/tasks/pages/task/components/concepts/concepts.component.ts @@ -0,0 +1,15 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { Concept } from 'app/misconceptions/models/concept'; + +@Component({ + selector: 'sb-task-concepts', + templateUrl: './concepts.component.html', + styleUrls: ['./concepts.component.scss'], +}) +export class ConceptsComponent implements OnInit { + @Input() concepts: Concept[]; + + constructor() {} + + ngOnInit(): void {} +} diff --git a/subato-web/src/app/tasks/pages/task/task.page.component.html b/subato-web/src/app/tasks/pages/task/task.page.component.html index d0fa40f75f07ba73cdb8e71fea9a28ee4ceceacf..18948ab53b9d10373c238efe6d435e7d9b6d0882 100644 --- a/subato-web/src/app/tasks/pages/task/task.page.component.html +++ b/subato-web/src/app/tasks/pages/task/task.page.component.html @@ -51,6 +51,14 @@ </div> </div> + <div class="row"> + <div class="col-12"> + <sb-task-concepts + [concepts]="vm.taskState.task.concepts"> + </sb-task-concepts> + </div> + </div> + <div class="row"> <div class="col-12"> <nb-card> diff --git a/subato-web/src/app/tasks/pages/task/task.page.module.ts b/subato-web/src/app/tasks/pages/task/task.page.module.ts index 5b98bcdabc03c5cb0fa4a643240a4a808a96dd5b..1ed936189683b3cd5463ffe2f25637ad09659f0a 100644 --- a/subato-web/src/app/tasks/pages/task/task.page.module.ts +++ b/subato-web/src/app/tasks/pages/task/task.page.module.ts @@ -10,6 +10,7 @@ import { TaskPageComponent } from 'app/tasks/pages/task/task.page.component'; import { FeedbackInfoComponent } from 'app/tasks/pages/task/components/feedback-info/feedback-info.component'; import { SolutionsSharedModule } from 'app/solutions/shared/solutions-shared.module'; import { TaskCardComponent } from 'app/tasks/pages/task/components/task-card/task-card.component'; +import { ConceptsComponent } from './components/concepts/concepts.component'; @NgModule({ declarations: [ @@ -19,6 +20,7 @@ import { TaskCardComponent } from 'app/tasks/pages/task/components/task-card/tas TaskCardComponent, RepoHintComponent, FeedbackInfoComponent, + ConceptsComponent, ], imports: [ CommonModule, diff --git a/subato-web/src/environments/shared.ts b/subato-web/src/environments/shared.ts index db6450e3ff5d8f9cfb0dd29fd099c31ed9979b0b..59ee39b2cbf8ee0e39273079e9fdfa28941c952c 100644 --- a/subato-web/src/environments/shared.ts +++ b/subato-web/src/environments/shared.ts @@ -2,7 +2,7 @@ let docsUrl = 'http://docs.subato-test2.local.cs.hs-rm.de'; let manualUrl = `${docsUrl}/docs`; export const shared = { - version: '2.1.0', + version: '2.1.1', docsUrl: docsUrl, manualUrl: manualUrl, stefDocsUrl: `${docsUrl}/stef`, diff --git a/subato/build.gradle b/subato/build.gradle index 11a4938e487642fa7bc715689d7f3459d8e8b8ad..e8d66ec2be0fdba7c5dbd471927a9d66be371c08 100755 --- a/subato/build.gradle +++ b/subato/build.gradle @@ -19,7 +19,7 @@ plugins { } group = 'de.hsrm.sls' -version = '2.1.0-SNAPSHOT' +version = '2.1.1-SNAPSHOT' repositories { mavenCentral() diff --git a/subato/src/main/java/de/hsrm/sls/subato/shared/config/SubatoConfig.java b/subato/src/main/java/de/hsrm/sls/subato/shared/config/SubatoConfig.java index 7b8da6a2f0045d90c64c032cc114518d06c34f64..831dd0093ea6aaf17acf68560e0460d6cb014c8b 100644 --- a/subato/src/main/java/de/hsrm/sls/subato/shared/config/SubatoConfig.java +++ b/subato/src/main/java/de/hsrm/sls/subato/shared/config/SubatoConfig.java @@ -12,6 +12,7 @@ public class SubatoConfig { private String seedDir; private String publicHostname; private int maxLabChanges; + private Integer maxConsentChanges; public String getTmpDir() { return tmpDir; @@ -52,4 +53,12 @@ public class SubatoConfig { public void setMaxLabChanges(int maxLabChanges) { this.maxLabChanges = maxLabChanges; } + + public Integer getMaxConsentChanges() { + return maxConsentChanges; + } + + public void setMaxConsentChanges(Integer maxConsentChanges) { + this.maxConsentChanges = maxConsentChanges; + } } diff --git a/subato/src/main/java/de/hsrm/sls/subato/shared/error/rendering/HtmlExceptionRenderer.java b/subato/src/main/java/de/hsrm/sls/subato/shared/error/rendering/HtmlExceptionRenderer.java index f6d2b1738d4f234c811bfc801ce09058200a53b9..8a521862c4c824ea40af5fd5dd61cba21bc2568b 100644 --- a/subato/src/main/java/de/hsrm/sls/subato/shared/error/rendering/HtmlExceptionRenderer.java +++ b/subato/src/main/java/de/hsrm/sls/subato/shared/error/rendering/HtmlExceptionRenderer.java @@ -20,7 +20,7 @@ public class HtmlExceptionRenderer implements ExceptionRenderer { private final RequestCache requestCache = new HttpSessionRequestCache(); - @Value("${spring.profiles.active") + @Value("${spring.profiles.active:}") private String activeProfile; @Override diff --git a/subato/src/main/java/de/hsrm/sls/subato/shared/swagger/SwaggerConfiguration.java b/subato/src/main/java/de/hsrm/sls/subato/shared/swagger/SwaggerConfiguration.java index ebc07762162fe6d38d202eb9e761f88903ecd886..7acb764058d90ca064bc537b6e63edf1db849ec2 100644 --- a/subato/src/main/java/de/hsrm/sls/subato/shared/swagger/SwaggerConfiguration.java +++ b/subato/src/main/java/de/hsrm/sls/subato/shared/swagger/SwaggerConfiguration.java @@ -30,7 +30,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class SwaggerConfiguration implements WebMvcConfigurer { - private final String API_VERSION = "2.1.0"; + private final String API_VERSION = "2.1.1"; @Value("${keycloak.auth-server-url}") private String keycloakUrl; diff --git a/subato/src/main/java/de/hsrm/sls/subato/tasks/stef/cache/StefSpecCache.java b/subato/src/main/java/de/hsrm/sls/subato/tasks/stef/cache/StefSpecCache.java index 7fb66a098c8ab175568d4642c3bbcbb131f87039..fdd4045adbe14226194f770cf4462ac664fa7584 100644 --- a/subato/src/main/java/de/hsrm/sls/subato/tasks/stef/cache/StefSpecCache.java +++ b/subato/src/main/java/de/hsrm/sls/subato/tasks/stef/cache/StefSpecCache.java @@ -12,7 +12,7 @@ public class StefSpecCache { * this value needs to be incremented if breaking changes are made in this class, which would * lead to parsing errors. in this case, the spec needs to be parsed and serialized again */ - public static final int SERIALIZATION_VERSION = 9; + public static final int SERIALIZATION_VERSION = 10; /** * not to be confused with the version of the spec! Changes in this model can occur, even if the diff --git a/subato/src/main/java/de/hsrm/sls/subato/tasks/stef/parser/meta/ConceptResolver.java b/subato/src/main/java/de/hsrm/sls/subato/tasks/stef/parser/meta/ConceptResolver.java new file mode 100644 index 0000000000000000000000000000000000000000..dc07796389a5628280dae2a926cc94caa4b5ee3b --- /dev/null +++ b/subato/src/main/java/de/hsrm/sls/subato/tasks/stef/parser/meta/ConceptResolver.java @@ -0,0 +1,45 @@ +package de.hsrm.sls.subato.tasks.stef.parser.meta; + +import de.hsrm.sls.subato.misclib.MisconceptionRepository; +import de.hsrm.sls.subato.misclib.model.Concept; +import de.hsrm.sls.subato.tasks.stef.parser.meta.xml.TaskXml; +import java.util.ArrayList; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +class ConceptResolver { + + private static Logger logger = LoggerFactory.getLogger(ConceptResolver.class); + + @Autowired + private MisconceptionRepository misconceptionRepository; + + public List<Concept> resolve(TaskXml doc) { + var concepts = new ArrayList<Concept>(); + + if (doc.getConcepts() == null) { + return concepts; + } + + var availableConcepts = misconceptionRepository.getConcepts(); + for (var conceptXml : doc.getConcepts().getConcepts()) { + var matchingConcept = availableConcepts.stream() + .filter(c -> c.getId().equals(conceptXml.getId())).findFirst().orElse(null); + if (matchingConcept == null) { + continue; + /* + throw new InvalidSpecException( + "Konzept %s existiert nicht im System".formatted(conceptXml.getId())); + */ + } + + concepts.add(matchingConcept); + } + + return concepts; + } +} diff --git a/subato/src/main/java/de/hsrm/sls/subato/tasks/stef/parser/meta/MetaParser.java b/subato/src/main/java/de/hsrm/sls/subato/tasks/stef/parser/meta/MetaParser.java index 06184009f9c60572a54ac2c8172e02e0515e2f28..351403d01f0961a2d3fcb870ccbd7ed1c97374eb 100644 --- a/subato/src/main/java/de/hsrm/sls/subato/tasks/stef/parser/meta/MetaParser.java +++ b/subato/src/main/java/de/hsrm/sls/subato/tasks/stef/parser/meta/MetaParser.java @@ -36,6 +36,9 @@ public class MetaParser { @Autowired private MimeMappingResolver mimeMappingResolver; + @Autowired + private ConceptResolver conceptResolver; + @Autowired private StefConfig stefConfig; @@ -65,6 +68,9 @@ public class MetaParser { var submissionModeConfig = submissionModeResolver.resolve(doc, meta.getTemplateFiles()); meta.setSubmissionModeConfig(submissionModeConfig); + var concepts = conceptResolver.resolve(doc); + meta.setConcepts(concepts); + return meta; } diff --git a/subato/src/main/java/de/hsrm/sls/subato/tasks/stef/parser/meta/xml/ConceptXml.java b/subato/src/main/java/de/hsrm/sls/subato/tasks/stef/parser/meta/xml/ConceptXml.java new file mode 100644 index 0000000000000000000000000000000000000000..1b3beef62a5439cd44f4d83d5d4f92108c5d6e4c --- /dev/null +++ b/subato/src/main/java/de/hsrm/sls/subato/tasks/stef/parser/meta/xml/ConceptXml.java @@ -0,0 +1,17 @@ +package de.hsrm.sls.subato.tasks.stef.parser.meta.xml; + +import jakarta.xml.bind.annotation.XmlValue; + +public class ConceptXml { + + private String id; + + public String getId() { + return id; + } + + @XmlValue + public void setId(String as) { + this.id = as; + } +} diff --git a/subato/src/main/java/de/hsrm/sls/subato/tasks/stef/parser/meta/xml/ConceptsXml.java b/subato/src/main/java/de/hsrm/sls/subato/tasks/stef/parser/meta/xml/ConceptsXml.java new file mode 100644 index 0000000000000000000000000000000000000000..acdb116eccf0da4f47723ca3ba15460cf3007e6c --- /dev/null +++ b/subato/src/main/java/de/hsrm/sls/subato/tasks/stef/parser/meta/xml/ConceptsXml.java @@ -0,0 +1,20 @@ +package de.hsrm.sls.subato.tasks.stef.parser.meta.xml; + +import jakarta.xml.bind.annotation.XmlElement; +import java.util.List; + +public class ConceptsXml { + + public static final String CONCEPT = "concept"; + + private List<ConceptXml> concepts; + + public List<ConceptXml> getConcepts() { + return concepts; + } + + @XmlElement(name = CONCEPT) + public void setConcepts(List<ConceptXml> concepts) { + this.concepts = concepts; + } +} diff --git a/subato/src/main/java/de/hsrm/sls/subato/tasks/stef/parser/meta/xml/TaskXml.java b/subato/src/main/java/de/hsrm/sls/subato/tasks/stef/parser/meta/xml/TaskXml.java index d13de0d69b51a0aaeec0be7cfbe4fffb2684c083..07d77c200298c7d2e3696d098f72f9ca9b0df540 100644 --- a/subato/src/main/java/de/hsrm/sls/subato/tasks/stef/parser/meta/xml/TaskXml.java +++ b/subato/src/main/java/de/hsrm/sls/subato/tasks/stef/parser/meta/xml/TaskXml.java @@ -17,6 +17,7 @@ public class TaskXml { public static final String SUBMISSION_MODES = "submissionModes"; public static final String FILES = "files"; public static final String MIME_MAPPINGS = "mimeMappings"; + public static final String CONCEPTS = "concepts"; private Long subatoId; private Integer attempts; @@ -28,6 +29,7 @@ public class TaskXml { private FilesXml files; private MimeMappingsXml mimeMappings; private SubmissionModesXml submissionModes; + private ConceptsXml concepts; public String getId() { return id; @@ -118,4 +120,13 @@ public class TaskXml { public void setMimeMappings(MimeMappingsXml mimeMappings) { this.mimeMappings = mimeMappings; } + + public ConceptsXml getConcepts() { + return concepts; + } + + @XmlElement(name = CONCEPTS) + public void setConcepts(ConceptsXml concepts) { + this.concepts = concepts; + } } diff --git a/subato/src/main/java/de/hsrm/sls/subato/tasks/stef/spec/Meta.java b/subato/src/main/java/de/hsrm/sls/subato/tasks/stef/spec/Meta.java index d2764f1088fd8fc4e698979e5377fb12bf1c4926..d49207a89000c6511690c9b0e890ad5e1059e3fe 100644 --- a/subato/src/main/java/de/hsrm/sls/subato/tasks/stef/spec/Meta.java +++ b/subato/src/main/java/de/hsrm/sls/subato/tasks/stef/spec/Meta.java @@ -1,6 +1,7 @@ package de.hsrm.sls.subato.tasks.stef.spec; import com.fasterxml.jackson.annotation.JsonIgnore; +import de.hsrm.sls.subato.misclib.model.Concept; import de.hsrm.sls.subato.shared.mime.MimeMapping; import java.util.List; import java.util.stream.Collectors; @@ -20,6 +21,7 @@ public class Meta { SubmissionModeConfig submissionModeConfig; List<FileRef> files; List<MimeMapping> mimeMappings; + List<Concept> concepts; public String getId() { return id; @@ -106,4 +108,12 @@ public class Meta { public void setMimeMappings(List<MimeMapping> mimeMappings) { this.mimeMappings = mimeMappings; } + + public List<Concept> getConcepts() { + return concepts; + } + + public void setConcepts(List<Concept> concepts) { + this.concepts = concepts; + } } diff --git a/subato/src/main/java/de/hsrm/sls/subato/tasks/task/dto/TaskDto.java b/subato/src/main/java/de/hsrm/sls/subato/tasks/task/dto/TaskDto.java index 299dc1ed93870c98be2d86c805bb7893436fa864..3843cdfa123f1f2596fd2045223076cb88806adc 100644 --- a/subato/src/main/java/de/hsrm/sls/subato/tasks/task/dto/TaskDto.java +++ b/subato/src/main/java/de/hsrm/sls/subato/tasks/task/dto/TaskDto.java @@ -1,5 +1,6 @@ package de.hsrm.sls.subato.tasks.task.dto; +import de.hsrm.sls.subato.misclib.model.Concept; import de.hsrm.sls.subato.solutions.evaluation.evaluator.EvaluatorDto; import de.hsrm.sls.subato.tasks.feedback.model.TaskFeedbackStats; import de.hsrm.sls.subato.tasks.lang.dto.LanguageDto; @@ -28,6 +29,7 @@ public class TaskDto { private List<String> allowedExtensions; private boolean archived; private String bundle; + private List<Concept> concepts; public long getId() { return id; @@ -172,4 +174,12 @@ public class TaskDto { public void setBundle(String bundle) { this.bundle = bundle; } + + public List<Concept> getConcepts() { + return concepts; + } + + public void setConcepts(List<Concept> concepts) { + this.concepts = concepts; + } } diff --git a/subato/src/main/java/de/hsrm/sls/subato/tasks/task/dto/TaskMapper.java b/subato/src/main/java/de/hsrm/sls/subato/tasks/task/dto/TaskMapper.java index ebd973c84f7f9f42911d7158a4758a4fbca3b0f4..247c4efdf01388f85cb8c3f1d0d135b7337d8bd5 100644 --- a/subato/src/main/java/de/hsrm/sls/subato/tasks/task/dto/TaskMapper.java +++ b/subato/src/main/java/de/hsrm/sls/subato/tasks/task/dto/TaskMapper.java @@ -9,5 +9,6 @@ public interface TaskMapper { @Mapping(target = "attempts", source = "specCache.spec.meta.attempts") @Mapping(target = "description", source = "specCache.spec.meta.description") + @Mapping(target = "concepts", source = "specCache.spec.meta.concepts") TaskDto map(Task task); } diff --git a/subato/src/main/java/de/hsrm/sls/subato/user/ConsentChangedTooOftenException.java b/subato/src/main/java/de/hsrm/sls/subato/user/ConsentChangedTooOftenException.java new file mode 100644 index 0000000000000000000000000000000000000000..18b5328c147669f05437dc0af2acf2fcda66ff23 --- /dev/null +++ b/subato/src/main/java/de/hsrm/sls/subato/user/ConsentChangedTooOftenException.java @@ -0,0 +1,9 @@ +package de.hsrm.sls.subato.user; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "Du hast zu oft deine Meinung geändert. Bitte melde dich persönlich bei uns, wenn du deine Entscheidung ändern möchtest.") +public class ConsentChangedTooOftenException extends RuntimeException { + +} \ No newline at end of file diff --git a/subato/src/main/java/de/hsrm/sls/subato/user/UpdateProfileUseCase.java b/subato/src/main/java/de/hsrm/sls/subato/user/UpdateProfileUseCase.java index 1b9eb77856fba2678dfdcc1597125b7628574135..0d7480d204c262883a263e3ae6e5131b579c9b1a 100644 --- a/subato/src/main/java/de/hsrm/sls/subato/user/UpdateProfileUseCase.java +++ b/subato/src/main/java/de/hsrm/sls/subato/user/UpdateProfileUseCase.java @@ -1,6 +1,7 @@ package de.hsrm.sls.subato.user; import de.hsrm.sls.subato.auth.principal.PrincipalService; +import de.hsrm.sls.subato.shared.config.SubatoConfig; import de.hsrm.sls.subato.user.dto.UpdateProfileDto; import de.hsrm.sls.subato.user.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; @@ -13,10 +14,20 @@ public class UpdateProfileUseCase { private UserRepository userRepository; @Autowired - PrincipalService principalService; + private PrincipalService principalService; + + @Autowired + private SubatoConfig subatoConfig; public User update(User user, UpdateProfileDto dto) { + if (subatoConfig.getMaxConsentChanges() != null + && user.getConsentChangeCount() > subatoConfig.getMaxConsentChanges()) { + throw new ConsentChangedTooOftenException(); + } + user.setConsentedToCollection(dto.isConsentedToCollection()); + user.setConsentChangeCount(user.getConsentChangeCount() + 1); + user = userRepository.save(user); principalService.evictPrincipal(user); return user; diff --git a/subato/src/main/java/de/hsrm/sls/subato/user/User.java b/subato/src/main/java/de/hsrm/sls/subato/user/User.java index 04fd0420fdffe66b3c5e2e7502fe729bac2d447e..047ae1e552ae51ce24e26c630f5f7151bc33a747 100755 --- a/subato/src/main/java/de/hsrm/sls/subato/user/User.java +++ b/subato/src/main/java/de/hsrm/sls/subato/user/User.java @@ -87,6 +87,7 @@ public class User { private List<CourseParticipantProfile> participantProfiles = new ArrayList<>(); private boolean consentedToCollection; + private int consentChangeCount; public User() { } @@ -235,9 +236,16 @@ public class User { return consentedToCollection; } - public User setConsentedToCollection(boolean consentedToCollection) { + public void setConsentedToCollection(boolean consentedToCollection) { this.consentedToCollection = consentedToCollection; - return this; + } + + public int getConsentChangeCount() { + return consentChangeCount; + } + + public void setConsentChangeCount(int consentChangeCount) { + this.consentChangeCount = consentChangeCount; } @Override diff --git a/subato/src/main/resources/application-prod.properties b/subato/src/main/resources/application-prod.properties index 10d587c5042328a0c8ae983ca45d1454ea673679..3f1134e479e12b01e3e58f6d8406cebb404d5ff2 100644 --- a/subato/src/main/resources/application-prod.properties +++ b/subato/src/main/resources/application-prod.properties @@ -1,4 +1,5 @@ server.port=8443 +subato.max_consent_changes=10 logging.level.de.hsrm.sls.subato.=INFO server.ssl.enabled=true server.ssl.key-store-type=PKCS12 diff --git a/subato/src/main/resources/db/changelog/2023/09/22-01-changelog.yaml b/subato/src/main/resources/db/changelog/2023/09/22-01-changelog.yaml new file mode 100644 index 0000000000000000000000000000000000000000..ee2b430c7327cf08405d2cddae13c7907b4efd1c --- /dev/null +++ b/subato/src/main/resources/db/changelog/2023/09/22-01-changelog.yaml @@ -0,0 +1,22 @@ +databaseChangeLog: + - changeSet: + id: 1695403516677-1 + author: fischer (generated) + objectQuotingStrategy: QUOTE_ONLY_RESERVED_WORDS + changes: + - addColumn: + columns: + - column: + name: consent_change_count + type: INT + defaultValueNumeric: 0 + tableName: users + - changeSet: + id: 1695403516677-2 + author: fischer (generated) + objectQuotingStrategy: QUOTE_ONLY_RESERVED_WORDS + changes: + - addNotNullConstraint: + columnName: consent_change_count + tableName: users + diff --git a/subato/src/main/resources/db/changelog/db.changelog-master.yaml b/subato/src/main/resources/db/changelog/db.changelog-master.yaml index d195f3b146269e88e0f431802aeccb0227ad9a8b..c07eb8ee0b0e8d70154a9ccab97557b29620a528 100644 --- a/subato/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/subato/src/main/resources/db/changelog/db.changelog-master.yaml @@ -46,4 +46,6 @@ databaseChangeLog: - include: file: db/changelog/2023/07/05-01-changelog.yaml - include: - file: db/changelog/2023/09/17-01-changelog.yaml \ No newline at end of file + file: db/changelog/2023/09/17-01-changelog.yaml + - include: + file: db/changelog/2023/09/22-01-changelog.yaml \ No newline at end of file