001/* 002 * Copyright 2021 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.plugins.workspaces.tasks.json; 017 018import java.io.InputStream; 019import java.util.ArrayList; 020import java.util.Arrays; 021import java.util.HashMap; 022import java.util.List; 023import java.util.Map; 024import java.util.Set; 025import java.util.stream.Collectors; 026 027import org.apache.avalon.framework.component.Component; 028import org.apache.avalon.framework.service.ServiceException; 029import org.apache.avalon.framework.service.ServiceManager; 030import org.apache.avalon.framework.service.Serviceable; 031 032import org.ametys.cms.data.Binary; 033import org.ametys.cms.repository.Content; 034import org.ametys.cms.repository.ReactionableObject.ReactionType; 035import org.ametys.cms.repository.comment.Comment; 036import org.ametys.core.user.User; 037import org.ametys.core.user.UserIdentity; 038import org.ametys.core.user.UserManager; 039import org.ametys.core.user.directory.NotUniqueUserException; 040import org.ametys.core.user.population.PopulationContextHelper; 041import org.ametys.plugins.core.user.UserHelper; 042import org.ametys.plugins.workspaces.members.ProjectMemberManager; 043import org.ametys.plugins.workspaces.tags.ProjectTagProviderExtensionPoint; 044import org.ametys.plugins.workspaces.tasks.Task; 045import org.ametys.plugins.workspaces.tasks.Task.CheckItem; 046import org.ametys.plugins.workspaces.tasks.jcr.JCRTask; 047import org.ametys.runtime.plugin.component.AbstractLogEnabled; 048 049/** 050 * Helper to convert task to JSON 051 */ 052public class TaskJSONHelper extends AbstractLogEnabled implements Serviceable, Component 053{ 054 /** The Avalon role */ 055 public static final String ROLE = TaskJSONHelper.class.getName(); 056 057 /** The user helper */ 058 protected UserHelper _userHelper; 059 060 /** The project member manager */ 061 protected ProjectMemberManager _projectMemberManager; 062 063 /** The user manager */ 064 protected UserManager _userManager; 065 066 /** The population context helper */ 067 protected PopulationContextHelper _populationContextHelper; 068 069 /** The tag provider extension point */ 070 protected ProjectTagProviderExtensionPoint _tagProviderExtensionPoint; 071 072 @Override 073 public void service(ServiceManager manager) throws ServiceException 074 { 075 _userHelper = (UserHelper) manager.lookup(UserHelper.ROLE); 076 _projectMemberManager = (ProjectMemberManager) manager.lookup(ProjectMemberManager.ROLE); 077 _userManager = (UserManager) manager.lookup(UserManager.ROLE); 078 _populationContextHelper = (PopulationContextHelper) manager.lookup(PopulationContextHelper.ROLE); 079 _tagProviderExtensionPoint = (ProjectTagProviderExtensionPoint) manager.lookup(ProjectTagProviderExtensionPoint.ROLE); 080 } 081 082 /** 083 * Task as JSON 084 * @param task the task 085 * @param lang the language 086 * @param siteName the site name 087 * @return the task as JSON 088 */ 089 public Map<String, Object> taskAsJSON(Task task, String lang, String siteName) 090 { 091 Map<String, Object> json = new HashMap<>(); 092 093 json.put(JCRTask.ATTRIBUTE_TASK_ID, task.getId()); 094 json.put(JCRTask.ATTRIBUTE_LABEL, task.getLabel()); 095 json.put(JCRTask.ATTRIBUTE_DESCRIPTION, task.getDescription()); 096 json.put(JCRTask.ATTRIBUTE_TASKSLISTID, task.getTaskListId()); 097 json.put("position", task.getPosition()); 098 json.put(JCRTask.ATTRIBUTE_CREATIONDATE, task.getCreationDate()); 099 json.put("closeInfo", _closeInfoAsJson(task)); 100 101 json.put(JCRTask.ATTRIBUTE_ASSIGNMENTS, _assignmentsToJson(task)); 102 103 List<String> tags = task.getTags() 104 .stream() 105 .filter(tag -> _tagProviderExtensionPoint.hasTag(tag, Map.of("siteName", siteName))) 106 .collect(Collectors.toList()); 107 json.put("tags", tags); 108 109 List<Map<String, Object>> attachments = task.getAttachments() 110 .stream() 111 .map(this::_binaryToJson) 112 .collect(Collectors.toList()); 113 json.put(JCRTask.ATTRIBUTE_ATTACHMENTS, attachments); 114 115 json.put(JCRTask.ATTRIBUTE_CHECKLIST, _checkListToJson(task)); 116 117 json.put("comments", _commentsToJson(task.getComments(true, true), lang, siteName)); 118 json.put(JCRTask.ATTRIBUTE_DUEDATE, task.getDueDate()); 119 json.put("datePassed", task.isClosed()); 120 121 return json; 122 } 123 124 private List<Map<String, Object>> _commentsToJson(List<Comment> comments, String lang, String siteName) 125 { 126 List<Map<String, Object>> json = new ArrayList<>(); 127 128 for (Comment comment : comments) 129 { 130 Map<String, Object> commentJson = new HashMap<>(); 131 commentJson.put("id", comment.getId()); 132 commentJson.put("subComments", _commentsToJson(comment.getSubComment(true, true), lang, siteName)); 133 commentJson.put("isDeleted", comment.isDeleted()); 134 commentJson.put("creationDate", comment.getCreationDate()); 135 if (!comment.isDeleted()) 136 { 137 commentJson.put("text", comment.getContent()); 138 commentJson.put("nbLike", comment.getReactionUsers(ReactionType.LIKE).size()); 139 List<String> userLikes = comment.getReactionUsers(ReactionType.LIKE) 140 .stream() 141 .map(UserIdentity::userIdentityToString) 142 .collect(Collectors.toList()); 143 commentJson.put("userLikes", userLikes); 144 145 Map<String, Object> jsonAuthor = new HashMap<>(); 146 jsonAuthor.put("name", comment.getAuthorName()); 147 jsonAuthor.put("avatar", comment.getAuthorURL()); 148 149 User user = _getUserByMail(comment.getAuthorEmail(), siteName); 150 if (user != null) 151 { 152 jsonAuthor.put("id", UserIdentity.userIdentityToString(user.getIdentity())); 153 Content member = _projectMemberManager.getUserContent(lang, user); 154 if (member != null) 155 { 156 if (member.hasValue("function")) 157 { 158 jsonAuthor.put("function", member.getValue("function")); 159 } 160 if (member.hasValue("organisation-accronym")) 161 { 162 jsonAuthor.put("organisationAcronym", member.getValue("organisation-accronym")); 163 } 164 } 165 } 166 commentJson.put("author", jsonAuthor); 167 commentJson.put("isEdited", comment.isEdited()); 168 } 169 170 json.add(commentJson); 171 } 172 173 return json; 174 } 175 176 private Map<String, Object> _binaryToJson(Binary b) 177 { 178 Map<String, Object> json = new HashMap<>(); 179 180 try (InputStream is = b.getInputStream()) 181 { 182 json.put("id", b.getFilename()); 183 json.put("name", b.getFilename()); 184 json.put("type", b.getMimeType()); 185 json.put("size", b.getLength()); 186 } 187 catch (Exception e) 188 { 189 getLogger().error("An error occurred reading binary {}", b.getFilename(), e); 190 } 191 return json; 192 } 193 194 private Map<String, Object> _closeInfoAsJson(Task task) 195 { 196 UserIdentity closeAuthor = task.getCloseAuthor(); 197 198 return task.isClosed() 199 ? Map.of(JCRTask.ATTRIBUTE_CLOSEAUTHOR, _userHelper.user2json(closeAuthor), JCRTask.ATTRIBUTE_CLOSEDATE, task.getCloseDate()) 200 : null; 201 } 202 203 private List<Map<String, Object>> _assignmentsToJson(Task task) 204 { 205 return task.getAssignments() 206 .stream() 207 .map(u -> _userHelper.user2json(u)) 208 .collect(Collectors.toList()); 209 } 210 211 private List<Map<String, Object>> _checkListToJson(Task task) 212 { 213 List<Map<String, Object>> json = new ArrayList<>(); 214 for (CheckItem checkItem : task.getCheckList()) 215 { 216 json.add(Map.of(JCRTask.ATTRIBUTE_CHECKLIST_LABEL, checkItem.getLabel(), JCRTask.ATTRIBUTE_CHECKLIST_ISCHECKED, checkItem.isChecked())); 217 } 218 return json; 219 } 220 221 private User _getUserByMail(String email, String siteName) 222 { 223 Set<String> userPopulationsOnSite = _populationContextHelper.getUserPopulationsOnContexts(Arrays.asList("/sites/" + siteName, "/sites-fo/" + siteName), false, false); 224 225 try 226 { 227 return _userManager.getUserByEmail(userPopulationsOnSite, email); 228 } 229 catch (NotUniqueUserException e) 230 { 231 getLogger().error("Cannot find user in site '" + siteName + "' by email '" + email + "' because 2 or more users match", e); 232 return null; 233 } 234 } 235}