Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
ca13504
Backend Prüfung klappt, aber Html Tags etc werden noch mitgezählt
lschulen Jul 1, 2026
2fb3e8f
Backend Prüfung klappt, aber Html Tags etc werden noch mitgezählt
lschulen Jul 7, 2026
afcbb64
Backend Prüfung klappt, aber Html Tags etc werden noch mitgezählt
lschulen Jul 7, 2026
b4c9fe5
aktueller Stand 26-07-15
lschulen Jul 15, 2026
aea6bcc
Merge remote-tracking branch 'origin/L_8-question-texts-can-be-too-lo…
lschulen Jul 15, 2026
8acfdbc
errormessages
lschulen Jul 15, 2026
2a54c74
Added a helper function to remove all HTML tags when counting the cha…
lschulen Jul 15, 2026
ba12df4
Added Counting functionality for the amount of characters in Descript…
lschulen Jul 21, 2026
122905c
corrected the calculation of the number of characters
lschulen Jul 21, 2026
086b742
Merge branch 'v3.4.0' into L_8-question-texts-can-be-too-long-restored
ywarnecke Aug 18, 2026
07ca4e7
Refactor visible text extraction into HtmlUtilities
lschulen Aug 18, 2026
5a6f8cc
Add live question text length validation
lschulen Aug 18, 2026
b182d43
Made live WYSIWYG length validation reusable and applied it in questi…
lschulen Aug 19, 2026
6d9927f
Added message keys and adjusted the frontend
lschulen Aug 19, 2026
30db67d
adjusted the backend as well
lschulen Aug 19, 2026
3e83d87
implemented frontend check for bundles and enures that info disappear…
lschulen Aug 19, 2026
0fc62f2
implemented backend check for bundles
lschulen Aug 19, 2026
d69f783
Added new message keys if welcometext or final text is too long
lschulen Aug 25, 2026
3fb2314
Added backend check if any welcome text in bundle is too long
lschulen Aug 25, 2026
e651dfa
Added backend check if any final text in bundle is too long
lschulen Aug 25, 2026
dba9c6e
Added frontend check if any welcome or final text in bundle is too long
lschulen Aug 25, 2026
fe0eed7
Added backend check if any welcome or final text in questionnaire is …
lschulen Aug 25, 2026
350d406
Added frontend check if any welcome or final text in questionnaire is…
lschulen Aug 25, 2026
5d52c45
correced a variable name
lschulen Aug 25, 2026
df160aa
Added frontend check if description text in clinic is too long
lschulen Aug 25, 2026
4faae27
Added backend check if description text in clinic is too long
lschulen Aug 25, 2026
8fdda9b
Added backend check if description text in clinic is empty
lschulen Aug 25, 2026
065e298
Optimized isEmpty check for description in bundle
lschulen Aug 25, 2026
506a5cb
Optimized isEmpty check for description in bundle
lschulen Aug 25, 2026
7ba9138
Added backend check if message in invitation is too long. added those…
lschulen Aug 26, 2026
ab9467e
Added frontend check if message in invitation is too long. needed a d…
lschulen Aug 26, 2026
13ee45c
comment
lschulen Aug 26, 2026
89e9667
deleted sysout
lschulen Aug 26, 2026
372caee
Show only one error message for completely empty text fields in Bundl…
lschulen Sep 1, 2026
57e16f1
Fixed JUnit errors
lschulen Sep 1, 2026
dee334f
Started JUnit Tests in Bundle. Still producing error
lschulen Sep 1, 2026
7a5ad0f
Fix: Removed outdated duplicates in src/test/resources/message
lschulen Sep 8, 2026
500a677
Added tests for welcome and final text length validation in BundleDTO…
lschulen Sep 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion src/main/java/de/imi/mopat/validator/QuestionDTOValidator.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class QuestionDTOValidator implements Validator {

private static final String MIN_NUMBER_ANSWERS = "minNumberAnswers";
private static final String MAX_NUMBER_ANSWERS = "maxNumberAnswers";
private static final int MAX_QUESTION_TEXT_LENGTH = 2_000;
@Autowired
private SelectAnswerDTOValidator selectAnswerDTOValidator;
@Autowired
Expand Down Expand Up @@ -56,7 +57,7 @@ public void validate(final Object target, final Errors errors) {
// [bt] now it's my time to validate the more complex stuff
QuestionDTO questionDTO = (QuestionDTO) target;

// [sw] Check if any added language contains an empty questionText
// [sw] Check if any added language contains an empty or too long questionText
for (Map.Entry<String, String> entry : questionDTO.getLocalizedQuestionText().entrySet()) {
if (entry.getValue() == null || entry.getValue().trim().isEmpty() || Pattern.matches(
"<p>(<p>|</p>|\\s|&nbsp;|<br>)+<\\/p>", entry.getValue())) {
Expand All @@ -72,6 +73,11 @@ public void validate(final Object target, final Errors errors) {
messageSource.getMessage("question.error" + ".questionTextIsNull",
new Object[]{}, LocaleContextHolder.getLocale()));
}
} else if(getVisibleText(entry.getValue()).length() > MAX_QUESTION_TEXT_LENGTH){
errors.rejectValue("localizedQuestionText[" + entry.getKey() + "]",
MoPatValidator.ERRORCODE_ERRORMESSAGE,
messageSource.getMessage("question.error.questionTextTooLong",
new Object[]{getVisibleText(entry.getValue()).length(),MAX_QUESTION_TEXT_LENGTH}, LocaleContextHolder.getLocale()));
}
}

Expand Down Expand Up @@ -234,4 +240,28 @@ public void validate(final Object target, final Errors errors) {
break;
}
}

// A utility function to remove all HTML tags when counting the characters in a text
public static String getVisibleText(String html) {
if (html == null || html.isEmpty()) {
return "";
}

String text = html
.replace("\r\n", "\n")
.replace("\r", "\n");

text = text.replaceAll("(?i)<br\\s*/?>", "\n");
text = text.replaceAll("(?i)</(?:div|p|li|h[1-6])\\s*>", "\n");
text = text.replaceAll("(?s)<[^>]+>", "");

text = text.replace("&nbsp;", " ");
text = text.replace("&amp;", "&");
text = text.replace("&lt;", "<");
text = text.replace("&gt;", ">");
text = text.replace("&quot;", "\"");
text = text.replace("&#39;", "'");

return text.replaceAll("\n{3,}", "\n\n").trim();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@
@Component
public class QuestionnaireDTOValidator implements Validator {

private static final int MAX_DESCRIPTION_TEXT_LENGTH = 2_000;

@Autowired
private MessageSource messageSource;

@Autowired
private QuestionnaireDao questionnaireDao;


@Override
public boolean supports(final Class<?> type) {
return QuestionnaireDTO.class.isAssignableFrom(type);
Expand Down Expand Up @@ -56,6 +59,15 @@ public void validate(final Object o, final Errors errors) {
LocaleContextHolder.getLocale()));
}

// Check if the description text is too long
int visibleDescpriptionLength = getVisibleText(questionnaireDTO.getDescription()).length();
if(visibleDescpriptionLength > MAX_DESCRIPTION_TEXT_LENGTH){
errors.rejectValue("description",
MoPatValidator.ERRORCODE_ERRORMESSAGE,
messageSource.getMessage("question.error.questionTextTooLong",
new Object[]{visibleDescpriptionLength, MAX_DESCRIPTION_TEXT_LENGTH}, LocaleContextHolder.getLocale()));
}

// [sw] Check if any added language contains an empty questionText
for (Map.Entry<String, String> entry : questionnaireDTO.getLocalizedDisplayName()
.entrySet()) {
Expand Down Expand Up @@ -124,4 +136,28 @@ public void validate(final Object o, final Errors errors) {
}
}
}

// A utility function to remove all HTML tags when counting the characters in a text
public static String getVisibleText(String html) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you use the same utility function twice, please see if it can be moved to an existing Helper class to reduce code duplicates. Otherwise you could also create a new HTMLHelper.

if (html == null || html.isEmpty()) {
return "";
}

String text = html
.replace("\r\n", "\n")
.replace("\r", "\n");

text = text.replaceAll("(?i)<br\\s*/?>", "\n");
text = text.replaceAll("(?i)</(?:div|p|li|h[1-6])\\s*>", "\n");
text = text.replaceAll("(?s)<[^>]+>", "");

text = text.replace("&nbsp;", " ");
text = text.replace("&amp;", "&");
text = text.replace("&lt;", "<");
text = text.replace("&gt;", ">");
text = text.replace("&quot;", "\"");
text = text.replace("&#39;", "'");

return text.replaceAll("\n{3,}", "\n\n").trim();
}
}
1 change: 1 addition & 0 deletions src/main/resources/message/messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,7 @@ question.error.noValidScoreMinMax=It is not possible to give exactly one answer.
question.error.noValidScoreQuestionType=A question type has been selected that does not support score calculation. If you save the question, all scores that contain this question will be deleted. Do you really want to save the question?
question.error.notModifiable=This question is not editable due to already existing responses from surveys
question.error.questionTextIsNull=The localized question text is required
question.error.questionTextTooLong=The question text contains {0} characters and exceeds the maximum of {1} characters.
question.error.sliderDifferenceMaxMinNotDivisibleByStepsize=The difference between max and min values is not divisible without remainder by the step size
question.error.sliderStepsizeLessOrEqualToZero=The stepsize should not be less or equal to 0
question.heading.editQuestion=Edit question
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/message/messages_de_DE.properties
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,7 @@ question.error.noValidScoreMinMax=Es kann nicht genau eine Antwortm\u00f6glichke
question.error.noValidScoreQuestionType=Es wurde ein Fragetyp ausgew\u00e4hlt, der keine Scoreberechnung unterst\u00fctzt. Wenn Sie die Frage speichern, werden somit alle Scores, die diese Frage enthalten gel\u00f6scht. Wollen Sie die Frage wirklich speichern?
question.error.notModifiable=Diese Frage ist nicht editierbar, da bereits Antworten in Befragungen gegeben wurden
question.error.questionTextIsNull=Die Frage ben\u00f6tigt einen lokalisierten Fragetext
question.error.questionTextTooLong=Der Fragetext hat {0} Zeichen und \u00fcberschreitet das Maximum von {1} Zeichen.
question.error.sliderDifferenceMaxMinNotDivisibleByStepsize=Der Abstand zwischen Minimum und Maximum ist nicht restlos durch die Schrittweite teilbar
question.error.sliderStepsizeLessOrEqualToZero=Die Schrittgr\u00f6\u00dfe darf nicht kleiner oder gleich 0 sein
question.heading.editQuestion=Frage bearbeiten
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/message/messages_en_GB.properties
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,7 @@ question.error.noValidScoreMinMax=It is not possible to give exactly one answer.
question.error.noValidScoreQuestionType=A question type has been selected that does not support score calculation. If you save the question, all scores that contain this question will be deleted. Do you really want to save the question?
question.error.notModifiable=This question is not editable due to already existing responses from surveys
question.error.questionTextIsNull=The localized question text is required
question.error.questionTextTooLong=The question text contains {0} characters and exceeds the maximum of {1} characters.
question.error.sliderDifferenceMaxMinNotDivisibleByStepsize=The difference between max and min values is not divisible without remainder by the step size
question.error.sliderStepsizeLessOrEqualToZero=The stepsize should not be less or equal to 0
question.heading.editQuestion=Edit question
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/message/messages_es_ES.properties
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,7 @@ question.error.noValidScoreMinMax=No puede incluirse solamente una opci\u00f3n c
question.error.noValidScoreQuestionType=Ha seleccionado un tipo de pregunta que no soporta el c\u00e1lculo de puntuaciones. Cuando guarde la pregunta, las puntuaciones asignadas a ella ser\u00e1n borradas.Est\u00e1 seguro/a de que desea guardar los cambios?
question.error.notModifiable=Esta pregunta no es editable debido a que ya existen respuestas
question.error.questionTextIsNull=Se requiere texto para la pregunta
question.error.questionTextTooLong=El texto de la pregunta contiene {0} caracteres y supera el m\u00e1ximo de {1} caracteres.
question.error.sliderDifferenceMaxMinNotDivisibleByStepsize=La diferencia entre los valores m\u00e1x y m\u00edn no es divisible entre el tama\u00f1o de salto
question.error.sliderStepsizeLessOrEqualToZero=El tama\u00f1o de salto debe ser mayor o igual que 0
question.heading.editQuestion=Editar pregunta
Expand Down
14 changes: 14 additions & 0 deletions src/main/webapp/WEB-INF/question/edit.html
Original file line number Diff line number Diff line change
Expand Up @@ -1346,6 +1346,20 @@
$(this).attr("disabled", true);
}
});

// Überwachung der WYSIWYG-Editoren auf maximale Länge

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please adjust this solution:

  1. It should show a red label above the textarea instead of an alert.
  2. Remove the console print
  3. Adjust the message to be language dependent. You can replace Javascript variables with Thymeleaf. You can see how starting from line 1251

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also please dont use german comments in the code base

$(document).on('keydown', '.note-editable', function(e) {
var currentLength = $(this).text().length;
if (currentLength >= 1500) {
// Verhindere die Eingabe, wenn 1500 Zeichen erreicht sind
// (Ausnahme: Backspace/Delete Taste erlauben)
console.log("Textlaenge ueberschritten")
if (e.keyCode !== 8 && e.keyCode !== 46) {
e.preventDefault();
alert("Maximale Länge von 1500 Zeichen erreicht!");
}
}
});
});

/*[- Reduce the size of multiple choice answers to 98% for the move buttons -]*/
Expand Down
Loading