diff --git a/backend/src/main/java/fr/cgi/magneto/model/Section.java b/backend/src/main/java/fr/cgi/magneto/model/Section.java index 48af01f6..351afd92 100644 --- a/backend/src/main/java/fr/cgi/magneto/model/Section.java +++ b/backend/src/main/java/fr/cgi/magneto/model/Section.java @@ -1,6 +1,7 @@ package fr.cgi.magneto.model; import fr.cgi.magneto.core.constants.Field; +import fr.cgi.magneto.model.cards.Card; import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; @@ -13,6 +14,7 @@ public class Section implements Model { private List cardIds; private String boardId; private Boolean displayed; + private List cards; @SuppressWarnings("unchecked") public Section(JsonObject section) { @@ -22,6 +24,8 @@ public Section(JsonObject section) { this.boardId = section.getString(Field.BOARDID); if (section.containsKey(Field.DISPLAYED)) this.displayed = section.getBoolean(Field.DISPLAYED); + if (section.containsKey(Field.CARDS)) + this.cards = section.getJsonArray(Field.CARDS).getList(); } public Section() { @@ -87,6 +91,15 @@ public void setDisplayed(boolean displayed) { this.displayed = displayed; } + public List getCards() { + return cards; + } + + public Section setCards(List cards) { + this.cards = cards; + return this; + } + @Override public JsonObject toJson() { JsonObject json = new JsonObject() @@ -96,6 +109,8 @@ public JsonObject toJson() { .put(Field.BOARDID, this.getBoardId()); if (this.displayed != null) json.put(Field.DISPLAYED, this.getDisplayed()); + if (this.cards != null) + json.put(Field.CARDS, this.getCards()); return json; } diff --git a/backend/src/main/java/fr/cgi/magneto/model/slides/SlideText.java b/backend/src/main/java/fr/cgi/magneto/model/slides/SlideText.java index 13a0e8b8..3138a7e0 100644 --- a/backend/src/main/java/fr/cgi/magneto/model/slides/SlideText.java +++ b/backend/src/main/java/fr/cgi/magneto/model/slides/SlideText.java @@ -32,22 +32,41 @@ public Object createApacheSlide(XSLFSlide newSlide) { return newSlide; } + private static boolean isBodyEmptyOrContainsEmptyParagraph(Element body) { + // Vérifier si le body est vide + if (body.children().isEmpty()) { + return true; + } + + // Vérifier si le body ne contient qu'une balise

vide + if (body.children().size() == 1) { + Element child = body.child(0); + if (child.tagName().equals("p") && child.html().trim().isEmpty()) { + return true; + } + } + + return false; + } + private void processHtmlContent(XSLFTextBox textBox, Element element) { if (textBox.getTextParagraphs().isEmpty()) { textBox.addNewTextParagraph(); } - for (Node node : element.childNodes()) { - if (node instanceof Element) { - Element elem = (Element) node; - XSLFTextParagraph para = textBox.addNewTextParagraph(); - XSLFTextRun run = para.addNewTextRun(); + if (!isBodyEmptyOrContainsEmptyParagraph(element)) { + for (Node node : element.childNodes()) { + if (node instanceof Element) { + Element elem = (Element) node; + XSLFTextParagraph para = textBox.addNewTextParagraph(); + XSLFTextRun run = para.addNewTextRun(); - processStyle(elem, para, run); + processStyle(elem, para, run); - String text = elem.text().trim(); - if (!text.isEmpty()) { - run.setText(text); + String text = elem.text().trim(); + if (!text.isEmpty()) { + run.setText(text); + } } } } diff --git a/backend/src/main/java/fr/cgi/magneto/service/CardService.java b/backend/src/main/java/fr/cgi/magneto/service/CardService.java index 9272457b..40bec09e 100644 --- a/backend/src/main/java/fr/cgi/magneto/service/CardService.java +++ b/backend/src/main/java/fr/cgi/magneto/service/CardService.java @@ -146,6 +146,16 @@ void removeCardSectionWithLocked(CardPayload updateCard, String oldBoardId, Futu */ Future getAllCardsBySection(Section section, Integer page, UserInfos user); + /** + * Get all cards by section, without the count and returning the list directly + * + * @param section Section object + * @param page Page number + * @param user {@link UserInfos} User info + * @return Future {@link Future >} containing the cards corresponding to the board identifier + */ + Future> getAllCardsBySectionSimple(Section section, Integer page, UserInfos user); + /** * Duplicate cards * diff --git a/backend/src/main/java/fr/cgi/magneto/service/SectionService.java b/backend/src/main/java/fr/cgi/magneto/service/SectionService.java index 4a9b036a..9b241baf 100644 --- a/backend/src/main/java/fr/cgi/magneto/service/SectionService.java +++ b/backend/src/main/java/fr/cgi/magneto/service/SectionService.java @@ -28,9 +28,10 @@ public interface SectionService { */ Future> getSectionsByBoardId(String boardId); + Future> createSectionWithCards(Board board, UserInfos user); + /** - * - * @param board board of the sections + * @param board board of the sections * @param isReadOnly * @return */ diff --git a/backend/src/main/java/fr/cgi/magneto/service/impl/DefaultCardService.java b/backend/src/main/java/fr/cgi/magneto/service/impl/DefaultCardService.java index ca775fe8..4792413a 100644 --- a/backend/src/main/java/fr/cgi/magneto/service/impl/DefaultCardService.java +++ b/backend/src/main/java/fr/cgi/magneto/service/impl/DefaultCardService.java @@ -807,6 +807,22 @@ public Future getAllCardsBySection(Section section, Integer page, Us return promise.future(); } + @Override + public Future> getAllCardsBySectionSimple(Section section, Integer page, UserInfos user) { + Promise> promise = Promise.promise(); + + fetchAllCardsBySection(section, page, user) + .compose(this::setMetadataCards) + .onFailure(fail -> { + log.error("[Magneto@%s::getAllCardsBySectionSimple] Failed to get section cards", this.getClass().getSimpleName(), + fail.getMessage()); + promise.fail(fail.getMessage()); + }) + .onSuccess(promise::complete); + + return promise.future(); + } + public Future> getAllCardsByCreationDate(StatisticsPayload statisticsPayload) { Promise> promise = Promise.promise(); diff --git a/backend/src/main/java/fr/cgi/magneto/service/impl/DefaultExportService.java b/backend/src/main/java/fr/cgi/magneto/service/impl/DefaultExportService.java index 10d4d487..869c6022 100644 --- a/backend/src/main/java/fr/cgi/magneto/service/impl/DefaultExportService.java +++ b/backend/src/main/java/fr/cgi/magneto/service/impl/DefaultExportService.java @@ -1,23 +1,12 @@ package fr.cgi.magneto.service.impl; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import org.apache.poi.openxml4j.opc.PackagePart; -import org.apache.poi.xslf.usermodel.XMLSlideShow; -import org.apache.poi.xslf.usermodel.XSLFSlide; -import org.entcore.common.user.UserInfos; - import fr.cgi.magneto.core.constants.Field; import fr.cgi.magneto.core.constants.Slideshow; import fr.cgi.magneto.core.enums.SlideResourceType; import fr.cgi.magneto.factory.SlideFactory; import fr.cgi.magneto.helper.I18nHelper; import fr.cgi.magneto.helper.SlideHelper; +import fr.cgi.magneto.model.Section; import fr.cgi.magneto.model.boards.Board; import fr.cgi.magneto.model.cards.Card; import fr.cgi.magneto.model.properties.SlideProperties; @@ -31,8 +20,13 @@ import io.vertx.core.json.JsonObject; import io.vertx.core.logging.Logger; import io.vertx.core.logging.LoggerFactory; +import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.sl.usermodel.TextParagraph; import org.apache.poi.xslf.usermodel.*; +import org.entcore.common.user.UserInfos; + +import java.util.*; +import java.util.stream.Collectors; public class DefaultExportService implements ExportService { @@ -65,8 +59,10 @@ public Future exportBoardToPPTX(String boardId, UserInfos user, I1 documentIds.add(imageId); return getBoardDocuments(documentIds); }) - .compose(documents -> createFreeLayoutSlideObjects(board, user, slideShow, documents, - i18nHelper)) + .compose(documents -> board.isLayoutFree() + ? createFreeLayoutSlideObjects(board, user, slideShow, documents, + i18nHelper) + : createSectionLayoutSlideObjects(board, user, slideShow, documents, i18nHelper)) .onFailure(err -> { String message = String.format( "[Magneto@%s::exportBoardToPptx] Failed to get documents: %s", @@ -149,6 +145,9 @@ private Future createFreeLayoutSlideObjects(Board board, UserInfos XMLSlideShow ppt = new XMLSlideShow(); ppt.setPageSize(new java.awt.Dimension(1280, 720)); + // TITRE + XSLFSlide newTitleSlide = ppt.createSlide(); + createTitleSlide(newTitleSlide, board, documents, i18nHelper); return serviceFactory.cardService().getAllCardsByBoard(board, user) .map(fetchedCards -> { // Créer une map des cartes récupérées pour un accès rapide @@ -157,10 +156,6 @@ private Future createFreeLayoutSlideObjects(Board board, UserInfos SlideFactory slideFactory = new SlideFactory(); - // TITRE - XSLFSlide newTitleSlide = ppt.createSlide(); - createTitleSlide(newTitleSlide, board, documents, i18nHelper); - // Utiliser l'ordre des cartes du Board for (Card boardCard : board.cards()) { String cardId = boardCard.getId(); @@ -195,6 +190,52 @@ private Future createFreeLayoutSlideObjects(Board board, UserInfos }); } + + private Future createSectionLayoutSlideObjects(Board board, UserInfos user, + JsonObject slideShowData, List> documents, I18nHelper i18nHelper) { + XMLSlideShow ppt = new XMLSlideShow(); + ppt.setPageSize(new java.awt.Dimension(1280, 720)); + + // TITRE + XSLFSlide newTitleSlide = ppt.createSlide(); + createTitleSlide(newTitleSlide, board, documents, i18nHelper); + + SlideFactory slideFactory = new SlideFactory(); + + return this.serviceFactory.sectionService().createSectionWithCards(board, user) + .map(sections -> { + for (Section section : sections) { + // TITRE SECTION + XSLFSlide sectionApacheSlide = ppt.createSlide(); + SlideHelper.createTitle(sectionApacheSlide, section.getTitle(), Slideshow.MAIN_TITLE_HEIGHT, Slideshow.MAIN_TITLE_FONT_SIZE, TextParagraph.TextAlign.CENTER); + + for (Card card : section.getCards()) { + if (card != null) { + try { + Slide slide = createSlideFromCard(card, slideFactory, slideShowData, documents); + XSLFSlide newSlide = ppt.createSlide(); + slide.createApacheSlide(newSlide); + } catch (Exception e) { + String message = String.format( + "[Magneto@%s::createSectionLayoutSlideObjects] Failed to create slide for card %s: %s", + this.getClass().getSimpleName(), card.getId(), e.getMessage()); + log.error(message); + } + } else { + log.warn(String.format("Card %s from board not found in fetched cards", card.getId())); + } + } + } + return ppt; + }) + .onFailure(err -> { + String message = String.format( + "[Magneto@%s::createSectionLayoutSlideObjects] Failed to create slides: %s", + this.getClass().getSimpleName(), err.getMessage()); + log.error(message); + }); + } + private XSLFSlide createTitleSlide(XSLFSlide newTitleSlide, Board board, List> documents, I18nHelper i18nHelper) { diff --git a/backend/src/main/java/fr/cgi/magneto/service/impl/DefaultSectionService.java b/backend/src/main/java/fr/cgi/magneto/service/impl/DefaultSectionService.java index 0f63a253..c93495e5 100644 --- a/backend/src/main/java/fr/cgi/magneto/service/impl/DefaultSectionService.java +++ b/backend/src/main/java/fr/cgi/magneto/service/impl/DefaultSectionService.java @@ -78,6 +78,31 @@ public Future> getSectionsByBoardId(String boardId) { return promise.future(); } + @Override + public Future> createSectionWithCards(Board board, UserInfos user) { + Promise> promise = Promise.promise(); + + List
sections = new ArrayList<>(); + this.serviceFactory.sectionService().getSectionsByBoardId(board.getId()) + .compose(sectionsResult -> { + sections.addAll(sectionsResult); + List futures = new ArrayList<>(); + for (Section section : sections) { + Future> cardsFuture = this.serviceFactory.cardService().getAllCardsBySectionSimple(section, 0, user); + futures.add(cardsFuture.map(cards -> { + section.setCards(cards); + return section; + })); + } + return CompositeFuture.all(futures); + }) + .compose(compositeFuture -> Future.succeededFuture(sections)) + .onSuccess(promise::complete) + .onFailure(promise::fail); + + return promise.future(); + } + @Override public Future> getSectionsByBoard(Board board, boolean isReadOnly) { Promise> promise = Promise.promise();