001/* 002 * Copyright 2014 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 */ 016 017package org.ametys.cms.repository.comment.ui; 018 019import java.io.IOException; 020import java.util.ArrayList; 021import java.util.HashMap; 022import java.util.List; 023import java.util.Map; 024 025import org.apache.avalon.framework.service.ServiceException; 026import org.apache.avalon.framework.service.ServiceManager; 027import org.apache.cocoon.ProcessingException; 028import org.apache.commons.lang.StringUtils; 029 030import org.ametys.cms.ObservationConstants; 031import org.ametys.cms.content.ContentHelper; 032import org.ametys.cms.repository.Content; 033import org.ametys.cms.repository.comment.Comment; 034import org.ametys.cms.repository.comment.CommentableContent; 035import org.ametys.core.observation.Event; 036import org.ametys.core.observation.ObservationManager; 037import org.ametys.core.right.RightManager.RightResult; 038import org.ametys.core.ui.Callable; 039import org.ametys.core.ui.StaticClientSideElement; 040import org.ametys.core.user.CurrentUserProvider; 041import org.ametys.core.user.UserIdentity; 042import org.ametys.plugins.repository.AmetysObjectResolver; 043import org.ametys.plugins.repository.UnknownAmetysObjectException; 044import org.ametys.plugins.repository.metadata.UnknownMetadataException; 045import org.ametys.runtime.parameter.ParameterHelper; 046 047/** 048 * This client site elements creates a button representing the validation state of a content's comment 049 */ 050public class CommentClientSideElement extends StaticClientSideElement 051{ 052 /** The constant for the name of the author from parameters */ 053 public static final String PARAMETER_AUTHOR_NAME = "author-name"; 054 /** The constant for the email of the author from parameters */ 055 public static final String PARAMETER_AUTHOR_EMAIL = "author-email"; 056 /** The constant for the hidden status of the email of the author from parameters */ 057 public static final String PARAMETER_AUTHOR_EMAILHIDDEN = "author-emailhidden"; 058 /** The constant for the url of the author from parameters */ 059 public static final String PARAMETER_AUTHOR_URL = "author-url"; 060 /** The constant for the text from parameters */ 061 public static final String PARAMETER_TEXT = "text"; 062 /** The constant for the content id from parameters */ 063 public static final String PARAMETER_CONTENT_ID = "contentId"; 064 /** The constant for the comment id from parameters */ 065 public static final String PARAMETER_COMMENT_ID = "commentId"; 066 /** The Ametys object resolver */ 067 protected AmetysObjectResolver _resolver; 068 /** The current user provider */ 069 protected CurrentUserProvider _userProvider; 070 /** The observation manager */ 071 protected ObservationManager _observationManager; 072 /** The content helper */ 073 private ContentHelper _contentHelper; 074 075 @Override 076 public void service(ServiceManager smanager) throws ServiceException 077 { 078 super.service(smanager); 079 _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE); 080 _userProvider = (CurrentUserProvider) smanager.lookup(CurrentUserProvider.ROLE); 081 _observationManager = (ObservationManager) smanager.lookup(ObservationManager.ROLE); 082 _contentHelper = (ContentHelper) smanager.lookup(ContentHelper.ROLE); 083 } 084 085 /** 086 * Return the list of comments with its validation state 087 * @param parameters The contents and their comments 088 * @return The comments and their validated status 089 */ 090 @Callable 091 public Map<String, Object> getComments(Map<String, Object> parameters) 092 { 093 Map<String, Object> results = new HashMap<>(); 094 results.put("comments", new ArrayList<Map<String, Object>>()); 095 096 for (String contentId : parameters.keySet()) 097 { 098 try 099 { 100 Content content = _resolver.resolveById(contentId); 101 if (content instanceof CommentableContent) 102 { 103 CommentableContent cContent = (CommentableContent) content; 104 105 @SuppressWarnings("unchecked") 106 List<String> commentIds = (List<String>) parameters.get(contentId); 107 108 for (String commentId : commentIds) 109 { 110 try 111 { 112 Comment comment = cContent.getComment(commentId); 113 @SuppressWarnings("unchecked") 114 List<Map<String, Object>> comments = (List<Map<String, Object>>) results.get("comments"); 115 comments.add(getCommentParameters(cContent, comment)); 116 } 117 catch (UnknownMetadataException e) 118 { 119 getLogger().warn("Ignoring request to get comment status " + contentId + " " + commentId + " because it does not exist", e); 120 } 121 } 122 } 123 else 124 { 125 getLogger().warn("Ignoring request to get comments status on non commentable content " + contentId); 126 } 127 } 128 catch (UnknownAmetysObjectException e) 129 { 130 getLogger().warn("Ignoring request to get comments status on inexisting content " + contentId, e); 131 } 132 } 133 134 return results; 135 } 136 137 /** 138 * Get a comment properties 139 * @param contentId the id of the content 140 * @param commentId the id of the comment. Can be null 141 * @return results the server's response in JSON. 142 * @throws IOException If an error occured 143 * @throws ProcessingException If an error occured 144 */ 145 @Callable 146 public Map<String, Object> getComment(String contentId, String commentId) throws IOException, ProcessingException 147 { 148 Map<String, Object> results = new HashMap<> (); 149 150 UserIdentity user = _userProvider.getUser(); 151 152 Map<String, Object> comments = new HashMap<> (); 153 154 Content content = _resolver.resolveById(contentId); 155 156 if (content instanceof CommentableContent) 157 { 158 if (_rightManager.hasRight(user, "CMS_Rights_CommentModerate", content) == RightResult.RIGHT_ALLOW) 159 { 160 CommentableContent commentableContent = (CommentableContent) content; 161 List<Comment> commentList = commentableContent.getComments(true, true); 162 163 for (Comment comment : commentList) 164 { 165 if (StringUtils.isNotEmpty(commentId) && !commentId.equals(comment.getId())) 166 { 167 continue; 168 } 169 170 comments = _jsonifyComment(comment, content); 171 } 172 } 173 } 174 175 results.put("comments", comments); 176 return results; 177 } 178 179 /** 180 * Edit a comment if connected user has sufficient rights 181 * @param parameters the JS parameters. Necessarily contains the content and comment id, and the values to edit 182 * @return An empty map 183 */ 184 @Callable 185 public Map<String, Object> editComment(Map<String, Object> parameters) 186 { 187 String contentId = (String) parameters.get(PARAMETER_CONTENT_ID); 188 String commentId = (String) parameters.get(PARAMETER_COMMENT_ID); 189 190 try 191 { 192 Content content = _resolver.resolveById(contentId); 193 194 if (!hasRight("CMS_Rights_CommentModerate", content)) 195 { 196 String errorMessage = "User " + getCurrentUser() + " try to edit a comment on content of id '" + contentId + "' with no sufficient rights"; 197 getLogger().error(errorMessage); 198 throw new IllegalStateException(errorMessage); 199 } 200 201 if (!(content instanceof CommentableContent)) 202 { 203 String errorMessage = "Can not edit comment for non-commentable content of id '" + contentId + "'"; 204 getLogger().error(errorMessage); 205 throw new IllegalStateException(errorMessage); 206 } 207 208 CommentableContent cContent = (CommentableContent) content; 209 210 Comment comment = cContent.getComment(commentId); 211 212 String oldAuthorName = comment.getAuthorName(); 213 String oldAuthorEmail = comment.getAuthorEmail(); 214 boolean oldAuthorEmailHidden = comment.isEmailHidden(); 215 String oldAuthorURL = comment.getAuthorURL(); 216 String oldContent = comment.getContent(); 217 218 boolean needSave = false; 219 220 String authorName = (String) parameters.get(PARAMETER_AUTHOR_NAME); 221 if (!authorName.equals(oldAuthorName)) 222 { 223 comment.setAuthorName(authorName); 224 needSave = true; 225 } 226 227 String authorEmail = (String) parameters.get(PARAMETER_AUTHOR_EMAIL); 228 if (!authorEmail.equals(oldAuthorEmail)) 229 { 230 comment.setAuthorEmail(authorEmail); 231 needSave = true; 232 } 233 234 boolean authorEmailHidden = (Boolean) parameters.get(PARAMETER_AUTHOR_EMAILHIDDEN); 235 if (authorEmailHidden != oldAuthorEmailHidden) 236 { 237 comment.setEmailHiddenStatus(authorEmailHidden); 238 needSave = true; 239 } 240 241 String authorUrl = (String) parameters.get(PARAMETER_AUTHOR_URL); 242 if (!authorUrl.equals(oldAuthorURL)) 243 { 244 comment.setAuthorURL(authorUrl); 245 needSave = true; 246 } 247 248 String text = (String) parameters.get(PARAMETER_TEXT); 249 if (!text.equals(oldContent)) 250 { 251 comment.setContent(text); 252 needSave = true; 253 } 254 255 if (needSave) 256 { 257 cContent.saveChanges(); 258 259 Map<String, Object> eventParams = new HashMap<>(); 260 eventParams.put(ObservationConstants.ARGS_CONTENT, content); 261 eventParams.put(ObservationConstants.ARGS_COMMENT, comment); 262 eventParams.put("content.comment.old.author", oldAuthorName); 263 eventParams.put("content.comment.old.author.email", oldAuthorEmail); 264 eventParams.put("content.comment.old.author.email.hidden", oldAuthorEmailHidden); 265 eventParams.put("content.comment.old.author.url", oldAuthorURL); 266 eventParams.put("content.comment.old.content", oldContent); 267 268 _observationManager.notify(new Event(ObservationConstants.EVENT_CONTENT_COMMENT_MODIFYING, getCurrentUser(), eventParams)); 269 } 270 } 271 catch (UnknownAmetysObjectException e) 272 { 273 getLogger().error("Unknown content of id '" + contentId + "'", e); 274 throw new IllegalArgumentException("Unknown content of id '" + contentId + "'", e); 275 } 276 catch (UnknownMetadataException e) 277 { 278 getLogger().error("Unknown comment of id '" + commentId + "' for content '" + contentId + "'", e); 279 throw new IllegalArgumentException("Unknown comment of id '" + commentId + "' for content '" + contentId + "'", e); 280 } 281 282 return java.util.Collections.EMPTY_MAP; 283 } 284 285 /** 286 * Removes comments 287 * @param contents the contents with comments to remove 288 * @return the JSON result with deleted comments or not 289 */ 290 @SuppressWarnings("unchecked") 291 @Callable 292 public Map<String, Object> deleteComments(Map<String, List<String>> contents) 293 { 294 Map<String, Object> results = new HashMap<> (); 295 296 results.put("deleted-comments", new ArrayList<Map<String, Object>>()); 297 results.put("undeleted-comments", new ArrayList<Map<String, Object>>()); 298 results.put("uncommentable-contents", new ArrayList<Map<String, Object>>()); 299 results.put("noright-contents", new ArrayList<Map<String, Object>>()); 300 results.put("unknown-contents", new ArrayList<Map<String, Object>>()); 301 302 for (String contentId : contents.keySet()) 303 { 304 try 305 { 306 Content content = _resolver.resolveById(contentId); 307 308 if (hasRight("CMS_Rights_CommentModerate", content)) 309 { 310 // For each associated comment 311 for (String commentId : contents.get(contentId)) 312 { 313 Map<String, Object> commentParams = new HashMap<>(); 314 commentParams.put("id", commentId); 315 commentParams.put("contentId", contentId); 316 commentParams.put("contentTitle", _contentHelper.getTitle(content)); 317 318 if (content instanceof CommentableContent) 319 { 320 CommentableContent cContent = (CommentableContent) content; 321 322 try 323 { 324 Comment comment = cContent.getComment(commentId); 325 326 Map<String, Object> eventParams = new HashMap<>(); 327 eventParams.put(ObservationConstants.ARGS_CONTENT, content); 328 eventParams.put(ObservationConstants.ARGS_COMMENT_ID, comment.getId()); 329 eventParams.put(ObservationConstants.ARGS_COMMENT_AUTHOR, comment.getAuthorName()); 330 eventParams.put(ObservationConstants.ARGS_COMMENT_AUTHOR_EMAIL, comment.getAuthorEmail()); 331 eventParams.put(ObservationConstants.ARGS_COMMENT_VALIDATED, comment.isValidated()); 332 eventParams.put("comment.content", comment.getContent()); 333 334 _observationManager.notify(new Event(ObservationConstants.EVENT_CONTENT_COMMENT_DELETING, getCurrentUser(), eventParams)); 335 336 comment.remove(); 337 cContent.saveChanges(); 338 339 List<Map<String, Object>> deletedComments = (List<Map<String, Object>>) results.get("deleted-comments"); 340 deletedComments.add(commentParams); 341 342 _observationManager.notify(new Event(ObservationConstants.EVENT_CONTENT_COMMENT_DELETED, getCurrentUser(), eventParams)); 343 } 344 catch (UnknownMetadataException e) 345 { 346 getLogger().error("Can not remove a non existing comment", e); 347 348 List<Map<String, Object>> undeletedComments = (List<Map<String, Object>>) results.get("undeleted-comments"); 349 undeletedComments.add(commentParams); 350 } 351 } 352 else 353 { 354 getLogger().error("Can not remove a comment on a non commentable content"); 355 356 List<Map<String, Object>> uncommentableContents = (List<Map<String, Object>>) results.get("uncommentable-contents"); 357 uncommentableContents.add(commentParams); 358 } 359 } 360 } 361 else 362 { 363 // No right 364 getLogger().error("User '" + getCurrentUser() + "' does not have right to moderate comments on content '" + content.getId() + "'"); 365 366 Map<String, Object> contentParams = new HashMap<>(); 367 contentParams.put("contentId", contentId); 368 contentParams.put("contentTitle", _contentHelper.getTitle(content)); 369 370 List<Map<String, Object>> norightContents = (List<Map<String, Object>>) results.get("noright-contents"); 371 norightContents.add(contentParams); 372 } 373 } 374 catch (UnknownAmetysObjectException e) 375 { 376 getLogger().error("Can not remove a comment on a non existing content", e); 377 378 Map<String, Object> contentParams = new HashMap<>(); 379 contentParams.put("contentId", contentId); 380 381 List<Map<String, Object>> unknownContents = (List<Map<String, Object>>) results.get("unknown-contents"); 382 unknownContents.add(contentParams); 383 } 384 } 385 386 return results; 387 } 388 389 /** 390 * Validates comments when it is possible. 391 * @param contents the contents with comments to validate 392 * @return the JSON result with validated comments or not 393 */ 394 @SuppressWarnings("unchecked") 395 @Callable 396 public Map<String, Object> validateComments(Map<String, List<String>> contents) 397 { 398 Map<String, Object> results = new HashMap<>(); 399 400 results.put("validated-comments", new ArrayList<Map<String, Object>>()); 401 results.put("error-comments", new ArrayList<Map<String, Object>>()); 402 results.put("uncommentable-contents", new ArrayList<Map<String, Object>>()); 403 results.put("noright-contents", new ArrayList<Map<String, Object>>()); 404 results.put("unknown-contents", new ArrayList<Map<String, Object>>()); 405 406 for (String contentId : contents.keySet()) 407 { 408 try 409 { 410 Content content = _resolver.resolveById(contentId); 411 412 if (hasRight("CMS_Rights_CommentModerate", content)) 413 { 414 // For each associated comment 415 for (String commentId : contents.get(contentId)) 416 { 417 Map<String, Object> commentParams = new HashMap<>(); 418 commentParams.put("id", commentId); 419 commentParams.put("contentId", contentId); 420 commentParams.put("contentTitle", _contentHelper.getTitle(content)); 421 422 if (content instanceof CommentableContent) 423 { 424 CommentableContent cContent = (CommentableContent) content; 425 426 try 427 { 428 Comment comment = cContent.getComment(commentId); 429 if (!comment.isValidated()) 430 { 431 comment.setValidated(true); 432 cContent.saveChanges(); 433 434 List<Map<String, Object>> validatedComments = (List<Map<String, Object>>) results.get("validated-comments"); 435 validatedComments.add(commentParams); 436 437 Map<String, Object> eventParams = new HashMap<>(); 438 eventParams.put(ObservationConstants.ARGS_CONTENT, content); 439 eventParams.put(ObservationConstants.ARGS_COMMENT, comment); 440 _observationManager.notify(new Event(ObservationConstants.EVENT_CONTENT_COMMENT_VALIDATED, getCurrentUser(), eventParams)); 441 } 442 } 443 catch (UnknownMetadataException e) 444 { 445 getLogger().error("Can not validate a non existing comment", e); 446 447 List<Map<String, Object>> errorComments = (List<Map<String, Object>>) results.get("error-comments"); 448 errorComments.add(commentParams); 449 } 450 } 451 else 452 { 453 getLogger().error("Can not validate a comment on a non commentable content"); 454 455 List<Map<String, Object>> errorComments = (List<Map<String, Object>>) results.get("error-comments"); 456 errorComments.add(commentParams); 457 } 458 } 459 } 460 else 461 { 462 // No right 463 getLogger().error("User '" + getCurrentUser() + "' does not have right to validate comments on content '" + content.getId() + "'"); 464 465 Map<String, Object> contentParams = new HashMap<>(); 466 contentParams.put("contentId", contentId); 467 contentParams.put("contentTitle", _contentHelper.getTitle(content)); 468 } 469 } 470 catch (UnknownAmetysObjectException e) 471 { 472 getLogger().error("Can not validate a comment on a non existing content", e); 473 474 Map<String, Object> contentParams = new HashMap<>(); 475 contentParams.put("contentId", contentId); 476 477 List<Map<String, Object>> unknownContents = (List<Map<String, Object>>) results.get("unknown-contents"); 478 unknownContents.add(contentParams); 479 } 480 } 481 482 return results; 483 } 484 485 /** 486 * Invalidates comments when it is possible. 487 * @param contents the contents with comments to invalidate 488 * @return the JSON result with invalidated comments or not 489 */ 490 @SuppressWarnings("unchecked") 491 @Callable 492 public Map<String, Object> invalidateComments(Map<String, List<String>> contents) 493 { 494 Map<String, Object> results = new HashMap<>(); 495 496 results.put("unvalidated-comments", new ArrayList<Map<String, Object>>()); 497 results.put("error-comments", new ArrayList<Map<String, Object>>()); 498 results.put("uncommentable-contents", new ArrayList<Map<String, Object>>()); 499 results.put("noright-contents", new ArrayList<Map<String, Object>>()); 500 results.put("unknown-contents", new ArrayList<Map<String, Object>>()); 501 502 for (String contentId : contents.keySet()) 503 { 504 try 505 { 506 Content content = _resolver.resolveById(contentId); 507 508 if (hasRight("CMS_Rights_CommentModerate", content)) 509 { 510 // For each associated comment 511 for (String commentId : contents.get(contentId)) 512 { 513 Map<String, Object> commentParams = new HashMap<>(); 514 commentParams.put("id", commentId); 515 commentParams.put("contentId", contentId); 516 commentParams.put("contentTitle", _contentHelper.getTitle(content)); 517 518 if (content instanceof CommentableContent) 519 { 520 CommentableContent cContent = (CommentableContent) content; 521 522 try 523 { 524 Comment comment = cContent.getComment(commentId); 525 if (comment.isValidated()) 526 { 527 comment.setValidated(false); 528 cContent.saveChanges(); 529 530 List<Map<String, Object>> validatedComments = (List<Map<String, Object>>) results.get("unvalidated-comments"); 531 validatedComments.add(commentParams); 532 533 Map<String, Object> eventParams = new HashMap<>(); 534 eventParams.put(ObservationConstants.ARGS_CONTENT, content); 535 eventParams.put(ObservationConstants.ARGS_COMMENT, comment); 536 _observationManager.notify(new Event(ObservationConstants.EVENT_CONTENT_COMMENT_UNVALIDATED, getCurrentUser(), eventParams)); 537 } 538 } 539 catch (UnknownMetadataException e) 540 { 541 getLogger().error("Can not validate a non existing comment", e); 542 543 List<Map<String, Object>> errorComments = (List<Map<String, Object>>) results.get("error-comments"); 544 errorComments.add(commentParams); 545 } 546 } 547 else 548 { 549 getLogger().error("Can not validate a comment on a non commentable content"); 550 551 List<Map<String, Object>> errorComments = (List<Map<String, Object>>) results.get("error-comments"); 552 errorComments.add(commentParams); 553 } 554 } 555 } 556 else 557 { 558 // No right 559 getLogger().error("User '" + getCurrentUser() + "' does not have right to validate comments on content '" + content.getId() + "'"); 560 561 Map<String, Object> contentParams = new HashMap<>(); 562 contentParams.put("contentId", contentId); 563 contentParams.put("contentTitle", _contentHelper.getTitle(content)); 564 } 565 } 566 catch (UnknownAmetysObjectException e) 567 { 568 getLogger().error("Can not validate a comment on a non existing content", e); 569 570 Map<String, Object> contentParams = new HashMap<>(); 571 contentParams.put("contentId", contentId); 572 573 List<Map<String, Object>> unknownContents = (List<Map<String, Object>>) results.get("unknown-contents"); 574 unknownContents.add(contentParams); 575 } 576 } 577 578 return results; 579 } 580 581 /** 582 * Jsonify the comment. 583 * @param comment The comment 584 * @param content The content 585 * @return commentMap the comment map 586 */ 587 protected Map<String, Object> _jsonifyComment (Comment comment, Content content) 588 { 589 Map<String, Object> result = new HashMap<> (); 590 591 Map<String, Object> commentMap = new HashMap<> (); 592 593 594 commentMap.put("validated", Boolean.toString(comment.isValidated())); 595 commentMap.put("id", comment.getId()); 596 commentMap.put("creationDate", ParameterHelper.valueToString(comment.getCreationDate())); 597 598 String authorName = comment.getAuthorName(); 599 if (authorName != null) 600 { 601 commentMap.put("author-name", authorName); 602 } 603 604 String authorEmail = comment.getAuthorEmail(); 605 if (authorEmail != null) 606 { 607 Map<String, Object> authorEmailMap = new HashMap<> (); 608 authorEmailMap.put("hidden", Boolean.toString(comment.isEmailHidden())); 609 authorEmailMap.put("value", authorEmail); 610 commentMap.put("author-email", authorEmailMap); 611 } 612 613 String authorUrl = comment.getAuthorURL(); 614 if (authorUrl != null) 615 { 616 commentMap.put("author-url", authorUrl); 617 } 618 619 String text = comment.getContent(); 620 if (text != null) 621 { 622 commentMap.put("text", comment.getContent()); 623 } 624 commentMap.put("content", _jsonifyContent(content)); 625 626 result.put("comment", commentMap); 627 628 return result; 629 } 630 631 /** 632 * Jsonify the content. 633 * @param content The content 634 * @return contentMap the content map 635 */ 636 protected Map<String, Object> _jsonifyContent (Content content) 637 { 638 Map<String, Object> contentMap = new HashMap<> (); 639 640 contentMap.put("id", content.getId()); 641 contentMap.put("title", _contentHelper.getTitle(content)); 642 contentMap.put("name", content.getName()); 643 644 return contentMap; 645 } 646 647 /** 648 * Get the parameters for a comment 649 * @param content The content 650 * @param comment The comment 651 * @return The parameters 652 */ 653 protected Map<String, Object> getCommentParameters (Content content, Comment comment) 654 { 655 Map<String, Object> params = new HashMap<>(); 656 657 params.put("contentId", content.getId()); 658 params.put("contentTitle", _contentHelper.getTitle(content)); 659 params.put("id", comment.getId()); 660 params.put("validated", comment.isValidated()); 661 662 return params; 663 } 664 665 /** 666 * Get the current user 667 * @return The current user 668 */ 669 protected UserIdentity getCurrentUser () 670 { 671 return _userProvider.getUser(); 672 } 673 674 /** 675 * Determines if connected user has right on content 676 * @param rightId The right id 677 * @param content The content 678 * @return true if user has right 679 */ 680 protected boolean hasRight (String rightId, Content content) 681 { 682 UserIdentity user = _userProvider.getUser(); 683 684 return _rightManager.hasRight(user, "CMS_Rights_CommentModerate", content) == RightResult.RIGHT_ALLOW; 685 } 686}