001/*
002 *  Copyright 2024 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;
017
018import java.time.ZonedDateTime;
019import java.util.List;
020import java.util.Optional;
021import java.util.function.BiFunction;
022
023import org.apache.commons.lang3.StringUtils;
024
025import org.ametys.cms.data.RichText;
026import org.ametys.cms.repository.CommentableAmetysObject;
027import org.ametys.plugins.repository.AmetysRepositoryException;
028import org.ametys.plugins.repository.data.holder.ModifiableModelLessDataHolder;
029
030/**
031 * A comment on a {@link CommentableAmetysObject} with rich text content
032 */
033public class RichTextComment extends AbstractComment
034{
035
036    /** Constants for the content Metadata */
037    public static final String METADATA_RICHTEXT_COMMENT_CONTENT = "richtextcontent";
038
039    /** Attribute Description for editing */
040    public static final String ATTRIBUTE_CONTENT_FOR_EDITING = "contentEditing";
041    /** Attribute Description for editing */
042    public static final String ATTRIBUTE_CONTENT_FOR_RENDERING = "contentRendering";
043    /** Attribute Description abstract */
044    public static final String ATTRIBUTE_CONTENT_ABSTRACT = "contentAbstract";
045    
046    /**
047     * Retrieves a comment by its id
048     * @param contentUnversionedDataHolder The unversioned data holder of the content hosting the comment
049     * @param commentId The id of the comment to retrieve
050     * @throws AmetysRepositoryException if an error occurred
051     */
052    public RichTextComment(ModifiableModelLessDataHolder contentUnversionedDataHolder, String commentId)
053    {
054        super(contentUnversionedDataHolder, commentId);
055    }
056    
057    /**
058     * Creates a new comment on the content
059     * @param contentUnversionedDataHolder The unversioned data holder of the content where to add the new comment 
060     */
061    public RichTextComment(ModifiableModelLessDataHolder contentUnversionedDataHolder)
062    {
063        this(contentUnversionedDataHolder, Optional.empty(), Optional.empty());
064    }
065    
066    /**
067     * Creates a new comment on the content, with the given id and creation date
068     * This method allow to create a comment from existing data (ex: data import from archive) 
069     * The id is not generated here, the source is trusted. Be careful using this method
070     * @param contentUnversionedDataHolder The unversioned data holder of the content where to add the new comment
071     * @param commentId the comment's id
072     * @param creationDate the comment's creation date
073     */
074    public RichTextComment(ModifiableModelLessDataHolder contentUnversionedDataHolder, String commentId, ZonedDateTime creationDate)
075    {
076        this(contentUnversionedDataHolder, Optional.ofNullable(commentId), Optional.ofNullable(creationDate));
077    }
078    
079    private RichTextComment(ModifiableModelLessDataHolder contentUnversionedDataHolder, Optional<String> commentId, Optional<ZonedDateTime> creationDate)
080    {
081        super(contentUnversionedDataHolder, commentId, creationDate);
082    }
083    
084    /**
085     * Creates a new sub comment of the comment
086     * @param comment The parent comment
087     */
088    public RichTextComment(RichTextComment comment)
089    {
090        this(comment, Optional.empty(), Optional.empty());
091    }
092    
093    /**
094     * Creates a new sub comment of the comment, with the given id and creation date
095     * This method allow to create a sub comment from existing data (ex: data import from archive) 
096     * The id is not generated here, the source is trusted. Be careful using this method
097     * @param comment The parent comment
098     * @param commentId the sub comment's id
099     * @param creationDate the sub comment's creation date
100     */
101    public RichTextComment(RichTextComment comment, String commentId, ZonedDateTime creationDate)
102    {
103        this(comment, Optional.ofNullable(commentId), Optional.ofNullable(creationDate));
104    }
105    
106    private RichTextComment(RichTextComment comment, Optional<String> commentId, Optional<ZonedDateTime> creationDate)
107    {
108        super(comment, commentId, creationDate);
109    }
110
111    /**
112     * Get a comment
113     * @param contentUnversionedDataHolder the content data holder
114     * @param commentId The comment identifier
115     * @return The comment
116     * @throws AmetysRepositoryException if the comment does not exist
117     */
118    public static RichTextComment getComment(ModifiableModelLessDataHolder contentUnversionedDataHolder, String commentId) throws AmetysRepositoryException
119    {
120        return new RichTextComment(contentUnversionedDataHolder, commentId);
121    }
122    
123    /**
124     * Get the comments of a content 
125     * @param <T> type of the value to retrieve
126     * @param parentComment The parent comment
127     * @param includeNotValidatedComments True to include the comments that are not validated
128     * @param includeValidatedComments True to include the comments that are validated
129     * @return the list of comments
130     * @throws AmetysRepositoryException If an error occurred
131     */
132    public static <T extends AbstractComment> List<T> getComments(RichTextComment parentComment, boolean includeNotValidatedComments, boolean includeValidatedComments) throws AmetysRepositoryException
133    {
134        return getComments(parentComment, includeNotValidatedComments, includeValidatedComments, false);
135    }
136    
137    /**
138     * Get the comments of a content 
139     * @param <T> type of the value to retrieve
140     * @param parentComment The parent comment
141     * @param includeNotValidatedComments True to include the comments that are not validated
142     * @param includeValidatedComments True to include the comments that are validated
143     * @param withSubComment true if we want to get all child comments
144     * @return the list of comments
145     * @throws AmetysRepositoryException If an error occurred
146     */
147    @SuppressWarnings("unchecked")
148    public static <T extends AbstractComment> List<T> getComments(RichTextComment parentComment, boolean includeNotValidatedComments, boolean includeValidatedComments, boolean withSubComment) throws AmetysRepositoryException
149    {
150        return (List<T>) getComments(parentComment, includeNotValidatedComments, includeValidatedComments, withSubComment, getCreationFunction());
151    }
152    
153    /**
154     * Get the comments of a content 
155     * @param <T> type of the value to retrieve
156     * @param contentUnversionedDataHolder The content unversioned data holder
157     * @param includeNotValidatedComments True to include the comments that are not validated
158     * @param includeValidatedComments True to include the comments that are validated
159     * @return the list of comments
160     * @throws AmetysRepositoryException If an error occurred
161     */
162    public static <T extends AbstractComment> List<T>  getComments(ModifiableModelLessDataHolder contentUnversionedDataHolder, boolean includeNotValidatedComments, boolean includeValidatedComments) throws AmetysRepositoryException
163    {
164        return getComments(contentUnversionedDataHolder, includeNotValidatedComments, includeValidatedComments, false);
165    }
166    
167    /**
168     * Get the comments of a content 
169     * @param <T> type of the value to retrieve
170     * @param contentUnversionedDataHolder The content unversioned metadata holder
171     * @param includeNotValidatedComments True to include the comments that are not validated
172     * @param includeValidatedComments True to include the comments that are validated
173     * @param isRecursive true if we want to have sub comments
174     * @return the list of comments
175     * @throws AmetysRepositoryException If an error occurred
176     */
177    public static <T extends AbstractComment> List<T> getComments(ModifiableModelLessDataHolder contentUnversionedDataHolder, boolean includeNotValidatedComments, boolean includeValidatedComments, boolean isRecursive) throws AmetysRepositoryException
178    {
179        return getComments(contentUnversionedDataHolder, includeNotValidatedComments, includeValidatedComments, isRecursive, getCreationFunction());
180    }
181
182    @Override
183    protected void update()
184    {
185        long validated = 0;
186        long notValidated = 0;
187
188        ModifiableModelLessDataHolder dataHolder = _rootDataHolder;
189        if (isSubComment())
190        {
191            dataHolder = getCommentParent()._rootDataHolder;
192        }
193        
194        List<RichTextComment> comments = getComments(dataHolder, true, true, false, getCreationFunction());
195
196        for (AbstractComment comment : comments)
197        {
198            if (comment.isValidated())
199            {
200                validated++;
201            }
202            else
203            {
204                notValidated++;
205            }
206        }
207
208        dataHolder.setValue(METADATA_COMMENTS_VALIDATED, validated);
209        dataHolder.setValue(METADATA_COMMENTS_NOTVALIDATED, notValidated);
210    }
211    
212    @SuppressWarnings("unchecked")
213    @Override
214    public RichTextComment getCommentParent()
215    {
216        if (isSubComment())
217        {
218            String parentId = StringUtils.substringBeforeLast(this.getId(), ID_SEPARATOR);
219            return new RichTextComment(_rootDataHolder, parentId);
220        }
221        return null;
222    }
223
224    @SuppressWarnings("unchecked")
225    @Override
226    public List<RichTextComment> getSubComment(boolean includeNotValidatedComments, boolean includeValidatedComments)
227    {
228        return getComments(this, includeNotValidatedComments, includeValidatedComments);
229    }
230    
231    /**
232     * Create sub comment from this comment
233     * @return the sub comment
234     */
235    @SuppressWarnings("unchecked")
236    @Override
237    public <T extends AbstractComment> T createSubComment()
238    {
239        return (T) new RichTextComment(this);
240    }
241
242    @SuppressWarnings("unchecked")
243    @Override
244    public <T extends AbstractComment> T createSubComment(String commentId, ZonedDateTime creationDate)
245    {
246        return (T) new RichTextComment(this, commentId, creationDate);
247    }
248    
249    
250    @SuppressWarnings("unchecked")
251    private static <T extends AbstractComment> BiFunction<ModifiableModelLessDataHolder, String, T> getCreationFunction() 
252    {
253        return (dataHolder, name) -> {
254            RichTextComment comment = new RichTextComment(dataHolder, name);
255            return (T) comment;
256        };
257    }
258    
259    /**
260     * Set rich text comment
261     * @param richText the content of the comment
262     */
263    public void setRichTextContent(RichText richText)
264    {
265        _commentComposite.setValue(METADATA_RICHTEXT_COMMENT_CONTENT, richText, org.ametys.cms.data.type.ModelItemTypeConstants.RICH_TEXT_ELEMENT_TYPE_ID);
266    }
267    
268    /**
269     * Get the rich text content
270     * @return rich text content
271     */
272    public RichText getRichTextContent()
273    {
274        return _commentComposite.getValueOfType(METADATA_RICHTEXT_COMMENT_CONTENT, org.ametys.cms.data.type.ModelItemTypeConstants.RICH_TEXT_ELEMENT_TYPE_ID);
275    }
276    
277    /**
278     * Check if there is a rich text content
279     * @return true if there is a rich text content
280     */
281    public boolean hasRichTextContent()
282    {
283        return _commentComposite.hasValue(METADATA_RICHTEXT_COMMENT_CONTENT);
284    }
285
286}