001/* 002 * Copyright 2019 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.cms.repository.comment.ui; 017 018import java.util.ArrayList; 019import java.util.HashMap; 020import java.util.HashSet; 021import java.util.List; 022import java.util.Map; 023import java.util.Set; 024 025import javax.jcr.Node; 026import javax.jcr.NodeIterator; 027import javax.jcr.Repository; 028import javax.jcr.RepositoryException; 029import javax.jcr.Session; 030import javax.jcr.query.Query; 031 032import org.apache.avalon.framework.component.Component; 033import org.apache.avalon.framework.context.Context; 034import org.apache.avalon.framework.context.ContextException; 035import org.apache.avalon.framework.context.Contextualizable; 036import org.apache.avalon.framework.service.ServiceException; 037import org.apache.avalon.framework.service.ServiceManager; 038import org.apache.avalon.framework.service.Serviceable; 039import org.apache.cocoon.components.ContextHelper; 040import org.apache.cocoon.environment.Request; 041import org.apache.commons.lang.StringUtils; 042 043import org.ametys.cms.content.ContentHelper; 044import org.ametys.cms.repository.Content; 045import org.ametys.cms.repository.ReportableObject; 046import org.ametys.cms.repository.ReportableObjectHelper; 047import org.ametys.cms.repository.comment.Comment; 048import org.ametys.cms.repository.comment.CommentableContent; 049import org.ametys.core.right.RightManager; 050import org.ametys.core.right.RightManager.RightResult; 051import org.ametys.core.ui.Callable; 052import org.ametys.core.user.CurrentUserProvider; 053import org.ametys.core.user.User; 054import org.ametys.core.user.UserIdentity; 055import org.ametys.core.user.UserManager; 056import org.ametys.core.util.DateUtils; 057import org.ametys.plugins.repository.AmetysObjectResolver; 058import org.ametys.plugins.repository.provider.AbstractRepository; 059 060/** 061 * Action for getting the list of comments. 062 */ 063public class CommentsAndReportsTreeComponent implements Component, Serviceable, Contextualizable 064{ 065 /** The ametys object resolver */ 066 protected AmetysObjectResolver _resolver; 067 068 /** The right manager */ 069 protected RightManager _rightManager; 070 071 /** The current user provider */ 072 protected CurrentUserProvider _currentUserProvider; 073 074 /** The content helper */ 075 protected ContentHelper _contentHelper; 076 077 /** The user manager */ 078 protected UserManager _userManager; 079 080 /** The context */ 081 protected Context _context; 082 083 /** The JCR repository */ 084 protected Repository _repository; 085 086 087 @Override 088 public void service(ServiceManager smanager) throws ServiceException 089 { 090 _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE); 091 _rightManager = (RightManager) smanager.lookup(RightManager.ROLE); 092 _currentUserProvider = (CurrentUserProvider) smanager.lookup(CurrentUserProvider.ROLE); 093 _contentHelper = (ContentHelper) smanager.lookup(ContentHelper.ROLE); 094 _userManager = (UserManager) smanager.lookup(UserManager.ROLE); 095 _repository = (Repository) smanager.lookup(AbstractRepository.ROLE); 096 } 097 098 public void contextualize(Context context) throws ContextException 099 { 100 _context = context; 101 } 102 103 /** 104 * Retrieves the comments and reports of the given selected contents 105 * @param contentIds identifiers of the contents 106 * @return the comments and reports of the contents 107 */ 108 @Callable 109 public Map getCommentsAndReportsFromSelectedContents(List<String> contentIds) 110 { 111 // Get current user to check rights 112 UserIdentity user = _currentUserProvider.getUser(); 113 114 // Get comments and reports for each content 115 List<Map<String, Object>> contentComments = new ArrayList<>(); 116 for (String contentId : contentIds) 117 { 118 Content content = _resolver.resolveById(contentId); 119 contentComments.add(_content2json(content, user)); 120 } 121 122 return Map.of("comments", contentComments); 123 } 124 125 /** 126 * Retrieves the comments and reports of the contents of the given type 127 * Manages only contents that have at least one report (on itself or on a comment) 128 * @param contentTypeId the content type identifier 129 * @return the comments and reports of the contents 130 * @throws RepositoryException if an error occurs while retrieving contents from the repository 131 */ 132 @Callable 133 public Map getCommentsAndReportsFromContentTypeId(String contentTypeId) throws RepositoryException 134 { 135 // Get current user to check rights 136 UserIdentity user = _currentUserProvider.getUser(); 137 138 // Get contents from the given content type 139 Iterable<Content> contents = _getReportedContentsFromContentType(contentTypeId); 140 141 // Get comments and reports for each content 142 List<Map<String, Object>> contentComments = new ArrayList<>(); 143 for (Content content : contents) 144 { 145 contentComments.add(_content2json(content, user, false)); 146 } 147 148 return Map.of("comments", contentComments); 149 } 150 151 /** 152 * Retrieves the contents from the current site with the given content type that have at least a report or a reported comment 153 * @param contentTypeId the content type identifier 154 * @return the contents 155 * @throws RepositoryException if an error occurs while retrieving the contents from the repository 156 */ 157 protected Iterable<Content> _getReportedContentsFromContentType(String contentTypeId) throws RepositoryException 158 { 159 Request request = ContextHelper.getRequest(_context); 160 String currentSiteName = (String) request.getAttribute("siteName"); 161 162 String xPathQuery = "//element(*, ametys:content)[@ametys:site = '" + currentSiteName + "' and @ametys-internal:contentType = '" + contentTypeId + "']" 163 + "//*[@ametys:" + ReportableObjectHelper.REPORTS_COUNT_ATTRIBUTE_NAME + " > 0]"; 164 165 Session session = _repository.login(); 166 @SuppressWarnings("deprecation") 167 Query query = session.getWorkspace().getQueryManager().createQuery(xPathQuery, Query.XPATH); 168 NodeIterator reportedNodesIterator = query.execute().getNodes(); 169 170 Set<Content> contents = new HashSet<>(); 171 172 while (reportedNodesIterator.hasNext()) 173 { 174 Node node = reportedNodesIterator.nextNode(); 175 while (!node.isNodeType("ametys:content")) 176 { 177 node = node.getParent(); 178 } 179 contents.add(_resolver.resolve(node, false)); 180 } 181 182 return contents; 183 } 184 185 /** 186 * Get the JSON representation of a content 187 * @param content The content 188 * @param user the current user 189 * @return The content as JSON object 190 */ 191 protected Map<String, Object> _content2json (Content content, UserIdentity user) 192 { 193 return _content2json(content, user, true); 194 } 195 196 /** 197 * Get the JSON representation of a content 198 * @param content The content 199 * @param user the current user 200 * @param includeNoReportedComments <code>true</code> to include the comment that have no report, <code>false</code> otherwise 201 * @return The content as JSON object 202 */ 203 protected Map<String, Object> _content2json (Content content, UserIdentity user, boolean includeNoReportedComments) 204 { 205 Map<String, Object> object = new HashMap<>(); 206 207 object.put("contentId", content.getId()); 208 object.put("commentId", ""); 209 object.put("contentTitle", StringUtils.defaultString(content.getTitle())); 210 object.put("text", StringUtils.defaultString(content.getTitle())); 211 object.put("creationDate", DateUtils.dateToString(content.getLastModified())); 212 213 UserIdentity lastContributorIdentity = content.getLastContributor(); 214 User lastContributor = _userManager.getUser(lastContributorIdentity); 215 if (lastContributor != null) 216 { 217 object.put("authorName", lastContributor.getFullName()); 218 object.put("authorEmail", lastContributor.getEmail()); 219 } 220 221 if (content instanceof ReportableObject) 222 { 223 object.put("reportsCount", ((ReportableObject) content).getReportsCount()); 224 } 225 226 List<Map<String, Object>> commentsList = new ArrayList<>(); 227 if (content instanceof CommentableContent) 228 { 229 if (_rightManager.hasRight(user, "CMS_Rights_CommentModerate", content) == RightResult.RIGHT_ALLOW) 230 { 231 CommentableContent commentableContent = (CommentableContent) content; 232 List<Comment> comments = commentableContent.getComments(true, true); 233 234 for (Comment comment : comments) 235 { 236 Map<String, Object> commentAsJSON = _comment2json(comment, content, includeNoReportedComments); 237 if (!commentAsJSON.isEmpty()) 238 { 239 commentsList.add(commentAsJSON); 240 } 241 } 242 } 243 } 244 245 object.put("comments", commentsList); 246 247 return object; 248 } 249 250 /** 251 * Get the JSON representation of a comment 252 * @param comment the comment 253 * @param content the content 254 * @param includeNoReportedComments <code>true</code> to include the comment that have no report, <code>false</code> otherwise 255 * @return The comment as JSON object 256 */ 257 protected Map<String, Object> _comment2json (Comment comment, Content content, boolean includeNoReportedComments) 258 { 259 List<Map<String, Object>> commentsList = new ArrayList<>(); 260 for (Comment subcomment : comment.getSubComment(true, true)) 261 { 262 Map<String, Object> subcommentAsJSON = _comment2json(subcomment, content, includeNoReportedComments); 263 if (!subcommentAsJSON.isEmpty()) 264 { 265 commentsList.add(_comment2json(subcomment, content, includeNoReportedComments)); 266 } 267 } 268 269 if (includeNoReportedComments || comment.getReportsCount() > 0 || !commentsList.isEmpty()) 270 { 271 Map<String, Object> object = new HashMap<>(); 272 object.put("commentId", comment.getId()); 273 object.put("contentId", content.getId()); 274 object.put("contentTitle", StringUtils.defaultString(content.getTitle())); 275 object.put("text", comment.getContent()); 276 object.put("authorName", comment.getAuthorName()); 277 object.put("authorEmail", comment.getAuthorEmail()); 278 object.put("authorUrl", comment.getAuthorURL()); 279 object.put("reportsCount", comment.getReportsCount()); 280 object.put("creationDate", DateUtils.zonedDateTimeToString(comment.getCreationDate())); 281 object.put("validated", Boolean.toString(comment.isValidated())); 282 object.put("authorIsEmailHidden", Boolean.toString(comment.isEmailHidden())); 283 object.put("comments", commentsList); 284 return object; 285 } 286 287 return Map.of(); 288 } 289}