feat(back-end): rich text
This commit is contained in:
@@ -42,6 +42,7 @@ dependencies {
|
|||||||
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
||||||
implementation 'net.codecrete.qrbill:qrbill-generator:3.4.0'
|
implementation 'net.codecrete.qrbill:qrbill-generator:3.4.0'
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-mail'
|
implementation 'org.springframework.boot:spring-boot-starter-mail'
|
||||||
|
implementation 'org.jsoup:jsoup:1.18.3'
|
||||||
implementation platform('org.lwjgl:lwjgl-bom:3.3.4')
|
implementation platform('org.lwjgl:lwjgl-bom:3.3.4')
|
||||||
implementation 'org.lwjgl:lwjgl'
|
implementation 'org.lwjgl:lwjgl'
|
||||||
implementation 'org.lwjgl:lwjgl-assimp'
|
implementation 'org.lwjgl:lwjgl-assimp'
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ import com.printcalculator.service.SlicerService;
|
|||||||
import com.printcalculator.service.media.PublicMediaQueryService;
|
import com.printcalculator.service.media.PublicMediaQueryService;
|
||||||
import com.printcalculator.service.shop.ShopStorageService;
|
import com.printcalculator.service.shop.ShopStorageService;
|
||||||
import com.printcalculator.service.storage.ClamAVService;
|
import com.printcalculator.service.storage.ClamAVService;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.safety.Safelist;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -64,6 +67,10 @@ public class AdminShopProductControllerService {
|
|||||||
private static final Pattern DIACRITICS_PATTERN = Pattern.compile("\\p{M}+");
|
private static final Pattern DIACRITICS_PATTERN = Pattern.compile("\\p{M}+");
|
||||||
private static final Pattern NON_ALPHANUMERIC_PATTERN = Pattern.compile("[^a-z0-9]+");
|
private static final Pattern NON_ALPHANUMERIC_PATTERN = Pattern.compile("[^a-z0-9]+");
|
||||||
private static final Pattern EDGE_DASH_PATTERN = Pattern.compile("(^-+|-+$)");
|
private static final Pattern EDGE_DASH_PATTERN = Pattern.compile("(^-+|-+$)");
|
||||||
|
private static final Safelist PRODUCT_DESCRIPTION_SAFELIST = Safelist.none()
|
||||||
|
.addTags("p", "div", "br", "strong", "b", "em", "i", "u", "ul", "ol", "li", "a")
|
||||||
|
.addAttributes("a", "href")
|
||||||
|
.addProtocols("a", "href", "http", "https", "mailto", "tel");
|
||||||
|
|
||||||
private final ShopProductRepository shopProductRepository;
|
private final ShopProductRepository shopProductRepository;
|
||||||
private final ShopCategoryRepository shopCategoryRepository;
|
private final ShopCategoryRepository shopCategoryRepository;
|
||||||
@@ -613,17 +620,17 @@ public class AdminShopProductControllerService {
|
|||||||
excerpts.put("fr", firstNonBlank(normalizeOptional(payload.getExcerptFr()), fallbackExcerpt));
|
excerpts.put("fr", firstNonBlank(normalizeOptional(payload.getExcerptFr()), fallbackExcerpt));
|
||||||
|
|
||||||
String fallbackDescription = firstNonBlank(
|
String fallbackDescription = firstNonBlank(
|
||||||
normalizeOptional(payload.getDescription()),
|
normalizeRichTextOptional(payload.getDescription()),
|
||||||
normalizeOptional(payload.getDescriptionIt()),
|
normalizeRichTextOptional(payload.getDescriptionIt()),
|
||||||
normalizeOptional(payload.getDescriptionEn()),
|
normalizeRichTextOptional(payload.getDescriptionEn()),
|
||||||
normalizeOptional(payload.getDescriptionDe()),
|
normalizeRichTextOptional(payload.getDescriptionDe()),
|
||||||
normalizeOptional(payload.getDescriptionFr())
|
normalizeRichTextOptional(payload.getDescriptionFr())
|
||||||
);
|
);
|
||||||
Map<String, String> descriptions = new LinkedHashMap<>();
|
Map<String, String> descriptions = new LinkedHashMap<>();
|
||||||
descriptions.put("it", firstNonBlank(normalizeOptional(payload.getDescriptionIt()), fallbackDescription));
|
descriptions.put("it", firstNonBlank(normalizeRichTextOptional(payload.getDescriptionIt()), fallbackDescription));
|
||||||
descriptions.put("en", firstNonBlank(normalizeOptional(payload.getDescriptionEn()), fallbackDescription));
|
descriptions.put("en", firstNonBlank(normalizeRichTextOptional(payload.getDescriptionEn()), fallbackDescription));
|
||||||
descriptions.put("de", firstNonBlank(normalizeOptional(payload.getDescriptionDe()), fallbackDescription));
|
descriptions.put("de", firstNonBlank(normalizeRichTextOptional(payload.getDescriptionDe()), fallbackDescription));
|
||||||
descriptions.put("fr", firstNonBlank(normalizeOptional(payload.getDescriptionFr()), fallbackDescription));
|
descriptions.put("fr", firstNonBlank(normalizeRichTextOptional(payload.getDescriptionFr()), fallbackDescription));
|
||||||
|
|
||||||
String fallbackSeoTitle = firstNonBlank(
|
String fallbackSeoTitle = firstNonBlank(
|
||||||
normalizeOptional(payload.getSeoTitle()),
|
normalizeOptional(payload.getSeoTitle()),
|
||||||
@@ -689,6 +696,27 @@ public class AdminShopProductControllerService {
|
|||||||
return normalized.isBlank() ? null : normalized;
|
return normalized.isBlank() ? null : normalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String normalizeRichTextOptional(String value) {
|
||||||
|
String normalized = normalizeOptional(value);
|
||||||
|
if (normalized == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String sanitized = Jsoup.clean(
|
||||||
|
normalized,
|
||||||
|
"",
|
||||||
|
PRODUCT_DESCRIPTION_SAFELIST,
|
||||||
|
new Document.OutputSettings().prettyPrint(false)
|
||||||
|
).trim();
|
||||||
|
|
||||||
|
if (sanitized.isBlank()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String plainText = Jsoup.parse(sanitized).text();
|
||||||
|
return plainText != null && !plainText.trim().isEmpty() ? sanitized : null;
|
||||||
|
}
|
||||||
|
|
||||||
private String firstNonBlank(String... values) {
|
private String firstNonBlank(String... values) {
|
||||||
if (values == null) {
|
if (values == null) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
Reference in New Issue
Block a user