From 65ad31456f0e28994f3a3d903ec8b91db86a78df Mon Sep 17 00:00:00 2001 From: Helmi Akermi <70575401+hakermi@users.noreply.github.com> Date: Wed, 25 Sep 2024 18:14:36 +0100 Subject: [PATCH] feat: Enhance notes treeview loading perf - EXO-74286 - Meeds-io/MIPs#129 (#1133) Enhance notes treeview loading perf --- .../exoplatform/wiki/jpa/JPADataStorage.java | 16 +- .../wiki/jpa/dao/DraftPageDAO.java | 10 + .../exoplatform/wiki/service/DataStorage.java | 14 +- .../exoplatform/wiki/service/NoteService.java | 9 + .../wiki/service/impl/NoteServiceImpl.java | 17 +- .../wiki/service/rest/NotesRestService.java | 414 +++++++----------- .../exoplatform/wiki/tree/JsonNodeData.java | 20 +- .../exoplatform/wiki/tree/PageTreeNode.java | 7 +- .../exoplatform/wiki/tree/RootTreeNode.java | 4 +- .../exoplatform/wiki/tree/SpaceTreeNode.java | 4 +- .../org/exoplatform/wiki/tree/TreeNode.java | 17 +- .../wiki/tree/WikiHomeTreeNode.java | 10 +- .../exoplatform/wiki/tree/WikiTreeNode.java | 4 +- .../wiki/tree/utils/TreeUtils.java | 203 +++++---- .../wiki/jpa/JPADataStorageTest.java | 10 +- .../wiki/service/TestNoteService.java | 21 +- .../service/rest/NotesRestServiceTest.java | 42 +- .../javascript/eXo/wiki/notesService.js | 26 +- .../notes/components/NoteContentTableItem.vue | 9 +- .../notes/components/NoteTreeviewDrawer.vue | 172 +++++--- .../components/NoteTreeviewItemPrepend.vue | 56 +++ .../notes/components/NotesOverview.vue | 69 ++- .../webapp/vue-app/notes/initComponents.js | 4 +- 23 files changed, 630 insertions(+), 528 deletions(-) create mode 100644 notes-webapp/src/main/webapp/vue-app/notes/components/NoteTreeviewItemPrepend.vue diff --git a/notes-service/src/main/java/org/exoplatform/wiki/jpa/JPADataStorage.java b/notes-service/src/main/java/org/exoplatform/wiki/jpa/JPADataStorage.java index 26380bfbba..cc3ae8ae03 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/jpa/JPADataStorage.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/jpa/JPADataStorage.java @@ -302,7 +302,7 @@ public Page getParentPageOf(Page page) throws WikiException { } @Override - public List getChildrenPageOf(Page page, boolean withDrafts) throws WikiException { + public List getChildrenPageOf(Page page, boolean withDrafts, boolean withChild) throws WikiException { PageEntity pageEntity = pageDAO.getPageOfWikiByName(page.getWikiType(), page.getWikiOwner(), page.getName()); if (pageEntity == null) { throw new WikiException("Cannot get children of page " + page.getWikiType() + ":" + page.getWikiOwner() + ":" @@ -313,7 +313,11 @@ public List getChildrenPageOf(Page page, boolean withDrafts) throws WikiEx List childrenPagesEntities = pageDAO.getChildrenPages(pageEntity); if (childrenPagesEntities != null) { for (PageEntity childPageEntity : childrenPagesEntities) { - childrenPages.add(convertPageEntityToPage(childPageEntity)); + Page childPage = convertPageEntityToPage(childPageEntity); + if (withChild) { + childPage.setHasChild(hasChildren(Long.parseLong(childPage.getId()))); + } + childrenPages.add(childPage); } } @@ -1586,4 +1590,12 @@ public void deleteOrphanDraftPagesByParentPage(long parentPageId) { public PageVersion getPageVersionById(long versionId) { return EntityConverter.convertPageVersionEntityToPageVersion(pageVersionDAO.find(versionId)); } + + /** + * {@inheritDoc} + */ + @Override + public List getDraftsOfWiki(String wikiOwner, String wikiType) { + return convertDraftPageEntitiesToDraftPages(draftPageDAO.findDraftsOfWiki(wikiOwner, wikiType)); + } } diff --git a/notes-service/src/main/java/org/exoplatform/wiki/jpa/dao/DraftPageDAO.java b/notes-service/src/main/java/org/exoplatform/wiki/jpa/dao/DraftPageDAO.java index 0aabbba168..479414671d 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/jpa/dao/DraftPageDAO.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/jpa/dao/DraftPageDAO.java @@ -116,4 +116,14 @@ public Long countDraftPagesByParentPage(long parentPageId) { .setParameter("parentPageId", parentPageId) .getSingleResult(); } + + public List findDraftsOfWiki(String wikiOwner, String wikiType) { + Query query = getEntityManager().createNativeQuery(""" + SELECT * FROM WIKI_DRAFT_PAGES wdp WHERE wdp.PARENT_PAGE_ID IN + (SELECT wp.PAGE_ID FROM WIKI_PAGES wp INNER JOIN WIKI_WIKIS ww ON wp.WIKI_ID = ww.WIKI_ID + WHERE ww.OWNER=:wikiOwner and ww.type=:wikiType)""", DraftPageEntity.class); + query.setParameter("wikiOwner", wikiOwner); + query.setParameter("wikiType", wikiType); + return query.getResultList(); + } } diff --git a/notes-service/src/main/java/org/exoplatform/wiki/service/DataStorage.java b/notes-service/src/main/java/org/exoplatform/wiki/service/DataStorage.java index 0ed2b71ff9..0eb0caa70b 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/service/DataStorage.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/service/DataStorage.java @@ -80,11 +80,12 @@ public interface DataStorage { * Get children notes and draft notes of page * * @param page the target page to retrieve its children - * @param withDrafts if set to true returns the children notes and draft notes + * @param withDrafts if set to true returns the children notes and draft notes, + * @param withChild Check if note has child * @return children notes of page * @throws WikiException */ - public List getChildrenPageOf(Page page, boolean withDrafts) throws WikiException; + public List getChildrenPageOf(Page page, boolean withDrafts, boolean withChild) throws WikiException; /** * Check if the given note page has children or not @@ -317,4 +318,13 @@ public default List getAttachmentsOfPage(Page page, boolean loadCont * @return {@link PageVersion} */ PageVersion getPageVersionById(long versionId); + + /** + * Gets draft pages of a given wiki + * + * @param wikiOwner wiki owner + * @param wikiType wiki type + * @return {@link List} of {@link DraftPage} + */ + List getDraftsOfWiki(String wikiOwner, String wikiType); } diff --git a/notes-service/src/main/java/org/exoplatform/wiki/service/NoteService.java b/notes-service/src/main/java/org/exoplatform/wiki/service/NoteService.java index 2f80f65f80..b26acedeb8 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/service/NoteService.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/service/NoteService.java @@ -851,4 +851,13 @@ Page getNoteByIdAndLang(Long pageId, Identity userIdentity, String source, Strin * {@inheritDoc} */ DraftPage getDraftOfPageByLang(WikiPageParams param, String lang) throws WikiException; + + /** + * Gets draft pages of a given wiki + * + * @param wikiOwner wiki owner + * @param wikiType wiki type + * @return {@link List} of {@link DraftPage} + */ + List getDraftsOfWiki(String wikiOwner, String wikiType); } diff --git a/notes-service/src/main/java/org/exoplatform/wiki/service/impl/NoteServiceImpl.java b/notes-service/src/main/java/org/exoplatform/wiki/service/impl/NoteServiceImpl.java index 0ccc2ae9f6..60c8c51c58 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/service/impl/NoteServiceImpl.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/service/impl/NoteServiceImpl.java @@ -757,14 +757,7 @@ public NoteToExport getParentNoteOf(NoteToExport note) throws WikiException { */ @Override public List getChildrenNoteOf(Page note, boolean withDrafts, boolean withChild) throws WikiException { - List pages = dataStorage.getChildrenPageOf(note, withDrafts); - if (withChild) { - for (Page page : pages) { - long pageId = Long.parseLong(page.getId()); - page.setHasChild(hasChildren(pageId)); - } - } - return pages; + return dataStorage.getChildrenPageOf(note, withDrafts, withChild); } /** @@ -1742,6 +1735,14 @@ public PageVersion getPageVersionById(Long versionId) { return dataStorage.getPageVersionById(versionId); } + /** + * {@inheritDoc} + */ + @Override + public List getDraftsOfWiki(String wikiOwner, String wikiType) { + return dataStorage.getDraftsOfWiki(wikiOwner, wikiType); + } + // ******* Listeners *******/ public void postUpdatePageVersionLanguage(String versionPageId) { diff --git a/notes-service/src/main/java/org/exoplatform/wiki/service/rest/NotesRestService.java b/notes-service/src/main/java/org/exoplatform/wiki/service/rest/NotesRestService.java index a4cf116ce9..21b8f37aa5 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/service/rest/NotesRestService.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/service/rest/NotesRestService.java @@ -23,17 +23,7 @@ import java.io.InputStream; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.Deque; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Objects; -import java.util.Optional; -import java.util.ResourceBundle; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; import javax.annotation.security.RolesAllowed; @@ -53,10 +43,11 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; -import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; +import org.exoplatform.commons.comparators.NaturalComparator; +import org.exoplatform.wiki.tree.PageTreeNode; import org.gatein.api.EntityNotFoundException; import org.json.simple.JSONArray; import org.json.simple.JSONObject; @@ -150,6 +141,8 @@ public class NotesRestService implements ResourceContainer { private static final long CACHE_DURATION_MILLISECONDS = CACHE_DURATION_SECONDS * 1000L; private static final CacheControl ILLUSTRATION_CACHE_CONTROL = new CacheControl(); + + private static final String DRAFTS_NOTE_TYPE = "drafts"; static { ILLUSTRATION_CACHE_CONTROL.setMaxAge(CACHE_DURATION_SECONDS); @@ -1153,109 +1146,22 @@ public Response importNote(@Parameter(description = "Note id", required = true) } @GET - @Path("/tree/{type}") + @Path("/tree/{treeType}/{noteType}") @RolesAllowed("users") @Produces(MediaType.APPLICATION_JSON) - @Operation(summary = "Get node's tree", method = "GET", description = "Display the current tree of a noteBook based on is path") + @Operation(summary = "Get node's tree data", method = "GET", description = "Display the current tree of a noteBook based on is path") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Request fulfilled"), @ApiResponse(responseCode = "400", description = "Invalid query input"), @ApiResponse(responseCode = "403", description = "Unauthorized operation"), @ApiResponse(responseCode = "404", description = "Resource not found") }) - public Response getTreeData(@PathParam("type") - String type, @QueryParam(TreeNode.PATH) - String path, @QueryParam(TreeNode.CURRENT_PATH) - String currentPath, @QueryParam(TreeNode.CAN_EDIT) - Boolean canEdit, @QueryParam(TreeNode.SHOW_EXCERPT) - Boolean showExcerpt, @QueryParam("lang") - String lang, @QueryParam(TreeNode.DEPTH) - String depth) { + public Response getNoteTreeData(@Parameter(description = "Tree of selected path children or all notes") @PathParam("treeType") String treeType, + @Parameter(description = "Tree of drafts or notes") @PathParam("noteType") String noteType, + @Parameter(description = "Note path", required = true) @QueryParam(TreeNode.PATH) String path) { try { + boolean withDrafts = Objects.equals(noteType, DRAFTS_NOTE_TYPE); Identity identity = ConversationState.getCurrent().getIdentity(); - List responseData = new ArrayList(); - HashMap context = new HashMap(); - context.put(TreeNode.CAN_EDIT, canEdit); - if (currentPath != null) { - currentPath = URLDecoder.decode(currentPath, StandardCharsets.UTF_8); - context.put(TreeNode.CURRENT_PATH, currentPath); - WikiPageParams currentNoteParam = TreeUtils.getPageParamsFromPath(currentPath); - Page currentNote = noteService.getNoteOfNoteBookByName(currentNoteParam.getType(), - currentNoteParam.getOwner(), - currentNoteParam.getPageName(), - identity); - context.put(TreeNode.CURRENT_PAGE, currentNote); - } - - EnvironmentContext env = EnvironmentContext.getCurrent(); - HttpServletRequest request = (HttpServletRequest) env.get(HttpServletRequest.class); - - // Put select note to context - path = URLDecoder.decode(path, StandardCharsets.UTF_8); - context.put(TreeNode.PATH, path); - WikiPageParams noteParam = TreeUtils.getPageParamsFromPath(path); - Page note = - noteService.getNoteOfNoteBookByName(noteParam.getType(), noteParam.getOwner(), noteParam.getPageName(), identity); - if (note == null) { - log.warn("User [{}] can not get noteBook path [{}]. Home is used instead", - ConversationState.getCurrent().getIdentity().getUserId(), - path); - note = noteService.getNoteOfNoteBookByName(noteParam.getType(), noteParam.getOwner(), NoteConstants.NOTE_HOME_NAME); - if (note == null) { - ResourceBundle resourceBundle = resourceBundleService.getResourceBundle("locale.portlet.wiki.WikiPortlet", - request.getLocale()); - String errorMessage = ""; - if (resourceBundle != null) { - errorMessage = resourceBundle.getString("UIWikiMovePageForm.msg.no-permission-at-wiki-destination"); - } - return Response.serverError().entity("{ \"message\": \"" + errorMessage + "\"}").cacheControl(cc).build(); - } - } - - context.put(TreeNode.SELECTED_PAGE, note); - - context.put(TreeNode.SHOW_EXCERPT, showExcerpt); - if (type.equalsIgnoreCase(TREETYPE.ALL.toString())) { - Deque stk = Utils.getStackParams(note); - context.put(TreeNode.STACK_PARAMS, stk); - responseData = getJsonTree(noteParam, context); - } else if (type.equalsIgnoreCase(TREETYPE.CHILDREN.toString())) { - // Get children only - if (depth == null) - depth = "1"; - context.put(TreeNode.DEPTH, depth); - responseData = getJsonDescendants(noteParam, context); - } - - encodeWikiTree(responseData, request.getLocale(), identity, false); - BeanToJsons toJsons = new BeanToJsons<>(responseData); - return Response.ok(toJsons, MediaType.APPLICATION_JSON).cacheControl(cc).build(); - } catch (IllegalAccessException e) { - log.error("User does not have view permissions on the note {}", path, e); - return Response.status(Response.Status.UNAUTHORIZED).build(); - } catch (Exception e) { - log.error("Failed for get tree data by rest service - Cause : " + e.getMessage(), e); - return Response.serverError().entity(e.getMessage()).cacheControl(cc).build(); - } - } - - @GET - @Path("/tree/full") - @RolesAllowed("users") - @Produces(MediaType.APPLICATION_JSON) - @Operation(summary = "Get node's tree", method = "GET", description = "Display the current tree of a noteBook based on is path") - @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Request fulfilled"), - @ApiResponse(responseCode = "400", description = "Invalid query input"), - @ApiResponse(responseCode = "403", description = "Unauthorized operation"), - @ApiResponse(responseCode = "404", description = "Resource not found") }) - public Response getFullTreeData(@Parameter(description = "Note path", required = true) - @QueryParam(TreeNode.PATH) - String path, - @Parameter(description = "With draft notes", required = true) - @QueryParam("withDrafts") - Boolean withDrafts) { - try { - Identity identity = ConversationState.getCurrent().getIdentity(); - List responseData; - HashMap context = new HashMap<>(); + List responseData = new ArrayList<>(); + Map context = new HashMap<>(); context.put(TreeNode.WITH_DRAFTS, withDrafts); EnvironmentContext env = EnvironmentContext.getCurrent(); @@ -1289,134 +1195,31 @@ public Response getFullTreeData(@Parameter(description = "Note path", required = Deque stk = Utils.getStackParams(note); context.put(TreeNode.STACK_PARAMS, stk); - List finalTree = new ArrayList<>(); - responseData = getJsonTree(noteParam, context); - JsonNodeData rootNodeData = responseData.getFirst(); - rootNodeData.setHasDraftDescendant(true); - finalTree.add(rootNodeData); - context.put(TreeNode.DEPTH, "1"); - - List listChildren = rootNodeData.getChildren(); - List children = listChildren != null ? new ArrayList<>(listChildren) : new ArrayList<>(); - List parents = new ArrayList<>(); - - do { - parents.addAll(children); - children.clear(); - for (JsonNodeData parent : parents) { - if (parent.isHasChild()) { - // Put select note to context - path = URLDecoder.decode(parent.getPath(), StandardCharsets.UTF_8); - context.put(TreeNode.PATH, path); - noteParam = TreeUtils.getPageParamsFromPath(path); - try { - Page parentNote = noteService.getNoteOfNoteBookByName(noteParam.getType(), - noteParam.getOwner(), - noteParam.getPageName(), - identity); - context.put(TreeNode.SELECTED_PAGE, parentNote); - } catch (EntityNotFoundException e) { - log.warn("Cannot find the note {}", noteParam.getPageName()); - } - List childNotes = getJsonDescendants(noteParam, context); - - children.addAll(childNotes); - parent.setChildren(childNotes); - } - finalTree.add(parent); - } - parents.clear(); - - } while (!children.isEmpty()); - - // from the bottom children nodes - List bottomChildren = - Boolean.TRUE.equals(withDrafts) ? finalTree.stream() - .filter(JsonNodeData::isDraftPage) - .collect(Collectors.toList()) - : finalTree.stream() - .filter(jsonNodeData -> !jsonNodeData.isHasChild()) - .collect(Collectors.toList()); - - // prepare draft note nodes tree - if (Boolean.TRUE.equals(withDrafts)) { - bottomChildren = TreeUtils.cleanDraftChildren(bottomChildren,request.getLocale()); - for (JsonNodeData child : bottomChildren) { - JsonNodeData parent; - do { - parent = null; - String parentId = child.getParentPageId(); - Optional parentOptional = finalTree.stream() - .filter(jsonNodeData -> StringUtils.equals(jsonNodeData.getNoteId(), - parentId)) - .findFirst(); - if (parentOptional.isPresent()) { - parent = parentOptional.get(); - parent.setHasDraftDescendant(true); - int index = finalTree.indexOf(parent); - finalTree.set(index, parent); - } - child = parent; - - } while (parent != null); - } - finalTree = finalTree.stream() - .filter(jsonNodeData -> jsonNodeData.isDraftPage() - || Boolean.TRUE.equals(jsonNodeData.isHasDraftDescendant())) - .collect(Collectors.toList()); + JsonNodeData rootNodeData = null; + List treeNodeData = new ArrayList<>(); + if (withDrafts) { + context.put(TreeNode.DEPTH, "1"); + rootNodeData = getJsonTree(noteParam, context, identity, request.getLocale()).getFirst(); + rootNodeData.setChildren(new ArrayList<>()); + rootNodeData.setHasDraftDescendant(true); + buildNoteDraftsTree(rootNodeData, treeNodeData, noteParam, path, context, identity, request.getLocale()); + } else if (treeType.equalsIgnoreCase(TREETYPE.ALL.toString())) { + responseData = getJsonTree(noteParam, context, identity, request.getLocale()); + rootNodeData = responseData.getFirst(); + buildNotesTree(rootNodeData, treeNodeData, context, identity, request.getLocale()); + } else if (treeType.equalsIgnoreCase(TREETYPE.CHILDREN.toString())) { + context.put(TreeNode.DEPTH, "1"); + responseData = getJsonDescendants(noteParam, context, identity, request.getLocale()); + treeNodeData.addAll(responseData); + } else { + rootNodeData = getJsonTree(noteParam, context, identity, request.getLocale()).getFirst(); + treeNodeData.addAll(rootNodeData.getChildren()); } - while (bottomChildren.size() > 1 || (bottomChildren.size() == 1 && bottomChildren.getFirst().getParentPageId() != null)) { - for (JsonNodeData bottomChild : bottomChildren) { - String parentPageId = bottomChild.getParentPageId(); - Optional parentOptional = finalTree.stream() - .filter(jsonNodeData -> StringUtils.equals(jsonNodeData.getNoteId(), - parentPageId)) - .findFirst(); - if (parentOptional.isPresent()) { - JsonNodeData parent = parentOptional.get(); - - if (!Boolean.TRUE.equals(withDrafts) || Boolean.TRUE.equals(parent.isHasDraftDescendant())) { - children = parent.getChildren(); - if (Boolean.TRUE.equals(withDrafts)) { - children = TreeUtils.cleanDraftChildren(children, request.getLocale()); - children = children.stream() - .filter(jsonNodeData -> jsonNodeData.isDraftPage() - || Boolean.TRUE.equals(jsonNodeData.isHasDraftDescendant())) - .collect(Collectors.toList()); - } - int indexChild = children.indexOf(bottomChild); - children.remove(bottomChild); - - if (!Boolean.TRUE.equals(withDrafts) || bottomChild.isDraftPage() - || Boolean.TRUE.equals(bottomChild.isHasDraftDescendant())) { - children.add(indexChild, bottomChild); - } - parent.setChildren(children); - - // update final tree - if (finalTree.contains(parent)) { - int index = finalTree.indexOf(parent); - finalTree.set(index, parent); - } - - // add node to parents - if (parents.contains(parent)) { - int index = parents.indexOf(parent); - parents.set(index, parent); - } else { - parents.add(parent); - } - } - } - } - bottomChildren.clear(); - bottomChildren.addAll(parents); - parents.clear(); + if (rootNodeData != null) { + responseData = List.of(rootNodeData); } - - encodeWikiTree(bottomChildren, request.getLocale(), identity, withDrafts); - BeanToJsons toJsons = new BeanToJsons<>(finalTree, bottomChildren); + BeanToJsons toJsons = new BeanToJsons<>(responseData, treeNodeData); return Response.ok(toJsons, MediaType.APPLICATION_JSON).cacheControl(cc).build(); } catch (IllegalAccessException e) { log.error("User does not have view permissions on the note {}", path, e); @@ -1614,46 +1417,19 @@ public Response getFeaturedImageIllustration(@Context Request request, } } - private List getJsonTree(WikiPageParams params, HashMap context) throws Exception { + private List getJsonTree(WikiPageParams params, Map context, Identity identity, Locale locale) throws Exception { Wiki noteBook = noteBookService.getWikiByTypeAndOwner(params.getType(), params.getOwner()); WikiTreeNode noteBookNode = new WikiTreeNode(noteBook); noteBookNode.pushDescendants(context, ConversationState.getCurrent().getIdentity().getUserId()); - return TreeUtils.tranformToJson(noteBookNode, context); + return TreeUtils.tranformToJson(noteBookNode, context, identity, locale); } - private List getJsonDescendants(WikiPageParams params, HashMap context) throws Exception { + private List getJsonDescendants(WikiPageParams params, + Map context, + Identity identity, + Locale locale) throws Exception { TreeNode treeNode = TreeUtils.getDescendants(params, context, ConversationState.getCurrent().getIdentity().getUserId()); - return TreeUtils.tranformToJson(treeNode, context); - } - - private void encodeWikiTree(List responseData, - Locale locale, - Identity identity, - boolean withDrafts) throws Exception { - ResourceBundle resourceBundle = resourceBundleService.getResourceBundle(Utils.WIKI_RESOUCE_BUNDLE_NAME, locale); - String untitledLabel = ""; - if (resourceBundle == null) { - // May happen in Tests - log.warn("Cannot find resource bundle '{}'", Utils.WIKI_RESOUCE_BUNDLE_NAME); - } else { - untitledLabel = resourceBundle.getString("Page.Untitled"); - } - - for (JsonNodeData data : responseData) { - if (StringUtils.isBlank(data.getName())) { - data.setName(untitledLabel); - } else { - if (!data.isDraftPage()) { - Page page = noteService.getNoteByIdAndLang(Long.valueOf(data.getNoteId()), identity, "", locale.getLanguage()); - if (page != null) { - data.setName(page.getTitle()); - } - } - } - if (CollectionUtils.isNotEmpty(data.getChildren())) { - encodeWikiTree(data.getChildren(), locale, identity, withDrafts); - } - } + return TreeUtils.tranformToJson(treeNode, context, identity, locale); } private Page updateChildrenContainer(Page note) throws WikiException { @@ -1693,4 +1469,110 @@ private String sanitizeAndSubstituteMentions(String content, String local) { } } + private void buildNotesTree(JsonNodeData rootNode, + List treeNodeData, + Map context, + Identity identity, + Locale locale) throws Exception { + for (JsonNodeData child : rootNode.getChildren()) { + treeNodeData.add(child); + if (child.isHasChild()) { + String path = URLDecoder.decode(child.getPath(), StandardCharsets.UTF_8); + context.put(TreeNode.PATH, path); + WikiPageParams noteParam = TreeUtils.getPageParamsFromPath(path); + try { + Page parentNote = noteService.getNoteOfNoteBookByName(noteParam.getType(), + noteParam.getOwner(), + noteParam.getPageName(), + identity); + context.put(TreeNode.SELECTED_PAGE, parentNote); + } catch (EntityNotFoundException e) { + log.warn("Cannot find the note {}", noteParam.getPageName()); + } + List children = getJsonDescendants(noteParam, context, identity, locale); + child.setChildren(children); + treeNodeData.addAll(children); + } + buildNotesTree(child, treeNodeData, context, identity, locale); + } + } + + private void buildNoteDraftsTree(JsonNodeData rootNode, + List treeNodeData, + WikiPageParams params, + String path, + Map context, + Identity identity, + Locale locale) { + List draftPages = noteService.getDraftsOfWiki(params.getOwner(), params.getType()); + Map> draftsByParentPage = draftPages.stream().map(draftPage -> { + try { + PageTreeNode pageTreeNode = new PageTreeNode(draftPage); + Boolean canEdit = context != null && context.get(TreeNode.CAN_EDIT) != null && (Boolean) context.get(TreeNode.CAN_EDIT); + return TreeUtils.toJsonNodeData(pageTreeNode, path, draftPage, context, canEdit, true, identity, locale, noteService); + } catch (Exception e) { + return null; + }}).filter(Objects::nonNull).collect(Collectors.groupingBy( + JsonNodeData::getParentPageId, + Collectors.collectingAndThen( + Collectors.toList(), + list -> list.stream() + .sorted((x, y) -> new NaturalComparator().compare(x.getName(), y.getName())) + .collect(Collectors.toList())))); + draftsByParentPage.forEach((parentPageId, drafts) -> { + try { + drafts = TreeUtils.cleanDraftChildren(drafts, locale); + Page parent = noteService.getNoteById(String.valueOf(parentPageId)); + PageTreeNode pageTreeNode = new PageTreeNode(parent); + Boolean canEdit = context != null && context.get(TreeNode.CAN_EDIT) != null && (Boolean) context.get(TreeNode.CAN_EDIT); + JsonNodeData parentJsonData = TreeUtils.toJsonNodeData(pageTreeNode, + path, + parent, + context, + canEdit, + true, + identity, + locale, + noteService); + if (!Objects.equals(parentJsonData.getNoteId(), rootNode.getNoteId())) { + parentJsonData.setChildren(drafts); + treeNodeData.add(parentJsonData); + buildPageAscendants(rootNode, parentJsonData, treeNodeData, context, identity, locale, path); + } else { + rootNode.addChildren(drafts); + } + } catch (Exception e) { + log.error("Error while building draft tree descendants", e); + } + }); + } + + private void buildPageAscendants(JsonNodeData rootNode, + JsonNodeData parentJsonData, + List treeNodeData, + Map context, + Identity identity, + Locale locale, + String path) throws Exception { + String parentPageId = parentJsonData.getParentPageId(); + if (parentPageId != null && !parentPageId.equals(rootNode.getNoteId())) { + Page parent = noteService.getNoteById(parentJsonData.getParentPageId()); + PageTreeNode pageTreeNode = new PageTreeNode(parent); + Boolean canEdit = context != null && context.get(TreeNode.CAN_EDIT) != null && (Boolean) context.get(TreeNode.CAN_EDIT); + JsonNodeData parentNode = TreeUtils.toJsonNodeData(pageTreeNode, + path, + parent, + context, + canEdit, + true, + identity, + locale, + noteService); + parentNode.addChildren(List.of(parentJsonData)); + treeNodeData.add(parentNode); + buildPageAscendants(rootNode, parentNode, treeNodeData, context, identity, locale, path); + } else { + rootNode.addChildren(List.of(parentJsonData)); + } + } } diff --git a/notes-service/src/main/java/org/exoplatform/wiki/tree/JsonNodeData.java b/notes-service/src/main/java/org/exoplatform/wiki/tree/JsonNodeData.java index 1c54e4798a..1ec248f77a 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/tree/JsonNodeData.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/tree/JsonNodeData.java @@ -20,11 +20,14 @@ package org.exoplatform.wiki.tree; import java.net.URLEncoder; -import java.util.HashMap; +import java.util.ArrayList; import java.util.List; +import java.util.Locale; +import java.util.Map; import lombok.Data; import org.apache.commons.lang3.StringUtils; +import org.exoplatform.services.security.Identity; import org.exoplatform.wiki.model.DraftPage; import org.exoplatform.wiki.model.Page; import org.exoplatform.wiki.tree.utils.TreeUtils; @@ -68,7 +71,7 @@ public class JsonNodeData { private String targetPageId; - private Boolean hasDraftDescendant; + private Boolean hasDraftDescendant; private String lang; @@ -79,7 +82,9 @@ public JsonNodeData(TreeNode treeNode, boolean isLastNode, boolean isSelectable, String currentPath, String excerpt, - HashMap context) throws Exception { + Map context, + Identity identity, + Locale locale) throws Exception { this.name = treeNode.getName(); this.noteId = treeNode.getId(); if (treeNode instanceof WikiHomeTreeNode){ @@ -96,7 +101,7 @@ public JsonNodeData(TreeNode treeNode, this.isLastNode = isLastNode; this.isSelectable = isSelectable; this.excerpt = excerpt; - this.children = TreeUtils.tranformToJson(treeNode, context); + this.children = TreeUtils.tranformToJson(treeNode, context, identity, locale); this.isSelected = treeNode.isSelected(); this.isRestricted = treeNode.isRetricted; if (this.children != null && !this.children.isEmpty()) { @@ -141,4 +146,11 @@ public String getTargetPageId() { public void setTargetPageId(String targetPageId) { this.targetPageId = targetPageId; } + + public void addChildren(List children) { + if (this.children == null) { + this.children = new ArrayList(); + } + this.children.addAll(children); + } } diff --git a/notes-service/src/main/java/org/exoplatform/wiki/tree/PageTreeNode.java b/notes-service/src/main/java/org/exoplatform/wiki/tree/PageTreeNode.java index 208b22427d..27f56c727c 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/tree/PageTreeNode.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/tree/PageTreeNode.java @@ -20,8 +20,8 @@ package org.exoplatform.wiki.tree; import java.util.Collection; -import java.util.HashMap; import java.util.Iterator; +import java.util.Map; import lombok.Getter; import lombok.Setter; @@ -59,12 +59,11 @@ public PageTreeNode(Page page) throws Exception { this.page = page; this.id = page.getId(); this.path = buildPath(); - this.hasChild = !page.isDraftPage() && (noteService.hasChildren(Long.parseLong(page.getId())) - || noteService.hasDrafts(Long.parseLong(page.getId()))); + this.hasChild = !page.isDraftPage() && noteService.hasChildren(Long.parseLong(page.getId())); } @Override - protected void addChildren(HashMap context, String userId) throws Exception { + protected void addChildren(Map context, String userId) throws Exception { boolean withDrafts = context.containsKey(TreeNode.WITH_DRAFTS) && (boolean) context.get(TreeNode.WITH_DRAFTS); Collection pages = noteService.getChildrenNoteOf(page, withDrafts, false); Iterator childPageIterator = pages.iterator(); diff --git a/notes-service/src/main/java/org/exoplatform/wiki/tree/RootTreeNode.java b/notes-service/src/main/java/org/exoplatform/wiki/tree/RootTreeNode.java index aae757bedf..567dec4b3a 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/tree/RootTreeNode.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/tree/RootTreeNode.java @@ -16,7 +16,7 @@ */ package org.exoplatform.wiki.tree; -import java.util.HashMap; +import java.util.Map; public class RootTreeNode extends TreeNode { @@ -25,7 +25,7 @@ public RootTreeNode() { } @Override - protected void addChildren(HashMap context, String userId) throws Exception { + protected void addChildren(Map context, String userId) throws Exception { SpaceTreeNode portalNode = new SpaceTreeNode("portal"); SpaceTreeNode groupNode = new SpaceTreeNode("group"); diff --git a/notes-service/src/main/java/org/exoplatform/wiki/tree/SpaceTreeNode.java b/notes-service/src/main/java/org/exoplatform/wiki/tree/SpaceTreeNode.java index c98f524007..b309b75433 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/tree/SpaceTreeNode.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/tree/SpaceTreeNode.java @@ -27,8 +27,8 @@ import org.exoplatform.wiki.tree.utils.TreeUtils; import java.util.Collection; -import java.util.HashMap; import java.util.Iterator; +import java.util.Map; public class SpaceTreeNode extends TreeNode { @@ -50,7 +50,7 @@ public SpaceTreeNode(String name) throws Exception { } @Override - protected void addChildren(HashMap context, String userId) throws Exception { + protected void addChildren(Map context, String userId) throws Exception { try { WikiType wikiType = WikiType.valueOf(name.toUpperCase()); Collection wikis = wikiService.getWikisByType(wikiType.toString()); diff --git a/notes-service/src/main/java/org/exoplatform/wiki/tree/TreeNode.java b/notes-service/src/main/java/org/exoplatform/wiki/tree/TreeNode.java index 357257f1b2..4b3cc03104 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/tree/TreeNode.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/tree/TreeNode.java @@ -26,10 +26,7 @@ import org.exoplatform.wiki.service.WikiPageParams; import org.exoplatform.wiki.utils.Utils; -import java.util.ArrayList; -import java.util.Deque; -import java.util.HashMap; -import java.util.List; +import java.util.*; public class TreeNode { @@ -191,16 +188,16 @@ public boolean equals(Object obj) { return true; } - public void pushDescendants(HashMap context, String userId) throws Exception { + public void pushDescendants(Map context, String userId) throws Exception { addChildren(context, userId); pushChildren(context, userId); this.children = this.children.stream().sorted((childItem1, childItem2) -> new NaturalComparator().compare(childItem1.getName(), childItem2.getName())).toList(); } - protected void addChildren(HashMap context, String userId) throws Exception { + protected void addChildren(Map context, String userId) throws Exception { } - protected int getNumberOfChildren(HashMap context, int size) { + protected int getNumberOfChildren(Map context, int size) { String childNumCdt = (String) context.get(CHILDREN_NUMBER); int childrenNUm = (childNumCdt == null || StringUtils.EMPTY.equals(childNumCdt)) ? -1 : Integer.valueOf(childNumCdt); @@ -214,7 +211,7 @@ protected int getNumberOfChildren(HashMap context, int size) { return childrenNUm; } - private void pushChildren(HashMap context, String userId) throws Exception { + private void pushChildren(Map context, String userId) throws Exception { Deque paramsStk = (Deque) context.get(STACK_PARAMS); if (paramsStk == null) { pushChild(context, userId); @@ -243,7 +240,7 @@ private void pushChildren(HashMap context, String userId) throws } } - private void pushChild(TreeNode child, HashMap context, String userId) throws Exception { + private void pushChild(TreeNode child, Map context, String userId) throws Exception { Boolean showDesCdt = (Boolean) context.get(SHOW_DESCENDANT); String depthCdt = (String) context.get(DEPTH); @@ -268,7 +265,7 @@ private void pushChild(TreeNode child, HashMap context, String u } } - private void pushChild(HashMap context, String userId) throws Exception { + private void pushChild(Map context, String userId) throws Exception { pushChild(null, context, userId); } diff --git a/notes-service/src/main/java/org/exoplatform/wiki/tree/WikiHomeTreeNode.java b/notes-service/src/main/java/org/exoplatform/wiki/tree/WikiHomeTreeNode.java index 5cb2a6349b..55373dae81 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/tree/WikiHomeTreeNode.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/tree/WikiHomeTreeNode.java @@ -20,10 +20,7 @@ package org.exoplatform.wiki.tree; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; +import java.util.*; import org.exoplatform.container.ExoContainerContext; import org.exoplatform.services.log.ExoLogger; @@ -61,12 +58,11 @@ public WikiHomeTreeNode(Page wikiHome) throws Exception { this.wikiHome = wikiHome; this.path = this.buildPath(); - this.hasChild = !wikiHome.isDraftPage() && (noteService.hasChildren(Long.parseLong(wikiHome.getId())) - || noteService.hasDrafts(Long.parseLong(wikiHome.getId()))); + this.hasChild = !wikiHome.isDraftPage() && noteService.hasChildren(Long.parseLong(wikiHome.getId())); } @Override - protected void addChildren(HashMap context, String userId) throws Exception { + protected void addChildren(Map context, String userId) throws Exception { boolean withDrafts = context.containsKey(TreeNode.WITH_DRAFTS) && (boolean) context.get(TreeNode.WITH_DRAFTS); Collection pages = noteService.getChildrenNoteOf(wikiHome, withDrafts, false); Iterator childPageIterator = pages.iterator(); diff --git a/notes-service/src/main/java/org/exoplatform/wiki/tree/WikiTreeNode.java b/notes-service/src/main/java/org/exoplatform/wiki/tree/WikiTreeNode.java index 4893f06b6d..b788ee1bb4 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/tree/WikiTreeNode.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/tree/WikiTreeNode.java @@ -23,7 +23,7 @@ import org.exoplatform.wiki.service.WikiPageParams; import org.exoplatform.wiki.tree.utils.TreeUtils; -import java.util.HashMap; +import java.util.Map; public class WikiTreeNode extends TreeNode { private Wiki wiki; @@ -40,7 +40,7 @@ public WikiHomeTreeNode getWikiHomeTreeNode() { } @Override - protected void addChildren(HashMap context, String userId) throws Exception { + protected void addChildren(Map context, String userId) throws Exception { this.children.add(new WikiHomeTreeNode(wiki.getWikiHome())); super.addChildren(context, userId); diff --git a/notes-service/src/main/java/org/exoplatform/wiki/tree/utils/TreeUtils.java b/notes-service/src/main/java/org/exoplatform/wiki/tree/utils/TreeUtils.java index 2ff183e566..27e3f048fd 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/tree/utils/TreeUtils.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/tree/utils/TreeUtils.java @@ -29,7 +29,9 @@ import org.exoplatform.portal.config.model.PortalConfig; import org.exoplatform.portal.localization.LocaleContextInfoUtils; import org.exoplatform.services.organization.OrganizationService; +import org.exoplatform.services.resources.ResourceBundleService; import org.exoplatform.services.security.ConversationState; +import org.exoplatform.services.security.Identity; import org.exoplatform.wiki.model.Page; import org.exoplatform.wiki.model.PermissionType; import org.exoplatform.wiki.model.Wiki; @@ -41,6 +43,8 @@ public class TreeUtils { + private static ResourceBundleService resourceBundleService; + /** * Create a tree node with a given {@link WikiPageParams} * @@ -79,64 +83,43 @@ public static TreeNode getTreeNode(WikiPageParams params) throws Exception { * @return TreeNode * @throws Exception if error occured */ - public static TreeNode getDescendants(WikiPageParams params, HashMap context, String userId) throws Exception { + public static TreeNode getDescendants(WikiPageParams params, Map context, String userId) throws Exception { TreeNode treeNode = getTreeNode(params); treeNode.pushDescendants(context, userId); return treeNode; } - public static List tranformToJson(TreeNode treeNode, HashMap context) throws Exception { + public static List tranformToJson(TreeNode treeNode, + Map context, + Identity identity, + Locale locale) throws Exception { NoteService noteService = ExoContainerContext.getCurrentContainer().getComponentInstanceOfType(NoteService.class); int counter = 1; Boolean showExcerpt = false; Page currentPage = null; String currentPath = null; - Boolean canEdit = false; + Boolean canEdit = false; if (context != null) { currentPath = (String) context.get(TreeNode.CURRENT_PATH); currentPage = (Page) context.get(TreeNode.CURRENT_PAGE); showExcerpt = (Boolean) context.get(TreeNode.SHOW_EXCERPT); - canEdit = (Boolean)context.get(TreeNode.CAN_EDIT); + canEdit = (Boolean)context.get(TreeNode.CAN_EDIT); } List children = new ArrayList<>(); for (TreeNode child : treeNode.getChildren()) { - boolean isSelectable = true; boolean isLastNode = counter >= treeNode.getChildren().size(); - - if (child.getNodeType().equals(TreeNodeType.WIKI)) { - isSelectable = false; - } else if (child.getNodeType().equals(TreeNodeType.PAGE)) { - Page page = ((PageTreeNode) child).getPage(); - if (((currentPage != null) && (currentPage.equals(page) || Utils.isDescendantPage(page, currentPage)))) { - isSelectable = false; - } - - if (!noteService.hasPermissionOnPage(page, PermissionType.VIEWPAGE, ConversationState.getCurrent().getIdentity())) { - isSelectable = false; - child.setRetricted(true); - } - if (BooleanUtils.isTrue(canEdit) - && !noteService.hasPermissionOnPage(page, PermissionType.EDITPAGE, ConversationState.getCurrent().getIdentity())) { - isSelectable = false; - child.setRetricted(true); - } - } else if (child.getNodeType().equals(TreeNodeType.WIKIHOME)) { - Page page = ((WikiHomeTreeNode) child).getWikiHome(); - if (!noteService.hasPermissionOnPage(page, PermissionType.VIEWPAGE, ConversationState.getCurrent().getIdentity())) { - isSelectable = false; - child.setRetricted(true); - } - - if (BooleanUtils.isTrue(canEdit) - && !noteService.hasPermissionOnPage(page, PermissionType.EDITPAGE, ConversationState.getCurrent().getIdentity())) { - isSelectable = false; - child.setRetricted(true); - } - - } - + JsonNodeData jsonNodeData = toJsonNodeData(child, + currentPath, + currentPage, + context, + canEdit, + isLastNode, + identity, + locale, + noteService); + encodeWikiTreeNode(jsonNodeData, locale, identity, noteService); String excerpt = null; if (showExcerpt != null && showExcerpt) { WikiPageParams params = getPageParamsFromPath(child.getPath()); @@ -144,8 +127,8 @@ public static List tranformToJson(TreeNode treeNode, HashMap cleanDraftChildren(List children, Locale locale) { List localesList = new ArrayList<>(LocaleContextInfoUtils.getSupportedLocales()); - List targetList = children.stream().map(JsonNodeData::getTargetPageId).distinct().collect(Collectors.toList()); + Map> childrenByTarget = children.stream() + .filter(jsonNodeData -> jsonNodeData.getTargetPageId() != null) + .collect(Collectors.groupingBy(JsonNodeData::getTargetPageId)); + List cleanedChildren = children.stream() - .filter(jsonNodeData -> (!jsonNodeData.isDraftPage() || StringUtils.isEmpty(jsonNodeData.getTargetPageId()))) - .collect(Collectors.toList()); - for (String target : targetList) { - if (StringUtils.isNotEmpty(target)) { - List subJsonNodeDataList = - children.stream() - .filter(jsonNodeData -> StringUtils.equals(jsonNodeData.getTargetPageId(), - target)) - .collect(Collectors.toList()); - if (subJsonNodeDataList.size() > 1) { - JsonNodeData currentLangDraft = null; - JsonNodeData originalLangDraft = null; - JsonNodeData anyLangDraft = null; - List subCleanedChildren = new ArrayList<>(); - for (JsonNodeData nodeData : subJsonNodeDataList) { - if (StringUtils.isEmpty(nodeData.getLang())) { - originalLangDraft = nodeData; - } else if (nodeData.getLang().equals(locale.getLanguage())) { - currentLangDraft = nodeData; - } else if (anyLangDraft == null || getLocatedLangDisplayName(localesList, locale, nodeData.getLang()).compareToIgnoreCase(getLocatedLangDisplayName(localesList, locale, anyLangDraft.getLang())) < 0) { - anyLangDraft = nodeData; - } - } - if (currentLangDraft != null) { - subCleanedChildren.add(currentLangDraft); - } else if (originalLangDraft != null) { - subCleanedChildren.add(originalLangDraft); - } else { - subCleanedChildren.add(anyLangDraft); + .filter(jsonNodeData -> !jsonNodeData.isDraftPage() + || StringUtils.isEmpty(jsonNodeData.getTargetPageId())) + .collect(Collectors.toList()); + + for (Map.Entry> entry : childrenByTarget.entrySet()) { + String target = entry.getKey(); + List subJsonNodeDataList = entry.getValue(); + + if (StringUtils.isNotEmpty(target) && subJsonNodeDataList.size() > 1) { + JsonNodeData currentLangDraft = null; + JsonNodeData originalLangDraft = null; + JsonNodeData anyLangDraft = null; + + for (JsonNodeData nodeData : subJsonNodeDataList) { + if (StringUtils.isEmpty(nodeData.getLang())) { + originalLangDraft = nodeData; + } else if (nodeData.getLang().equals(locale.getLanguage())) { + currentLangDraft = nodeData; + } else if (anyLangDraft == null + || getLocatedLangDisplayName(localesList, + locale, + nodeData.getLang()).compareToIgnoreCase(getLocatedLangDisplayName(localesList, locale, anyLangDraft.getLang())) < 0) { + anyLangDraft = nodeData; } - cleanedChildren.addAll(subCleanedChildren); - } else { - cleanedChildren.addAll(subJsonNodeDataList); } + + cleanedChildren.add(Optional.ofNullable(currentLangDraft) + .orElse(Optional.ofNullable(originalLangDraft).orElse(anyLangDraft))); + } else { + cleanedChildren.addAll(subJsonNodeDataList); } } return cleanedChildren; } public static String getLocatedLangDisplayName(List localesList, Locale currentLocale, String lang) { - Optional opLocal = localesList.stream().filter(local -> local.getLanguage().equals(lang)).findAny(); - if (opLocal.isPresent()) { - return opLocal.get().getDisplayName(currentLocale); + for (Locale locale : localesList) { + if (locale.getLanguage().equals(lang)) { + return locale.getDisplayName(currentLocale); + } } return lang; } + + public static JsonNodeData toJsonNodeData(TreeNode node, + String path, + Page currentPage, + Map context, + Boolean canEdit, + Boolean isLastNode, + Identity identity, + Locale locale, + NoteService noteService) throws Exception { + boolean isSelectable = true; + if (node.getNodeType().equals(TreeNodeType.WIKI)) { + isSelectable = false; + } else if (node.getNodeType().equals(TreeNodeType.PAGE)) { + Page page = ((PageTreeNode) node).getPage(); + if (((currentPage != null) && (currentPage.equals(page) || Utils.isDescendantPage(page, currentPage)))) { + isSelectable = false; + } + + if (!noteService.hasPermissionOnPage(page, PermissionType.VIEWPAGE, ConversationState.getCurrent().getIdentity())) { + isSelectable = false; + node.setRetricted(true); + } + if (BooleanUtils.isTrue(canEdit) + && !noteService.hasPermissionOnPage(page, PermissionType.EDITPAGE, ConversationState.getCurrent().getIdentity())) { + isSelectable = false; + node.setRetricted(true); + } + } else if (node.getNodeType().equals(TreeNodeType.WIKIHOME)) { + Page page = ((WikiHomeTreeNode) node).getWikiHome(); + if (!noteService.hasPermissionOnPage(page, PermissionType.VIEWPAGE, ConversationState.getCurrent().getIdentity())) { + isSelectable = false; + node.setRetricted(true); + } + + if (BooleanUtils.isTrue(canEdit) + && !noteService.hasPermissionOnPage(page, PermissionType.EDITPAGE, ConversationState.getCurrent().getIdentity())) { + isSelectable = false; + node.setRetricted(true); + } + + } + JsonNodeData jsonNodeData = new JsonNodeData(node, isLastNode, isSelectable, path, "", context, identity, locale); + encodeWikiTreeNode(jsonNodeData, locale, identity, noteService); + return jsonNodeData; + } + + private static void encodeWikiTreeNode(JsonNodeData nodeData, Locale locale, Identity identity, NoteService noteService) throws Exception { + ResourceBundle resourceBundle = getResourceBundleService().getResourceBundle(Utils.WIKI_RESOUCE_BUNDLE_NAME, locale); + String untitledLabel = resourceBundle.getString("Page.Untitled"); + if (StringUtils.isBlank(nodeData.getName())) { + nodeData.setName(untitledLabel); + } else { + if (!nodeData.isDraftPage()) { + Page page = noteService.getNoteByIdAndLang(Long.valueOf(nodeData.getNoteId()), identity, "", locale.getLanguage()); + if (page != null) { + nodeData.setName(page.getTitle()); + } + } + } + } + + private static ResourceBundleService getResourceBundleService() { + if (resourceBundleService == null) { + resourceBundleService = ExoContainerContext.getCurrentContainer().getComponentInstanceOfType(ResourceBundleService.class); + } + return resourceBundleService; + } } diff --git a/notes-service/src/test/java/org/exoplatform/wiki/jpa/JPADataStorageTest.java b/notes-service/src/test/java/org/exoplatform/wiki/jpa/JPADataStorageTest.java index c4bd2b75b1..6208b72067 100644 --- a/notes-service/src/test/java/org/exoplatform/wiki/jpa/JPADataStorageTest.java +++ b/notes-service/src/test/java/org/exoplatform/wiki/jpa/JPADataStorageTest.java @@ -276,7 +276,7 @@ public void testChildrenPagesOfPage() throws WikiException { storage.createPage(wiki, wiki.getWikiHome(), parentPage); storage.createPage(wiki, parentPage, page1); storage.createPage(wiki, parentPage, page2); - List childrenPages = storage.getChildrenPageOf(parentPage, true); + List childrenPages = storage.getChildrenPageOf(parentPage, true, true); // Then assertEquals(4, pageDAO.findAll().size()); @@ -379,19 +379,19 @@ public void testMovePage() throws WikiException { storage.createPage(wiki1, page1, page11); storage.createPage(wiki1, wiki1.getWikiHome(), page2); assertEquals(5, pageDAO.findAll().size()); - assertEquals(2, storage.getChildrenPageOf(wiki1.getWikiHome(), true).size()); + assertEquals(2, storage.getChildrenPageOf(wiki1.getWikiHome(), true, false).size()); storage.movePage(new WikiPageParams(wiki1.getType(), wiki1.getOwner(), page1.getName()), new WikiPageParams(wiki2.getType(), wiki2.getOwner(), wiki2.getWikiHome().getName())); // Then assertEquals(5, pageDAO.findAll().size()); - assertEquals(1, storage.getChildrenPageOf(wiki1.getWikiHome(), true).size()); - List wiki2HomeChildrenPages = storage.getChildrenPageOf(wiki2.getWikiHome(), true); + assertEquals(1, storage.getChildrenPageOf(wiki1.getWikiHome(), true, false).size()); + List wiki2HomeChildrenPages = storage.getChildrenPageOf(wiki2.getWikiHome(), true, false); assertEquals(1, wiki2HomeChildrenPages.size()); Page movedPage1 = wiki2HomeChildrenPages.get(0); assertEquals("page1", movedPage1.getName()); assertEquals("Page 1", movedPage1.getTitle()); - assertEquals(1, storage.getChildrenPageOf(movedPage1, true).size()); + assertEquals(1, storage.getChildrenPageOf(movedPage1, true, false).size()); Page fetchedPage11 = storage.getPageOfWikiByName(PortalConfig.PORTAL_TYPE, "wiki2", "page11"); assertNotNull(fetchedPage11); assertEquals("page11", fetchedPage11.getName()); diff --git a/notes-service/src/test/java/org/exoplatform/wiki/service/TestNoteService.java b/notes-service/src/test/java/org/exoplatform/wiki/service/TestNoteService.java index b8d091bfcb..658ca69152 100644 --- a/notes-service/src/test/java/org/exoplatform/wiki/service/TestNoteService.java +++ b/notes-service/src/test/java/org/exoplatform/wiki/service/TestNoteService.java @@ -821,7 +821,9 @@ public void testGetNoteFeaturedImageInfo() throws Exception { } public void testCreatePageWithProperties() throws Exception { - Identity user = new Identity("user"); + startSessionAs("root"); + Identity user = new Identity("root"); + identityManager.getOrCreateUserIdentity("root"); this.bindMockedUploadService(); NotePageProperties notePageProperties = createNotePageProperties(0L, "alt text", "summary Test"); DraftPage draftPage = new DraftPage(); @@ -969,4 +971,21 @@ public void testFeaturedImageWhenRemoveDraftById() throws Exception { noteService.removeDraftById(draftPage.getId()); assertTrue(fileService.getFile(featuredImage.getId()).getFileInfo().isDeleted()); } + + public void testGetDraftsOfWiki() throws Exception { + Identity user = new Identity("user"); + Wiki portalWiki = getOrCreateWiki(wService, PortalConfig.PORTAL_TYPE, "testPortal"); + Page note = noteService.createNote(portalWiki, "Home", new Page("testGetDraftsOfWiki", "testGetDraftsOfWiki"), user); + Page note2 = noteService.createNote(portalWiki, "Home", new Page("testGetDraftsOfWiki", "testGetDraftsOfWiki"), user); + DraftPage draftPage = new DraftPage(); + draftPage.setTitle("test"); + draftPage.setContent("test"); + draftPage.setWikiOwner(portalWiki.getId()); + draftPage.setParentPageId(note.getId()); + noteService.createDraftForExistPage(draftPage, note, null, new Date().getTime(), "root"); + draftPage.setParentPageId(note2.getId()); + noteService.createDraftForExistPage(draftPage, note, null, new Date().getTime(), "root"); + assertEquals(2, noteService.getDraftsOfWiki(portalWiki.getOwner(), portalWiki.getType()).size()); + + } } diff --git a/notes-service/src/test/java/org/exoplatform/wiki/service/rest/NotesRestServiceTest.java b/notes-service/src/test/java/org/exoplatform/wiki/service/rest/NotesRestServiceTest.java index d28114c33c..029028f9cd 100644 --- a/notes-service/src/test/java/org/exoplatform/wiki/service/rest/NotesRestServiceTest.java +++ b/notes-service/src/test/java/org/exoplatform/wiki/service/rest/NotesRestServiceTest.java @@ -21,9 +21,7 @@ package org.exoplatform.wiki.service.rest; import static org.mockito.AdditionalAnswers.returnsFirstArg; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyList; -import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; @@ -42,6 +40,9 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; +import org.exoplatform.services.organization.Group; +import org.exoplatform.services.organization.GroupHandler; +import org.exoplatform.services.organization.OrganizationService; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -111,12 +112,13 @@ public class NotesRestServiceTest extends AbstractKernelTest { @Mock private SpaceService spaceService; + @Mock + private OrganizationService organizationService; + private MockedStatic conversationStateStatic; private MockedStatic environmentContextStatic; - private MockedStatic treeUtilsStatic; - private MockedStatic utilsStatic; private MockedStatic REST_UTILS; @@ -131,7 +133,6 @@ public void setUp() throws Exception { super.setUp(); conversationStateStatic = mockStatic(ConversationState.class); environmentContextStatic = mockStatic(EnvironmentContext.class); - treeUtilsStatic = mockStatic(TreeUtils.class); utilsStatic = mockStatic(Utils.class); REST_UTILS = mockStatic(RestUtils.class); ENTITY_BUILDER = mockStatic(EntityBuilder.class); @@ -165,6 +166,8 @@ public void setUp() throws Exception { when(exoContainer.getComponentInstanceOfType(WikiService.class)).thenReturn(noteBookService); when(exoContainer.getComponentInstanceOfType(NoteService.class)).thenReturn(noteService); when(exoContainer.getComponentInstanceOfType(SpaceService.class)).thenReturn(spaceService); + when(exoContainer.getComponentInstanceOfType(OrganizationService.class)).thenReturn(organizationService); + when(exoContainer.getComponentInstanceOfType(ResourceBundleService.class)).thenReturn(resourceBundleService); } @Override @@ -172,7 +175,6 @@ public void setUp() throws Exception { public void tearDown() throws Exception { conversationStateStatic.close(); environmentContextStatic.close(); - treeUtilsStatic.close(); utilsStatic.close(); REST_UTILS.close(); ENTITY_BUILDER.close(); @@ -317,8 +319,8 @@ public void testGetFullTreeData() throws Exception { draftPage.setWikiType("PAGE"); WikiPageParams pageParams = new WikiPageParams(); pageParams.setPageName("home"); - pageParams.setOwner("user"); - pageParams.setType("WIKI"); + pageParams.setOwner("/spaces/test"); + pageParams.setType("group"); List childrenWithDraft = new ArrayList<>(List.of(page, draftPage, page10, page12, page2, page1)); List childrenWithoutDrafts = new ArrayList<>(List.of(page12, page10, page1, page, page2)); // return // an @@ -327,7 +329,10 @@ public void testGetFullTreeData() throws Exception { @SuppressWarnings("unchecked") Deque paramsDeque = mock(Deque.class); when(identity.getUserId()).thenReturn("1"); - treeUtilsStatic.when(() -> TreeUtils.getPageParamsFromPath("path")).thenReturn(pageParams); + GroupHandler groupHandler = mock(GroupHandler.class); + Group group = mock(Group.class); + when(groupHandler.findGroupById(anyString())).thenReturn(null, group, null, group, null, group, null, group); + when (organizationService.getGroupHandler()).thenReturn(groupHandler); utilsStatic.when(() -> Utils.getStackParams(homePage)).thenReturn(paramsDeque); when(paramsDeque.pop()).thenReturn(pageParams); when(noteService.getNoteOfNoteBookByName(pageParams.getType(), @@ -344,9 +349,6 @@ public void testGetFullTreeData() throws Exception { when(noteService.getChildrenNoteOf(homePage, true, false)).thenReturn(childrenWithDraft); when(noteService.getChildrenNoteOf(homePage, false, false)).thenReturn(childrenWithoutDrafts); - treeUtilsStatic.when(() -> TreeUtils.getPathFromPageParams(any())).thenCallRealMethod(); - treeUtilsStatic.when(() -> TreeUtils.tranformToJson(any(), any())).thenCallRealMethod(); - utilsStatic.when(() -> Utils.validateWikiOwner(homePage.getWikiType(), homePage.getWikiOwner())).thenCallRealMethod(); utilsStatic.when(() -> Utils.getObjectFromParams(pageParams)).thenReturn(homePage); utilsStatic.when(() -> Utils.isDescendantPage(homePage, page)).thenReturn(true); @@ -357,15 +359,13 @@ public void testGetFullTreeData() throws Exception { utilsStatic.when(() -> Utils.isDescendantPage(homePage, draftPage)).thenReturn(true); utilsStatic.when(() -> Utils.canManageNotes(anyString(), any(Space.class), any(Page.class))).thenReturn(true); when(spaceService.getSpaceByGroupId(anyString())).thenReturn(mock(Space.class)); - treeUtilsStatic.when(() -> TreeUtils.cleanDraftChildren(anyList(), any())).then(returnsFirstArg()); - Response response = notesRestService.getFullTreeData("path", true); + Response response = notesRestService.getNoteTreeData("all","drafts", "group/spaces/test/home"); assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); - Response response3 = notesRestService.getFullTreeData("path", false); + Response response3 = notesRestService.getNoteTreeData("all", "notes", "group/spaces/test/home"); assertEquals(Response.Status.OK.getStatusCode(), response3.getStatus()); - assertEquals(6, ((BeanToJsons) response3.getEntity()).getJsonList().size()); - List treeNodeList = ((BeanToJsons) response3.getEntity()).getTreeNodeData(); - JsonNodeData jsonNodeData = treeNodeList.get(0); + assertEquals(5, ((JsonNodeData)((BeanToJsons) response3.getEntity()).getJsonList().getFirst()).getChildren().size()); + JsonNodeData jsonNodeData = ((JsonNodeData)((BeanToJsons) response3.getEntity()).getJsonList().getFirst()); assertEquals(5, jsonNodeData.getChildren().size()); assertEquals("testPage", jsonNodeData.getChildren().get(0).getName()); assertEquals("testPage 1", jsonNodeData.getChildren().get(1).getName()); @@ -378,7 +378,7 @@ public void testGetFullTreeData() throws Exception { pageParams.getOwner(), pageParams.getPageName(), identity); - Response response1 = notesRestService.getFullTreeData("path", true); + Response response1 = notesRestService.getNoteTreeData("all","drafts", "group/spaces/test/home"); assertEquals(Response.Status.UNAUTHORIZED.getStatusCode(), response1.getStatus()); doThrow(new RuntimeException()).when(noteService) @@ -386,7 +386,7 @@ public void testGetFullTreeData() throws Exception { pageParams.getOwner(), pageParams.getPageName(), identity); - Response response2 = notesRestService.getFullTreeData("path", true); + Response response2 = notesRestService.getNoteTreeData("all","drafts", "group/spaces/test/home"); assertEquals(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), response2.getStatus()); } diff --git a/notes-webapp/src/main/webapp/javascript/eXo/wiki/notesService.js b/notes-webapp/src/main/webapp/javascript/eXo/wiki/notesService.js index d1a70b5424..d9411c1e04 100644 --- a/notes-webapp/src/main/webapp/javascript/eXo/wiki/notesService.js +++ b/notes-webapp/src/main/webapp/javascript/eXo/wiki/notesService.js @@ -83,11 +83,11 @@ export function getSeparator(url) { return url.indexOf('?') !== -1 ? '&' : '?'; } -export function getNoteTree(noteBookType, noteBookOwner, noteId,treeType) { +export function getNoteTree(noteBookType, noteBookOwner, noteId, treeType, noteType) { if (noteBookOwner.indexOf('/') !== 0) { noteBookOwner = `/${noteBookOwner}`; } - return fetch(`${notesConstants.PORTAL}/${notesConstants.PORTAL_REST}/notes/tree/${treeType}?path=${noteBookType}${noteBookOwner}/${noteId}`, { + return fetch(`${notesConstants.PORTAL}/${notesConstants.PORTAL_REST}/notes/tree/${treeType}/${noteType}?path=${noteBookType}${noteBookOwner}/${noteId}`, { method: 'GET', credentials: 'include', }).then(resp => { @@ -100,7 +100,7 @@ export function getNoteTree(noteBookType, noteBookOwner, noteId,treeType) { } export function getNoteTreeLevel(path) { - return fetch(`${notesConstants.PORTAL}/${notesConstants.PORTAL_REST}/notes/tree/children?path=${path}`, { + return fetch(`${notesConstants.PORTAL}/${notesConstants.PORTAL_REST}/notes/tree/children/notes?path=${path}`, { method: 'GET', credentials: 'include', }).then(resp => { @@ -112,26 +112,6 @@ export function getNoteTreeLevel(path) { }); } -export function getFullNoteTree(noteBookType, noteBookOwner, noteId, withDrafts, lang) { - if (noteBookOwner.indexOf('/') !== 0) { - noteBookOwner = `/${noteBookOwner}`; - } - let url = `${notesConstants.PORTAL}/${notesConstants.PORTAL_REST}/notes/tree/full?path=${noteBookType}${noteBookOwner}/${noteId}&withDrafts=${withDrafts}`; - if (lang){ - url=`${url}${getSeparator(url)}lang=${lang}`; - } - return fetch(url, { - method: 'GET', - credentials: 'include', - }).then(resp => { - if (!resp || !resp.ok) { - throw new Error('Response code indicates a server error', resp); - } else { - return resp.json(); - } - }); -} - export function createNote(page) { return fetch(`${notesConstants.PORTAL}/${notesConstants.PORTAL_REST}/notes/note`, { headers: { diff --git a/notes-webapp/src/main/webapp/vue-app/notes/components/NoteContentTableItem.vue b/notes-webapp/src/main/webapp/vue-app/notes/components/NoteContentTableItem.vue index 99f8e4a989..ecdd14f77c 100644 --- a/notes-webapp/src/main/webapp/vue-app/notes/components/NoteContentTableItem.vue +++ b/notes-webapp/src/main/webapp/vue-app/notes/components/NoteContentTableItem.vue @@ -20,9 +20,9 @@ --> -