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