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.ModifiableDataHolder;
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(ModifiableDataHolder 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(ModifiableDataHolder 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(ModifiableDataHolder contentUnversionedDataHolder, String commentId, ZonedDateTime creationDate)
075    {
076        this(contentUnversionedDataHolder, Optional.ofNullable(commentId), Optional.ofNullable(creationDate));
077    }
078    
079    private RichTextComment(ModifiableDataHolder 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     * Creates a reference comment of the comment, with the given comment
113     * @param parentComment The parent comment
114     * @param refComment the referenced comment
115     */
116    public RichTextComment(RichTextComment parentComment, AbstractComment refComment)
117    {
118        super(parentComment, refComment);
119    }
120    
121    /**
122     * Get a comment
123     * @param contentUnversionedDataHolder the content data holder
124     * @param commentId The comment identifier
125     * @return The comment
126     * @throws AmetysRepositoryException if the comment does not exist
127     */
128    public static RichTextComment getComment(ModifiableDataHolder contentUnversionedDataHolder, String commentId) throws AmetysRepositoryException
129    {
130        return new RichTextComment(contentUnversionedDataHolder, commentId);
131    }
132    
133    /**
134     * Get the comments of a content
135     * @param <T> type of the value to retrieve
136     * @param parentComment The parent comment
137     * @param includeNotValidatedComments True to include the comments that are not validated
138     * @param includeValidatedComments True to include the comments that are validated
139     * @return the list of comments
140     * @throws AmetysRepositoryException If an error occurred
141     */
142    public static <T extends AbstractComment> List<T> getComments(RichTextComment parentComment, boolean includeNotValidatedComments, boolean includeValidatedComments) throws AmetysRepositoryException
143    {
144        return getComments(parentComment, includeNotValidatedComments, includeValidatedComments, false);
145    }
146    
147    /**
148     * Get the comments of a content
149     * @param <T> type of the value to retrieve
150     * @param parentComment The parent comment
151     * @param includeNotValidatedComments True to include the comments that are not validated
152     * @param includeValidatedComments True to include the comments that are validated
153     * @param withSubComment true if we want to get all child comments
154     * @return the list of comments
155     * @throws AmetysRepositoryException If an error occurred
156     */
157    @SuppressWarnings("unchecked")
158    public static <T extends AbstractComment> List<T> getComments(RichTextComment parentComment, boolean includeNotValidatedComments, boolean includeValidatedComments, boolean withSubComment) throws AmetysRepositoryException
159    {
160        return (List<T>) getComments(parentComment, includeNotValidatedComments, includeValidatedComments, withSubComment, getCreationFunction());
161    }
162    
163    /**
164     * Get the comments of a content
165     * @param <T> type of the value to retrieve
166     * @param contentUnversionedDataHolder The content unversioned data holder
167     * @param includeNotValidatedComments True to include the comments that are not validated
168     * @param includeValidatedComments True to include the comments that are validated
169     * @return the list of comments
170     * @throws AmetysRepositoryException If an error occurred
171     */
172    public static <T extends AbstractComment> List<T>  getComments(ModifiableDataHolder contentUnversionedDataHolder, boolean includeNotValidatedComments, boolean includeValidatedComments) throws AmetysRepositoryException
173    {
174        return getComments(contentUnversionedDataHolder, includeNotValidatedComments, includeValidatedComments, false);
175    }
176    
177    /**
178     * Get the comments of a content
179     * @param <T> type of the value to retrieve
180     * @param contentUnversionedDataHolder The content unversioned metadata holder
181     * @param includeNotValidatedComments True to include the comments that are not validated
182     * @param includeValidatedComments True to include the comments that are validated
183     * @param isRecursive true if we want to have sub comments
184     * @return the list of comments
185     * @throws AmetysRepositoryException If an error occurred
186     */
187    public static <T extends AbstractComment> List<T> getComments(ModifiableDataHolder contentUnversionedDataHolder, boolean includeNotValidatedComments, boolean includeValidatedComments, boolean isRecursive) throws AmetysRepositoryException
188    {
189        return getComments(contentUnversionedDataHolder, includeNotValidatedComments, includeValidatedComments, isRecursive, getCreationFunction());
190    }
191
192    @Override
193    protected void update()
194    {
195        long validated = 0;
196        long notValidated = 0;
197
198        ModifiableDataHolder dataHolder = _rootDataHolder;
199        if (isSubComment())
200        {
201            dataHolder = getCommentParent()._rootDataHolder;
202        }
203        
204        List<RichTextComment> comments = getComments(dataHolder, true, true, false, getCreationFunction());
205
206        for (AbstractComment comment : comments)
207        {
208            if (comment.isValidated())
209            {
210                validated++;
211            }
212            else
213            {
214                notValidated++;
215            }
216        }
217
218        dataHolder.setValue(METADATA_COMMENTS_VALIDATED, validated);
219        dataHolder.setValue(METADATA_COMMENTS_NOTVALIDATED, notValidated);
220    }
221    
222    @SuppressWarnings("unchecked")
223    @Override
224    public RichTextComment getCommentParent()
225    {
226        if (isSubComment())
227        {
228            String parentId = StringUtils.substringBeforeLast(this.getId(), ID_SEPARATOR);
229            return new RichTextComment(_rootDataHolder, parentId);
230        }
231        return null;
232    }
233
234    @SuppressWarnings("unchecked")
235    @Override
236    public List<RichTextComment> getSubComment(boolean includeNotValidatedComments, boolean includeValidatedComments)
237    {
238        return getComments(this, includeNotValidatedComments, includeValidatedComments);
239    }
240    
241    @SuppressWarnings("unchecked")
242    @Override
243    public RichTextComment getReferencedComment()
244    {
245        return getReferencedComment(this, getCreationFunction());
246    }
247    
248    /**
249     * Create sub comment from this comment
250     * @return the sub comment
251     */
252    @SuppressWarnings("unchecked")
253    @Override
254    public <T extends AbstractComment> T createSubComment()
255    {
256        return (T) new RichTextComment(this);
257    }
258
259    @SuppressWarnings("unchecked")
260    @Override
261    public <T extends AbstractComment> T createSubComment(String commentId, ZonedDateTime creationDate)
262    {
263        return (T) new RichTextComment(this, commentId, creationDate);
264    }
265    
266    @SuppressWarnings("unchecked")
267    @Override
268    public RichTextComment createReferencedComment(AbstractComment comment)
269    {
270        return new RichTextComment(this, comment);
271    }
272    
273    @SuppressWarnings("unchecked")
274    private static <T extends AbstractComment> BiFunction<ModifiableDataHolder, String, T> getCreationFunction()
275    {
276        return (dataHolder, name) -> {
277            RichTextComment comment = new RichTextComment(dataHolder, name);
278            return (T) comment;
279        };
280    }
281    
282    /**
283     * Set rich text comment
284     * @param richText the content of the comment
285     */
286    public void setRichTextContent(RichText richText)
287    {
288        _commentComposite.setValue(METADATA_RICHTEXT_COMMENT_CONTENT, richText, org.ametys.cms.data.type.ModelItemTypeConstants.RICH_TEXT_ELEMENT_TYPE_ID);
289    }
290    
291    /**
292     * Get the rich text content
293     * @return rich text content
294     */
295    public RichText getRichTextContent()
296    {
297        return _commentComposite.getValueOfType(METADATA_RICHTEXT_COMMENT_CONTENT, org.ametys.cms.data.type.ModelItemTypeConstants.RICH_TEXT_ELEMENT_TYPE_ID);
298    }
299    
300    /**
301     * Check if there is a rich text content
302     * @return true if there is a rich text content
303     */
304    public boolean hasRichTextContent()
305    {
306        return _commentComposite.hasValue(METADATA_RICHTEXT_COMMENT_CONTENT);
307    }
308    
309}