Skip to content
This repository was archived by the owner on May 5, 2021. It is now read-only.

Commit e5b9405

Browse files
[IMIS] SORMAS-Foundation#3220 Fix classification-rule HTML formatting, add helper method to unescape specific tags (SORMAS-Foundation#3240)
* initial commit, not yet fully functional * Info-Popup will now correctly display html by only escaping where necessary and allowing specific tags * Improved styling, only applicable criteria are shown * Fixed linebreak in "classified by system" label * Allow additional Tags. Close SORMAS-Foundation#3220 Co-authored-by: David <[email protected]>
1 parent 758c90a commit e5b9405

5 files changed

Lines changed: 90 additions & 40 deletions

File tree

sormas-api/src/main/java/de/symeda/sormas/api/caze/classification/ClassificationHtmlRenderer.java

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,14 @@
1717
*******************************************************************************/
1818
package de.symeda.sormas.api.caze.classification;
1919

20+
import static de.symeda.sormas.api.utils.HtmlHelper.escapeAndUnescapeBasicTags;
21+
import static de.symeda.sormas.api.utils.HtmlHelper.unescapeBasicTags;
22+
2023
import java.util.Date;
2124
import java.util.List;
2225

2326
import org.apache.commons.lang3.StringUtils;
27+
import org.apache.commons.text.StringEscapeUtils;
2428

2529
import de.symeda.sormas.api.Disease;
2630
import de.symeda.sormas.api.FacadeProvider;
@@ -31,7 +35,6 @@
3135
import de.symeda.sormas.api.utils.DataHelper;
3236
import de.symeda.sormas.api.utils.DateHelper;
3337
import de.symeda.sormas.api.utils.InfoProvider;
34-
import org.apache.commons.text.StringEscapeUtils;
3538

3639
/**
3740
* Provides methods that create HTML Strings to visualize the automatic classification rules.
@@ -131,18 +134,16 @@ public static String createHtmlForDownload(String sormasServerUrl, List<Disease>
131134
" padding: 8px;\r\n" +
132135
"}\r\n" +
133136
".classification-rules .main-criteria.main-criteria-suspect {\r\n" +
134-
" background: rgba(255, 215, 0, 0.6);\r\n" +
135-
" margin-bottom: 16px;\r\n" +
137+
" background: rgba(255, 215, 0, 0.6);\r\n" +
136138
"}\r\n" +
137139
".classification-rules .main-criteria.main-criteria-probable {\r\n" +
138-
" background: rgba(255, 140, 0, 0.6);\r\n" +
139-
" margin-bottom: 16px;\r\n" +
140+
" background: rgba(255, 140, 0, 0.6);\r\n" +
140141
"}\r\n" +
141142
".classification-rules .main-criteria.main-criteria-confirmed {\r\n" +
142143
" background: rgba(255, 0, 0, 0.6);\r\n" +
143144
"}\r\n" +
144145
".classification-rules .main-criteria.main-criteria-not_a_case {\r\n" +
145-
" background: rgba(201, 201, 201, 0.6);\r\n" +
146+
" background: rgba(160, 160, 160, 0.6);\r\n" +
146147
"}\r\n" +
147148
".classification-rules .headline {\r\n" +
148149
" font-weight: bold;\r\n" +
@@ -188,6 +189,7 @@ public static String createHtmlForDownload(String sormasServerUrl, List<Disease>
188189
html.append(createSuspectHtmlString(diseaseCriteria));
189190
html.append(createProbableHtmlString(diseaseCriteria));
190191
html.append(createConfirmedHtmlString(diseaseCriteria));
192+
html.append(createNotACaseHtmlString(diseaseCriteria));
191193
}
192194
}
193195
html.append("</body></html>");
@@ -239,7 +241,7 @@ private static String buildSubCriteriaDiv(
239241
for (ClassificationCriteriaDto subCriteria : ((ClassificationCollectiveCriteria) criteria).getSubCriteria()) {
240242
if (!(subCriteria instanceof ClassificationCollectiveCriteria) || subCriteria instanceof ClassificationCompactCriteria) {
241243
// For non-collective or compact collective criteria, add the description as a list item
242-
subCriteriaSb.append("- " + subCriteria.buildDescription() + "</br>");
244+
subCriteriaSb.append("- " + escapeAndUnescapeBasicTags(subCriteria.buildDescription() + "</br>"));
243245
} else if (subCriteria instanceof ClassificationCollectiveCriteria
244246
&& !(subCriteria instanceof ClassificationAllOfCriteriaDto)
245247
&& !(subCriteria.getClass() == ClassificationXOfCriteriaDto.class)) {
@@ -273,7 +275,7 @@ private static String createSurroundingDiv(ClassificationCriteriaType criteriaTy
273275
+ "<div class='main-criteria main-criteria-"
274276
+ StringEscapeUtils.escapeHtml4(criteriaType.toString())
275277
+ "'>"
276-
+ StringEscapeUtils.escapeHtml4(content)
278+
+ content
277279
+ "</div></div>";
278280
//@formatter:on
279281
}
@@ -294,11 +296,13 @@ private static String createHeadlineDiv(String headline) {
294296
* Creates a div containing an info text.
295297
*/
296298
private static String createInfoDiv() {
297-
return I18nProperties.getString(Strings.classificationInfoText);
299+
return unescapeBasicTags(StringEscapeUtils.escapeHtml4(I18nProperties.getString(Strings.classificationInfoText)));
298300
}
299301

300302
private static String createInfoDiv(int requirementsNumber) {
301-
return String.format(I18nProperties.getString(Strings.classificationInfoNumberText), DataHelper.parseNumberToString(requirementsNumber));
303+
return unescapeBasicTags(
304+
StringEscapeUtils.escapeHtml4(
305+
String.format(I18nProperties.getString(Strings.classificationInfoNumberText), DataHelper.parseNumberToString(requirementsNumber))));
302306
}
303307

304308
/**
@@ -308,7 +312,7 @@ private static String createCriteriaSurroundingDiv(String content) {
308312

309313
//@formatter:off
310314
return "<div class='criteria'>"
311-
+ StringEscapeUtils.escapeHtml4(content)
315+
+ content
312316
+ "</div>";
313317
//@formatter:on
314318
}
@@ -320,16 +324,17 @@ private static String createSubCriteriaSurroundingDiv(String content) {
320324

321325
//@formatter:off
322326
return "<div class='sub-criteria'><div class='sub-criteria-content'>"
323-
+ StringEscapeUtils.escapeHtml4(content)
327+
+ content
324328
+ "</div></div>";
325329
//@formatter:on
326330
}
327331

328332
/**
329333
* Creates the div for an actual criteria containing its description.
334+
* Specific tags are allowed to be contained in i18n strings and are thus unescaped
330335
*/
331336
private static String createCriteriaItemDiv(String text) {
332-
return StringEscapeUtils.escapeHtml4(text) + "<br/>";
337+
return unescapeBasicTags(StringEscapeUtils.escapeHtml4(text) + "<br>");
333338
}
334339

335340
private enum ClassificationCriteriaType {
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package de.symeda.sormas.api.utils;
2+
3+
import org.apache.commons.text.StringEscapeUtils;
4+
5+
public class HtmlHelper {
6+
7+
// unescape specific tags (<b>,<i>,<br>,<li>,<ul>) escaped using StringEscapeUtils.escapeHtml4(String)
8+
public static String unescapeBasicTags(String escapedString) {
9+
String res = escapedString;
10+
11+
// <b> Tags
12+
res = res.replaceAll("&lt;b&gt;", "<b>");
13+
res = res.replaceAll("&lt;/b&gt;", "</b>");
14+
// <i> Tags
15+
res = res.replaceAll("&lt;i&gt;", "<i>");
16+
res = res.replaceAll("&lt;/i&gt;", "</i>");
17+
// <ul> Tags
18+
res = res.replaceAll("&lt;ul&gt;", "<ul>");
19+
res = res.replaceAll("&lt;/ul&gt;", "</ul>");
20+
// <li> Tags
21+
res = res.replaceAll("&lt;li&gt;", "<li>");
22+
res = res.replaceAll("&lt;/li&gt;", "</li>");
23+
// <br> Tags
24+
res = res.replaceAll("&lt;br&gt;", "<br>");
25+
res = res.replaceAll("&lt;/br&gt;", "<br>");
26+
res = res.replaceAll("&lt;br/&gt;", "<br>");
27+
28+
return res;
29+
}
30+
31+
// escapes html4 and then unescapes specific tags
32+
public static String escapeAndUnescapeBasicTags(String text) {
33+
return unescapeBasicTags(StringEscapeUtils.escapeHtml4(text));
34+
}
35+
}

sormas-ui/src/main/java/de/symeda/sormas/ui/caze/CaseController.java

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,29 +1084,37 @@ public void openClassificationRulesPopup(DiseaseClassificationCriteriaDto diseas
10841084
classificationRulesLayout.setMargin(true);
10851085

10861086
if (diseaseCriteria != null) {
1087-
Label suspectContent = new Label();
1088-
suspectContent.setContentMode(ContentMode.HTML);
1089-
suspectContent.setWidth(100, Unit.PERCENTAGE);
1090-
suspectContent.setValue(ClassificationHtmlRenderer.createSuspectHtmlString(diseaseCriteria));
1091-
classificationRulesLayout.addComponent(suspectContent);
1092-
1093-
Label probableContent = new Label();
1094-
probableContent.setContentMode(ContentMode.HTML);
1095-
probableContent.setWidth(100, Unit.PERCENTAGE);
1096-
probableContent.setValue(ClassificationHtmlRenderer.createProbableHtmlString(diseaseCriteria));
1097-
classificationRulesLayout.addComponent(probableContent);
1098-
1099-
Label confirmedContent = new Label();
1100-
confirmedContent.setContentMode(ContentMode.HTML);
1101-
confirmedContent.setWidth(100, Unit.PERCENTAGE);
1102-
confirmedContent.setValue(ClassificationHtmlRenderer.createConfirmedHtmlString(diseaseCriteria));
1103-
classificationRulesLayout.addComponent(confirmedContent);
1104-
1105-
Label notACaseContent = new Label();
1106-
notACaseContent.setContentMode(ContentMode.HTML);
1107-
notACaseContent.setWidth(100, Unit.PERCENTAGE);
1108-
notACaseContent.setValue(ClassificationHtmlRenderer.createNotACaseHtmlString(diseaseCriteria));
1109-
classificationRulesLayout.addComponent(notACaseContent);
1087+
if (diseaseCriteria.getSuspectCriteria() != null) {
1088+
Label suspectContent = new Label();
1089+
suspectContent.setContentMode(ContentMode.HTML);
1090+
suspectContent.setWidth(100, Unit.PERCENTAGE);
1091+
suspectContent.setValue(ClassificationHtmlRenderer.createSuspectHtmlString(diseaseCriteria));
1092+
classificationRulesLayout.addComponent(suspectContent);
1093+
}
1094+
1095+
if (diseaseCriteria.getProbableCriteria() != null) {
1096+
Label probableContent = new Label();
1097+
probableContent.setContentMode(ContentMode.HTML);
1098+
probableContent.setWidth(100, Unit.PERCENTAGE);
1099+
probableContent.setValue(ClassificationHtmlRenderer.createProbableHtmlString(diseaseCriteria));
1100+
classificationRulesLayout.addComponent(probableContent);
1101+
}
1102+
1103+
if (diseaseCriteria.getConfirmedCriteria() != null) {
1104+
Label confirmedContent = new Label();
1105+
confirmedContent.setContentMode(ContentMode.HTML);
1106+
confirmedContent.setWidth(100, Unit.PERCENTAGE);
1107+
confirmedContent.setValue(ClassificationHtmlRenderer.createConfirmedHtmlString(diseaseCriteria));
1108+
classificationRulesLayout.addComponent(confirmedContent);
1109+
}
1110+
1111+
if (diseaseCriteria.getNotACaseCriteria() != null) {
1112+
Label notACaseContent = new Label();
1113+
notACaseContent.setContentMode(ContentMode.HTML);
1114+
notACaseContent.setWidth(100, Unit.PERCENTAGE);
1115+
notACaseContent.setValue(ClassificationHtmlRenderer.createNotACaseHtmlString(diseaseCriteria));
1116+
classificationRulesLayout.addComponent(notACaseContent);
1117+
}
11101118
}
11111119

11121120
Window popupWindow = VaadinUiUtil.showPopupWindow(classificationRulesLayout);

sormas-ui/src/main/java/de/symeda/sormas/ui/caze/CaseDataForm.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import com.vaadin.server.ThemeResource;
4444
import com.vaadin.server.UserError;
4545
import com.vaadin.ui.Button;
46+
import com.vaadin.ui.GridLayout;
4647
import com.vaadin.ui.Image;
4748
import com.vaadin.ui.Label;
4849
import com.vaadin.ui.Window;
@@ -866,7 +867,10 @@ protected void addFields() {
866867
getField(CaseDataDto.CLASSIFICATION_USER).setVisible(false);
867868
Label classifiedBySystemLabel = new Label(I18nProperties.getCaption(Captions.system));
868869
classifiedBySystemLabel.setCaption(I18nProperties.getPrefixCaption(CaseDataDto.I18N_PREFIX, CaseDataDto.CLASSIFIED_BY));
869-
getContent().addComponent(classifiedBySystemLabel, CLASSIFIED_BY_SYSTEM_LOC);
870+
// ensure correct formatting
871+
GridLayout tempLayout = new GridLayout();
872+
tempLayout.addComponent(classifiedBySystemLabel);
873+
getContent().addComponent(tempLayout, CLASSIFIED_BY_SYSTEM_LOC);
870874
}
871875

872876
updateFollowUpStatusComponents();

sormas-ui/src/main/webapp/VAADIN/themes/sormas/views/classification.scss

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,15 @@
1111

1212
&.main-criteria-suspect {
1313
background: rgba(255, 215, 0, 0.6);
14-
margin-bottom: 16px;
1514
}
1615
&.main-criteria-probable {
1716
background: rgba(255, 140, 0, 0.6);
18-
margin-bottom: 16px;
1917
}
2018
&.main-criteria-confirmed {
2119
background: rgba(255, 0, 0, 0.6);
2220
}
2321
&.main-criteria-not_a_case {
24-
background: rgba(201, 201, 201, 0.6);
22+
background: rgba(160, 160, 160, 0.6);
2523
}
2624
}
2725

0 commit comments

Comments
 (0)