001/* 002 * Copyright 2010 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; 018 019import java.time.ZonedDateTime; 020import java.util.ArrayList; 021import java.util.List; 022import java.util.Optional; 023import java.util.function.BiFunction; 024 025import org.apache.commons.lang3.StringUtils; 026 027import org.ametys.cms.repository.CommentableAmetysObject; 028import org.ametys.cms.repository.ReactionableObject; 029import org.ametys.cms.repository.ReactionableObjectHelper; 030import org.ametys.cms.repository.ReportableObject; 031import org.ametys.cms.repository.ReportableObjectHelper; 032import org.ametys.core.user.UserIdentity; 033import org.ametys.plugins.repository.AmetysRepositoryException; 034import org.ametys.plugins.repository.data.holder.DataHolder; 035import org.ametys.plugins.repository.data.holder.ModifiableDataHolder; 036import org.ametys.plugins.repository.data.holder.group.ModifiableComposite; 037import org.ametys.plugins.repository.data.repositorydata.ModifiableRepositoryData; 038import org.ametys.runtime.model.ModelItem; 039import org.ametys.runtime.model.type.ModelItemTypeConstants; 040/** 041 * A comment on a {@link CommentableAmetysObject} 042 */ 043public abstract class AbstractComment implements ReactionableObject, ReportableObject 044{ 045 /** Constants for comments Metadata not validted */ 046 public static final String METADATA_COMMENTS_VALIDATED = "validated"; 047 /** Constants for comments Metadata validated */ 048 public static final String METADATA_COMMENTS_NOTVALIDATED = "not-validated"; 049 /** Constants for sub comments data name */ 050 public static final String SUB_COMMENTS_DATA_NAME = "comments"; 051 /** Constants for referenced comment data name */ 052 public static final String REFERENCED_COMMENT_DATA_NAME = "reference"; 053 /** Constants for creation Metadata */ 054 public static final String METADATA_COMMENT_CREATIONDATE = "creation"; 055 /** Constants for last modification Metadata */ 056 public static final String METADATA_COMMENT_LAST_MODIFICATION_DATE = "last-modification"; 057 /** Constants for author Metadata */ 058 public static final String METADATA_COMMENT_AUTHOR = "author"; 059 /** Constants for author name Metadata */ 060 public static final String METADATA_COMMENT_AUTHORNAME = "author-name"; 061 /** Constants for author email Metadata */ 062 public static final String METADATA_COMMENT_AUTHOREMAIL = "author-email"; 063 /** Constants for author email hidden Metadata */ 064 public static final String METADATA_COMMENT_AUTHOREMAIL_HIDDEN = "author-email-hidden"; 065 /** Constants for author url Metadata */ 066 public static final String METADATA_COMMENT_AUTHORURL = "author-url"; 067 /** Constants for the content Metadata */ 068 public static final String METADATA_COMMENT_CONTENT = "content"; 069 /** Constants for the validated status Metadata */ 070 public static final String METADATA_COMMENT_VALIDATED = "validated"; 071 /** Constants for the is edited status Metadata */ 072 public static final String METADATA_COMMENT_IS_EDITED = "is-edited"; 073 /** Constants for the is deleted status Metadata */ 074 public static final String METADATA_COMMENT_IS_DELETED = "is-deleted"; 075 /** Constants for the is marked status Metadata */ 076 public static final String METADATA_COMMENT_IS_ACCEPTED = "is-accepted"; 077 /** Constant for the prefix of comments' name */ 078 public static final String COMMENT_NAME_PREFIX = "comment-"; 079 080 /** Constants for the separator */ 081 public static final String ID_SEPARATOR = "_"; 082 083 /** The root data holder where to store the first level of comments */ 084 protected ModifiableDataHolder _rootDataHolder; 085 /** The node of the comment */ 086 protected ModifiableComposite _commentComposite; 087 /** The id of the comment (unique in the ametys object) */ 088 protected String _id; 089 090 /** 091 * Retrieves a comment by its id 092 * @param rootDataHolder The root data holder hosting the first level of comments 093 * @param commentId The id of the comment to retrieve 094 * @throws AmetysRepositoryException if an error occurred 095 */ 096 public AbstractComment(ModifiableDataHolder rootDataHolder, String commentId) 097 { 098 _rootDataHolder = rootDataHolder; 099 _id = commentId; 100 101 String commentDataPath = getCommentDataPath(_id); 102 _commentComposite = _rootDataHolder.getComposite(commentDataPath); 103 } 104 105 /** 106 * Creates a new comment 107 * @param dataHolder The data holder where to add the new comment 108 */ 109 public AbstractComment(ModifiableDataHolder dataHolder) 110 { 111 this(dataHolder, Optional.empty(), Optional.empty()); 112 } 113 114 /** 115 * Creates a new comment, with the given id and creation date 116 * This method allows to create a comment from existing data (ex: data import from archive) 117 * The id is not generated here, the source is trusted. Be careful using this method 118 * @param dataHolder The data holder where to add the new comment 119 * @param commentId the comment's id 120 * @param creationDate the comment's creation date 121 */ 122 public AbstractComment(ModifiableDataHolder dataHolder, String commentId, ZonedDateTime creationDate) 123 { 124 this(dataHolder, Optional.ofNullable(commentId), Optional.ofNullable(creationDate)); 125 } 126 127 /** 128 * Creates a new comment on the content, with the given id and creation date 129 * This method allow to create a comment from existing data (ex: data import from archive) 130 * The id is not generated here, the source is trusted. Be careful using this method 131 * @param dataHolder The data holder of the content where to add the new comment 132 * @param commentId the comment's id 133 * @param creationDate the comment's creation date 134 */ 135 protected AbstractComment(ModifiableDataHolder dataHolder, Optional<String> commentId, Optional<ZonedDateTime> creationDate) 136 { 137 _rootDataHolder = dataHolder; 138 139 _id = commentId.orElseGet(() -> _getNextCommentName(_rootDataHolder)); 140 _commentComposite = _rootDataHolder.getComposite(_id, true); 141 _commentComposite.setValue(METADATA_COMMENT_CREATIONDATE, creationDate.orElseGet(ZonedDateTime::now)); 142 143 update(); 144 } 145 146 /** 147 * Creates a new sub comment of the comment 148 * @param comment The parent comment 149 */ 150 public AbstractComment(AbstractComment comment) 151 { 152 this(comment, Optional.empty(), Optional.empty()); 153 } 154 155 /** 156 * Creates a new sub comment of the comment, with the given id and creation date 157 * This method allow to create a sub comment from existing data (ex: data import from archive) 158 * The id is not generated here, the source is trusted. Be careful using this method 159 * @param comment The parent comment 160 * @param commentId the sub comment's id 161 * @param creationDate the sub comment's creation date 162 */ 163 public AbstractComment(AbstractComment comment, String commentId, ZonedDateTime creationDate) 164 { 165 this(comment, Optional.ofNullable(commentId), Optional.ofNullable(creationDate)); 166 } 167 168 /** 169 * Creates a new sub comment of the comment, with the given id and creation date 170 * This method allow to create a sub comment from existing data (ex: data import from archive) 171 * The id is not generated here, the source is trusted. Be careful using this method 172 * @param comment The parent comment 173 * @param commentId the sub comment's id 174 * @param creationDate the sub comment's creation date 175 */ 176 protected AbstractComment(AbstractComment comment, Optional<String> commentId, Optional<ZonedDateTime> creationDate) 177 { 178 _rootDataHolder = comment._rootDataHolder; 179 ModifiableComposite subCommentsDataHolder = comment._commentComposite.getComposite(SUB_COMMENTS_DATA_NAME, true); 180 181 String commentName = commentId.map(id -> StringUtils.substringAfterLast(id, ID_SEPARATOR)) 182 .orElseGet(() -> _getNextCommentName(subCommentsDataHolder)); 183 _id = commentId.orElseGet(() -> comment.getId() + ID_SEPARATOR + commentName); 184 185 _commentComposite = subCommentsDataHolder.getComposite(commentName, true); 186 _commentComposite.setValue(METADATA_COMMENT_CREATIONDATE, creationDate.orElseGet(ZonedDateTime::now)); 187 188 update(); 189 } 190 191 /** 192 * Creates a reference comment of the comment, with the given comment 193 * @param parentComment The parent comment 194 * @param refComment the referenced comment 195 */ 196 protected AbstractComment(AbstractComment parentComment, AbstractComment refComment) 197 { 198 _rootDataHolder = parentComment._rootDataHolder; 199 ModifiableComposite subCommentsDataHolder = parentComment._commentComposite.getComposite(REFERENCED_COMMENT_DATA_NAME, true); 200 201 _id = refComment.getId(); 202 _commentComposite = subCommentsDataHolder.getComposite(refComment.getId(), true); 203 refComment._commentComposite.copyTo(_commentComposite); 204 } 205 206 private static String _getNextCommentName(DataHolder dataHolder) 207 { 208 String base = COMMENT_NAME_PREFIX; 209 int i = 0; 210 while (dataHolder.hasValueOrEmpty(base + i)) 211 { 212 i++; 213 } 214 215 return base + i; 216 } 217 218 /** 219 * Retrieves the path of the comment with the given identifier 220 * @param commentId the comment identifier 221 * @return the path of the comment 222 */ 223 protected String getCommentDataPath(String commentId) 224 { 225 String[] commentIdSegments = commentId.split(ID_SEPARATOR); 226 return StringUtils.join(commentIdSegments, ModelItem.ITEM_PATH_SEPARATOR + SUB_COMMENTS_DATA_NAME + ModelItem.ITEM_PATH_SEPARATOR); 227 } 228 229 /** 230 * Retrieves the repository data of the {@link AbstractComment} 231 * @return the repository data of the {@link AbstractComment} 232 */ 233 public ModifiableRepositoryData getRepositoryData() 234 { 235 return _commentComposite.getRepositoryData(); 236 } 237 238 /** 239 * The comment id (unique to the ametys object) 240 * @return The id. Cannot be null. 241 */ 242 public String getId() 243 { 244 return _id; 245 } 246 247 /** 248 * Get the date and time the comment was created 249 * @return The non null date of creation of the comment. 250 */ 251 public ZonedDateTime getCreationDate() 252 { 253 return _commentComposite.getValue(METADATA_COMMENT_CREATIONDATE); 254 } 255 256 /** 257 * Get the date and time the comment was last modified 258 * @return The non null date of last modification of the comment. 259 */ 260 public ZonedDateTime getLastModificationDate() 261 { 262 return _commentComposite.getValue(METADATA_COMMENT_LAST_MODIFICATION_DATE); 263 } 264 265 /** 266 * Set the date and time the comment was last modified 267 * @param lastModificationDate the last modification date 268 */ 269 public void setLastModificationDate(ZonedDateTime lastModificationDate) 270 { 271 _commentComposite.setValue(METADATA_COMMENT_LAST_MODIFICATION_DATE, lastModificationDate); 272 } 273 274 /** 275 * Get the comment's author 276 * @return the comment's author 277 */ 278 public UserIdentity getAuthor() 279 { 280 return _commentComposite.getValueOfType(METADATA_COMMENT_AUTHOR, ModelItemTypeConstants.USER_ELEMENT_TYPE_ID); 281 } 282 283 /** 284 * Set the comment's author. 285 * @param author the author. Can be null to remove the name. 286 */ 287 public void setAuthor(UserIdentity author) 288 { 289 _commentComposite.setValue(METADATA_COMMENT_AUTHOR, author, ModelItemTypeConstants.USER_ELEMENT_TYPE_ID); 290 } 291 292 /** 293 * Get the readable name of the author. 294 * @return The full name. Can be null. 295 */ 296 public String getAuthorName() 297 { 298 return _commentComposite.getValueOfType(METADATA_COMMENT_AUTHORNAME, ModelItemTypeConstants.STRING_TYPE_ID); 299 } 300 301 /** 302 * Set the readable name of the author. 303 * @param name The full name. Can be null to remove the name. 304 */ 305 public void setAuthorName(String name) 306 { 307 _commentComposite.setValue(METADATA_COMMENT_AUTHORNAME, name, ModelItemTypeConstants.STRING_TYPE_ID); 308 } 309 310 /** 311 * Get the email of the author. 312 * @return The ameil. Can be null. 313 */ 314 public String getAuthorEmail() 315 { 316 return _commentComposite.getValueOfType(METADATA_COMMENT_AUTHOREMAIL, ModelItemTypeConstants.STRING_TYPE_ID); 317 } 318 319 /** 320 * Set the email of the author. 321 * @param email The email. Can be null to remove the email. 322 */ 323 public void setAuthorEmail(String email) 324 { 325 _commentComposite.setValue(METADATA_COMMENT_AUTHOREMAIL, email, ModelItemTypeConstants.STRING_TYPE_ID); 326 } 327 328 /** 329 * Get the url given by the author as its personal site url. 330 * @return The url. Can be null. 331 */ 332 public String getAuthorURL() 333 { 334 return _commentComposite.getValueOfType(METADATA_COMMENT_AUTHORURL, ModelItemTypeConstants.STRING_TYPE_ID); 335 } 336 337 /** 338 * Set the personal site url of the author 339 * @param url The url. Can be null to remove url. 340 */ 341 public void setAuthorURL(String url) 342 { 343 _commentComposite.setValue(METADATA_COMMENT_AUTHORURL, url, ModelItemTypeConstants.STRING_TYPE_ID); 344 } 345 346 /** 347 * Does the email of the authors have to be hidden ? 348 * @return true (default value) if the email does not have to appears to others users. Can still be used for administration. 349 */ 350 public boolean isEmailHidden() 351 { 352 return _commentComposite.getValueOfTypeOrDefault(METADATA_COMMENT_AUTHOREMAIL_HIDDEN, ModelItemTypeConstants.BOOLEAN_TYPE_ID, true); 353 } 354 /** 355 * Set the email hidden status. 356 * @param hideEmail true to set the email as hidden. 357 */ 358 public void setEmailHiddenStatus(boolean hideEmail) 359 { 360 _commentComposite.setValue(METADATA_COMMENT_AUTHOREMAIL_HIDDEN, hideEmail, ModelItemTypeConstants.BOOLEAN_TYPE_ID); 361 } 362 363 /** 364 * Does the comment is edited 365 * @return true the email is edited 366 */ 367 public boolean isEdited() 368 { 369 return _commentComposite.getValueOfTypeOrDefault(METADATA_COMMENT_IS_EDITED, ModelItemTypeConstants.BOOLEAN_TYPE_ID, false); 370 } 371 /** 372 * Set the comment to edited. 373 * @param isEdited true to set the comment to edited. 374 */ 375 public void setEdited(boolean isEdited) 376 { 377 _commentComposite.setValue(METADATA_COMMENT_IS_EDITED, isEdited, ModelItemTypeConstants.BOOLEAN_TYPE_ID); 378 } 379 380 /** 381 * Does the comment is deleted 382 * @return true the comment is deleted 383 */ 384 public boolean isDeleted() 385 { 386 return _commentComposite.getValueOfTypeOrDefault(METADATA_COMMENT_IS_DELETED, ModelItemTypeConstants.BOOLEAN_TYPE_ID, false); 387 } 388 /** 389 * Set the comment to deleted. 390 * @param isEdited true to set the comment to deleted. 391 */ 392 public void setDeleted(boolean isEdited) 393 { 394 _commentComposite.setValue(METADATA_COMMENT_IS_DELETED, isEdited, ModelItemTypeConstants.BOOLEAN_TYPE_ID); 395 } 396 397 /** 398 * Get the content of the comment. A simple String (with \n or \t). 399 * @return The content. Can be null. 400 */ 401 public String getContent() 402 { 403 return _commentComposite.getValueOfType(METADATA_COMMENT_CONTENT, ModelItemTypeConstants.STRING_TYPE_ID); 404 } 405 /** 406 * Set the content of the comment. 407 * @param content The content to set. Can be null to remove the content. Have to be a simple String (with \n or \t). 408 */ 409 public void setContent(String content) 410 { 411 _commentComposite.setValue(METADATA_COMMENT_CONTENT, content, ModelItemTypeConstants.STRING_TYPE_ID); 412 } 413 414 /** 415 * Is the comment validated 416 * @return the status of validation of the comment 417 */ 418 public boolean isValidated() 419 { 420 return _commentComposite.getValueOfTypeOrDefault(METADATA_COMMENT_VALIDATED, ModelItemTypeConstants.BOOLEAN_TYPE_ID, false); 421 } 422 423 /** 424 * Set the validation status of the comment 425 * @param validated true the comment is validated 426 */ 427 public void setValidated(boolean validated) 428 { 429 _commentComposite.setValue(METADATA_COMMENT_VALIDATED, validated, ModelItemTypeConstants.BOOLEAN_TYPE_ID); 430 update(); 431 } 432 433 public void addReport() 434 { 435 ReportableObjectHelper.addReport(_commentComposite); 436 } 437 438 public void setReportsCount(long reportsCount) 439 { 440 ReportableObjectHelper.setReportsCount(_commentComposite, reportsCount); 441 } 442 443 public void clearReports() 444 { 445 ReportableObjectHelper.clearReports(_commentComposite); 446 } 447 448 public long getReportsCount() 449 { 450 return ReportableObjectHelper.getReportsCount(_commentComposite); 451 } 452 453 /** 454 * Remove the comment. 455 */ 456 public void remove() 457 { 458 String commentDataPath = getCommentDataPath(_id); 459 _rootDataHolder.removeValue(commentDataPath); 460 update(); 461 } 462 463 /** 464 * Get sub comments of the comment 465 * @param <T> type of the value to retrieve 466 * @param includeNotValidatedComments True to include the comments that are not validated 467 * @param includeValidatedComments True to include the comments that are validated 468 * @return the list of comments 469 */ 470 public abstract <T extends AbstractComment> List<T> getSubComment(boolean includeNotValidatedComments, boolean includeValidatedComments); 471 472 /** 473 * Create sub comment from this comment 474 * @param <T> type of the value to retrieve 475 * @return the sub comment 476 */ 477 public abstract <T extends AbstractComment> T createSubComment(); 478 479 /** 480 * Creates a sub comment from this comment, with the given id and creation date 481 * This method allow to create a sub comment from existing data (ex: data import from archive) 482 * The id is not generated here, the source is trusted. Be careful using this method 483 * @param <T> type of the value to retrieve 484 * @param commentId the comment's id 485 * @param creationDate the comment's creation date 486 * @return the new comment 487 */ 488 public abstract <T extends AbstractComment> T createSubComment(String commentId, ZonedDateTime creationDate); 489 490 /** 491 * Get the referenced comment of the comment 492 * @param <T> type of the value to retrieve 493 * @return the referenced comment 494 */ 495 public abstract <T extends AbstractComment> T getReferencedComment(); 496 497 /** 498 * Creates a referenced comment from this comment, with the given comment 499 * @param <T> type of the value to retrieve 500 * @param comment the comment to reference 501 * @return the referenced comment 502 */ 503 public abstract <T extends AbstractComment> T createReferencedComment(T comment); 504 505 /** 506 * Update the comment tag statistics 507 */ 508 protected abstract void update(); 509 510 /** 511 * Get the sub comments of the given comment 512 * @param <T> type of the value to retrieve 513 * @param parentComment The parent comment 514 * @param includeNotValidatedComments True to include the comments that are not validated 515 * @param includeValidatedComments True to include the comments that are validated 516 * @param commentCreator function to create new comment 517 * @return the list of sub comments 518 * @throws AmetysRepositoryException If an error occurred 519 */ 520 public static <T extends AbstractComment> List<T> getComments(T parentComment, boolean includeNotValidatedComments, boolean includeValidatedComments, BiFunction<ModifiableDataHolder, String, T> commentCreator) throws AmetysRepositoryException 521 { 522 return getComments(parentComment, includeNotValidatedComments, includeValidatedComments, false, commentCreator); 523 } 524 525 /** 526 * Get the sub comments of the given comment 527 * @param <T> type of the value to retrieve 528 * @param parentComment The parent comment 529 * @param includeNotValidatedComments True to include the comments that are not validated 530 * @param includeValidatedComments True to include the comments that are validated 531 * @param withSubComment true if we want to get all child comments 532 * @param commentCreator function to create new comment 533 * @return the list of comments 534 * @throws AmetysRepositoryException If an error occurred 535 */ 536 public static <T extends AbstractComment> List<T> getComments(AbstractComment parentComment, boolean includeNotValidatedComments, boolean includeValidatedComments, boolean withSubComment, BiFunction<ModifiableDataHolder, String, T> commentCreator) throws AmetysRepositoryException 537 { 538 ModifiableComposite parentComposite = parentComment._commentComposite; 539 List<T> comments = new ArrayList<>(); 540 541 if (parentComposite.hasValue(SUB_COMMENTS_DATA_NAME, org.ametys.plugins.repository.data.type.ModelItemTypeConstants.COMPOSITE_TYPE_ID)) 542 { 543 ModifiableComposite subCommentsComposite = parentComposite.getComposite(SUB_COMMENTS_DATA_NAME); 544 return _getComments(parentComment._rootDataHolder, subCommentsComposite, parentComment.getId() + ID_SEPARATOR, includeNotValidatedComments, includeValidatedComments, withSubComment, commentCreator); 545 } 546 547 return comments; 548 } 549 550 /** 551 * Get the comments in the given root data holder 552 * @param <T> type of the value to retrieve 553 * @param rootDataHolder The root data holder 554 * @param includeNotValidatedComments True to include the comments that are not validated 555 * @param includeValidatedComments True to include the comments that are validated 556 * @param commentCreator function to create new comment 557 * @return the list of comments 558 * @throws AmetysRepositoryException If an error occurred 559 */ 560 public static <T extends AbstractComment> List<T> getComments(ModifiableDataHolder rootDataHolder, boolean includeNotValidatedComments, boolean includeValidatedComments, BiFunction<ModifiableDataHolder, String, T> commentCreator) throws AmetysRepositoryException 561 { 562 return getComments(rootDataHolder, includeNotValidatedComments, includeValidatedComments, false, commentCreator); 563 } 564 565 /** 566 * Get the comments in the given root data holder 567 * @param <T> type of the value to retrieve 568 * @param rootDataHolder The root data holder 569 * @param includeNotValidatedComments True to include the comments that are not validated 570 * @param includeValidatedComments True to include the comments that are validated 571 * @param isRecursive true if we want to have sub comments 572 * @param commentCreator function to create new comment 573 * @return the list of comments 574 * @throws AmetysRepositoryException If an error occurred 575 */ 576 public static <T extends AbstractComment> List<T> getComments(ModifiableDataHolder rootDataHolder, boolean includeNotValidatedComments, boolean includeValidatedComments, boolean isRecursive, BiFunction<ModifiableDataHolder, String, T> commentCreator) throws AmetysRepositoryException 577 { 578 return _getComments(rootDataHolder, rootDataHolder, StringUtils.EMPTY, includeNotValidatedComments, includeValidatedComments, isRecursive, commentCreator); 579 } 580 581 private static <T extends AbstractComment> List<T> _getComments(ModifiableDataHolder rootDataHolder, ModifiableDataHolder dataHolder, String commentIdPrefix, boolean includeNotValidatedComments, boolean includeValidatedComments, boolean isRecursive, BiFunction<ModifiableDataHolder, String, T> commentCreator) 582 { 583 List<T> comments = new ArrayList<>(); 584 585 for (String name : dataHolder.getDataNames()) 586 { 587 if (METADATA_COMMENTS_NOTVALIDATED.equals(name) || METADATA_COMMENTS_VALIDATED.equals(name)) 588 { 589 continue; 590 } 591 592 String id = commentIdPrefix + name; 593 T c = commentCreator.apply(rootDataHolder, id); 594 if (includeNotValidatedComments && !c.isValidated() || includeValidatedComments && c.isValidated()) 595 { 596 comments.add(c); 597 if (isRecursive) 598 { 599 comments.addAll(getComments(c, includeNotValidatedComments, includeValidatedComments, isRecursive, commentCreator)); 600 } 601 } 602 } 603 604 return comments; 605 } 606 607 /** 608 * Get the referenced comment of the given comment 609 * @param <T> type of the value to retrieve 610 * @param parentComment The parent comment 611 * @param commentCreator function to create new comment 612 * @return the referenced comment 613 * @throws AmetysRepositoryException If an error occurred 614 */ 615 public static <T extends AbstractComment> T getReferencedComment(AbstractComment parentComment, BiFunction<ModifiableDataHolder, String, T> commentCreator) throws AmetysRepositoryException 616 { 617 ModifiableComposite parentComposite = parentComment._commentComposite; 618 if (parentComposite.hasValue(REFERENCED_COMMENT_DATA_NAME, org.ametys.plugins.repository.data.type.ModelItemTypeConstants.COMPOSITE_TYPE_ID)) 619 { 620 ModifiableComposite refComposite = parentComposite.getComposite(REFERENCED_COMMENT_DATA_NAME); 621 String refCommentId = refComposite.getDataNames().stream() 622 .findFirst() 623 .orElse(null); 624 return commentCreator.apply(refComposite, refCommentId); 625 } 626 627 return null; 628 } 629 630 @Override 631 public void addReaction(UserIdentity user, ReactionType reactionType) 632 { 633 ReactionableObjectHelper.addReaction(_commentComposite, user, reactionType); 634 } 635 636 @Override 637 public void removeReaction(UserIdentity user, ReactionType reactionType) 638 { 639 ReactionableObjectHelper.removeReaction(_commentComposite, user, reactionType); 640 } 641 642 @Override 643 public List<UserIdentity> getReactionUsers(ReactionType reactionType) 644 { 645 return ReactionableObjectHelper.getReactionUsers(_commentComposite, reactionType); 646 } 647 648 /** 649 * Get parent of comment if exists 650 * @param <T> type of the value to retrieve 651 * @return the comment parent. null if the comment is not a sub comment 652 */ 653 public abstract <T extends AbstractComment> T getCommentParent(); 654 655 /** 656 * Check if the comment is a sub-comment from an other comment 657 * @return true if the comment is a sub-comment 658 */ 659 public boolean isSubComment() 660 { 661 return this.getId().contains(ID_SEPARATOR); 662 } 663 664 /** 665 * Check if the comment have any sub-comments 666 * @return true if the comment have any sub-comments 667 */ 668 public boolean hasSubComments() 669 { 670 return !this.getSubComment(true, true).isEmpty(); 671 } 672 673 /** 674 * Is the comment the accepted answer 675 * @return true if the comment is the accepted answer 676 */ 677 public boolean isAccepted() 678 { 679 return _commentComposite.getValueOfTypeOrDefault(METADATA_COMMENT_IS_ACCEPTED, ModelItemTypeConstants.BOOLEAN_TYPE_ID, false); 680 } 681 682 /** 683 * Set the comment as accepted answer. 684 * @param isAccepted true to set the comment as accepted answer. 685 */ 686 public void setAccepted(boolean isAccepted) 687 { 688 _commentComposite.setValue(METADATA_COMMENT_IS_ACCEPTED, isAccepted, ModelItemTypeConstants.BOOLEAN_TYPE_ID); 689 } 690 691}