001/*
002 *  Copyright 2019 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.data;
017
018import java.util.ArrayList;
019import java.util.Arrays;
020import java.util.Collection;
021import java.util.HashMap;
022import java.util.List;
023import java.util.Map;
024import java.util.stream.Collectors;
025
026import org.apache.commons.lang3.StringUtils;
027import org.apache.jackrabbit.util.Text;
028
029import org.ametys.cms.data.type.ResourceElementTypeHelper;
030import org.ametys.plugins.repository.RepositoryConstants;
031import org.ametys.plugins.repository.data.repositorydata.RepositoryData;
032
033/**
034 * CLass representing a rich text
035 */
036public class RichText extends Resource
037{
038    private static final String __ATTACHMENT_CONTENT_IDENTIFIER = "content";
039    
040    private static final String __RICH_TEXT_MIME_TYPE = "text/xml";
041    private static final String __RICH_TEXT_ENCODING = "UTF-8";
042    
043    /** the rich text's annotations */
044    protected Map<String, List<String>> _annotations = new HashMap<>();
045    
046    /** The resource's repository data */
047    protected RepositoryData _folderData;
048    
049    /** the files contained in the rich text */
050    protected Map<String, NamedResource> _attachments = new HashMap<>();
051    
052    /** the files contained in the rich text */
053    protected Map<String, NamedResource> _addedAttachments = new HashMap<>();
054    
055    /** the files contained in the rich text */
056    protected Collection<String> _removedAttachments = new ArrayList<>();
057    
058    /**
059     * Default constructor
060     */
061    public RichText()
062    {
063        // Empty contructor
064    }
065    
066    /**
067     * Constructor to use when reading the rich text from the repository
068     * @param richTextData the repository data containing the rich text's data
069     * @param folderData the repository data containing the folder's data
070     */
071    public RichText(RepositoryData richTextData, RepositoryData folderData)
072    {
073        super(richTextData);
074        _folderData = folderData;
075    }
076    
077    @Override
078    public String getMimeType()
079    {
080        return __RICH_TEXT_MIME_TYPE;
081    }
082    
083    @Override
084    public void setMimeType(String mimeType)
085    {
086        if (__RICH_TEXT_MIME_TYPE.equals(mimeType))
087        {
088            super.setMimeType(mimeType);
089        }
090        else
091        {
092            throw new IllegalArgumentException("The mime type '" + mimeType + "' is not authorized for rich texts. The only authorized mime type is '" + __RICH_TEXT_MIME_TYPE + "'.");
093        }
094    }
095    
096    @Override
097    public String getEncoding()
098    {
099        return __RICH_TEXT_ENCODING;
100    }
101    
102    @Override
103    public void setEncoding(String encoding)
104    {
105        if (__RICH_TEXT_ENCODING.equals(encoding))
106        {
107            super.setEncoding(encoding);
108        }
109        else
110        {
111            throw new IllegalArgumentException("The encoding '" + encoding + "' is not authorized for rich texts. The only authorized encoding is '" + __RICH_TEXT_ENCODING + "'.");
112        }
113    }
114
115    /**
116     * Retrieves the rich text's annotations
117     * @return the rich text's annotations
118     */
119    public Map<String, List<String>> getAllAnnotations()
120    {
121        return _annotations;
122    }
123
124    /**
125     * Retrieves the rich text's annotations of the given name
126     * @param name the name of the annotations to retrieve
127     * @return the rich text's annotations of the given name
128     */
129    public List<String> getAnnotations(String name)
130    {
131        return _annotations.containsKey(name) ? _annotations.get(name) : new ArrayList<>();
132    }
133
134    /**
135     * Add annotations to the rich text
136     * @param name the name of the annotations to add
137     * @param values annotations to add
138     */
139    public void addAnnotations(String name, String... values)
140    {
141        List<String> allValues = new ArrayList<>();
142        if (_annotations.containsKey(name))
143        {
144            allValues.addAll(_annotations.get(name));
145        }
146        
147        allValues.addAll(Arrays.asList(values));
148        _annotations.put(name, allValues);
149    }
150
151    /**
152     * Removes all rich text's annotations
153     */
154    public void removeAllAnnotations()
155    {
156        _annotations = new HashMap<>();
157    }
158
159    /**
160     * Removes the rich text's annotations of the given name
161     * @param name the name of the annotations to remove
162     */
163    public void removeAnnotations(String name)
164    {
165        _annotations.remove(name);
166    }
167    
168    /**
169     * Retrieves the names of the rich text's attachments
170     * @return the names of the rich text's attachments
171     */
172    public Collection<String> getAttachmentNames()
173    {
174        if (_folderData != null)
175        {
176            return _folderData.getDataNames(StringUtils.EMPTY).stream()
177                                                              .map(Text::unescapeIllegalJcrChars)
178                                                              .collect(Collectors.toSet());
179        }
180        else
181        {
182            return _attachments.keySet();
183        }
184    }
185    
186    /**
187     * Retrieves the rich text's attachments
188     * @return the rich text's attachments
189     */
190    public Collection<NamedResource> getAttachments()
191    {
192        if (_folderData != null)
193        {
194            return _getAttachmentsFromFolderData();
195        }
196        else
197        {
198            return _attachments.values();
199        }
200    }
201    
202    private List<NamedResource> _getAttachmentsFromFolderData()
203    {
204        List<NamedResource> attachments = new ArrayList<>();
205        
206        for (String escapedFileName : _folderData.getDataNames(StringUtils.EMPTY))
207        {
208            NamedResource file = _getAttachmentFromFolderData(Text.unescapeIllegalJcrChars(escapedFileName));
209            
210            attachments.add(file);
211        }
212        
213        return attachments;
214    }
215    
216    /**
217     * Retrieves the attachments that have been added to the rich text  
218     * @return the added attachments
219     */
220    public Collection<NamedResource> getAddedAttachments()
221    {
222        return _addedAttachments.values();
223    }
224    
225    /**
226     * Retrieves the names of the removed attachments of the rich text
227     * @return the names of the removed attachments
228     */
229    public Collection<String> getRemovedAttachments()
230    {
231        return _removedAttachments;
232    }
233    
234    /**
235     * Checks if the rich text has an attachment with the given name
236     * @param filename the name of the attachment
237     * @return <code>true</code> if the rich text has an attachment with the given name, <code>false</code> otherwise
238     */
239    public boolean hasAttachment(String filename)
240    {
241        if (_folderData != null)
242        {
243            return _folderData.hasValue(Text.escapeIllegalJcrChars(filename), StringUtils.EMPTY);
244        }
245        else
246        {
247            return _attachments.containsKey(filename);
248        }
249    }
250
251    /**
252     * Retrieves the rich text's attachment with the given name, or <code>null</code> if there is no such attachment
253     * @param filename the name of the attachment to retrieve
254     * @return the rich text's attachment with the given name
255     */
256    public NamedResource getAttachment(String filename)
257    {
258        if (_folderData != null)
259        {
260            return _getAttachmentFromFolderData(filename);
261        }
262        else
263        {
264            return _attachments.get(filename);
265        }
266    }
267
268    private NamedResource _getAttachmentFromFolderData(String filename)
269    {
270        String escapedFilename = Text.escapeIllegalJcrChars(filename);
271        RepositoryData attachmentData = ResourceElementTypeHelper.getRepositoryData(_folderData, RepositoryConstants.FILE_NODETYPE, escapedFilename, StringUtils.EMPTY);
272        RepositoryData resourceData = ResourceElementTypeHelper.getRepositoryData(attachmentData, RepositoryConstants.RESOURCE_NODETYPE, __ATTACHMENT_CONTENT_IDENTIFIER, ResourceElementTypeHelper.METADATA_PREFIX);
273        
274        NamedResource attachment = new NamedResource(resourceData);
275        ResourceElementTypeHelper.readResourceData(resourceData, attachment);
276        attachment.setFilename(filename);
277        
278        return attachment;
279    }
280    
281    /**
282     * Adds an attachment to the rich text
283     * @param attachment the attachment to add
284     */
285    public void addAttachment(NamedResource attachment)
286    {
287        String filename = attachment.getFilename();
288        
289        _addedAttachments.put(filename, attachment);
290        _attachments.put(filename, attachment);
291        
292        if (_removedAttachments.contains(filename))
293        {
294            _removedAttachments.remove(filename);
295        }
296        
297        _folderData = null;
298    }
299
300    /**
301     * Remove an attachment from the rich text
302     * @param filename the name of the attachment to remove
303     */
304    public void removeAttachment(String filename)
305    {
306        if (_attachments.containsKey(filename))
307        {
308            _removedAttachments.add(filename);
309            _attachments.remove(filename);
310        
311            if (_addedAttachments.containsKey(filename))
312            {
313                _addedAttachments.remove(filename);
314            }
315            
316            _folderData = null;
317        }
318    }
319
320    /**
321     * Remove all the attachments from the rich text
322     */
323    public void removeAttachments()
324    {
325        _removedAttachments.addAll(_attachments.keySet());
326        _attachments = new HashMap<>();
327        _addedAttachments = new HashMap<>();
328        _folderData = null;
329    }
330}