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.HashSet; 023import java.util.List; 024import java.util.Map; 025import java.util.Set; 026import java.util.stream.Collectors; 027 028import org.apache.commons.lang3.StringUtils; 029import org.apache.jackrabbit.util.Text; 030 031import org.ametys.cms.data.type.ResourceElementTypeHelper; 032import org.ametys.plugins.repository.RepositoryConstants; 033import org.ametys.plugins.repository.data.repositorydata.RepositoryData; 034 035/** 036 * CLass representing a rich text 037 */ 038public class RichText extends Resource 039{ 040 private static final String __ATTACHMENT_CONTENT_IDENTIFIER = "content"; 041 042 private static final Set<String> __AVAILABLE_MIME_TYPES_FOR_RICH_TEXT; 043 private static final Set<String> __AVAILABLE_ENCODINGS_FOR_RICH_TEXT; 044 045 static 046 { 047 __AVAILABLE_MIME_TYPES_FOR_RICH_TEXT = new HashSet<>(); 048 __AVAILABLE_MIME_TYPES_FOR_RICH_TEXT.add("text/xml"); 049 __AVAILABLE_MIME_TYPES_FOR_RICH_TEXT.add("application/xml"); 050 __AVAILABLE_MIME_TYPES_FOR_RICH_TEXT.add("text/plain"); 051 052 __AVAILABLE_ENCODINGS_FOR_RICH_TEXT = new HashSet<>(); 053 __AVAILABLE_ENCODINGS_FOR_RICH_TEXT.add("UTF-8"); 054 } 055 056 /** the rich text's annotations */ 057 protected Map<String, List<String>> _annotations = new HashMap<>(); 058 059 /** The resource's repository data */ 060 protected RepositoryData _folderData; 061 062 /** the files contained in the rich text */ 063 protected Map<String, NamedResource> _fetchedAttachments = new HashMap<>(); 064 065 /** the files contained in the rich text */ 066 protected Collection<String> _removedAttachments = new ArrayList<>(); 067 068 /** 069 * Default constructor 070 */ 071 public RichText() 072 { 073 // Empty contructor 074 } 075 076 /** 077 * Constructor to use when reading the rich text from the repository 078 * @param richTextData the repository data containing the rich text's data 079 * @param folderData the repository data containing the folder's data 080 */ 081 public RichText(RepositoryData richTextData, RepositoryData folderData) 082 { 083 super(richTextData); 084 _folderData = folderData; 085 } 086 087 @Override 088 public void setMimeType(String mimeType) 089 { 090 if (__AVAILABLE_MIME_TYPES_FOR_RICH_TEXT.stream().anyMatch(availableMimeType -> availableMimeType.equalsIgnoreCase(mimeType))) 091 { 092 super.setMimeType(mimeType); 093 } 094 else 095 { 096 String availableMimeTypes = StringUtils.join(__AVAILABLE_MIME_TYPES_FOR_RICH_TEXT, ", "); 097 throw new IllegalArgumentException("The mime type '" + mimeType + "' is not available for rich texts. Available mime types are: '" + availableMimeTypes + "'."); 098 } 099 } 100 101 @Override 102 public void setEncoding(String encoding) 103 { 104 if (__AVAILABLE_ENCODINGS_FOR_RICH_TEXT.stream().anyMatch(availableEncoding -> availableEncoding.equalsIgnoreCase(encoding))) 105 { 106 super.setEncoding(encoding); 107 } 108 else 109 { 110 String availableEncodings = StringUtils.join(__AVAILABLE_ENCODINGS_FOR_RICH_TEXT, ", "); 111 throw new IllegalArgumentException("The encoding '" + encoding + "' is not available for rich texts. Available encodings are: '" + availableEncodings + "'."); 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 Collection<String> attachnmentNames = new HashSet<>(); 175 attachnmentNames.addAll(_fetchedAttachments.keySet()); 176 177 if (_folderData != null) 178 { 179 attachnmentNames.addAll(_folderData.getDataNames(StringUtils.EMPTY).stream() 180 .map(Text::unescapeIllegalJcrChars) 181 .filter(name -> !_fetchedAttachments.containsKey(name)) 182 .filter(name -> !_removedAttachments.contains(name)) 183 .collect(Collectors.toSet())); 184 } 185 186 return attachnmentNames; 187 } 188 189 /** 190 * Retrieves the rich text's attachments 191 * @return the rich text's attachments 192 */ 193 public Collection<NamedResource> getAttachments() 194 { 195 _fetchAttachments(); 196 return _fetchedAttachments.values(); 197 } 198 199 private void _fetchAttachments() 200 { 201 for (String filename : getAttachmentNames()) 202 { 203 _fetchAttachment(filename); 204 } 205 } 206 207 /** 208 * Retrieves the attachments that have already been fetched 209 * @return the fetched attachments 210 */ 211 public Collection<NamedResource> getFetchedAttachments() 212 { 213 return _fetchedAttachments.values(); 214 } 215 216 /** 217 * Retrieves the names of the removed attachments of the rich text 218 * @return the names of the removed attachments 219 */ 220 public Collection<String> getRemovedAttachments() 221 { 222 return _removedAttachments; 223 } 224 225 /** 226 * Checks if the rich text has an attachment with the given name 227 * @param filename the name of the attachment 228 * @return <code>true</code> if the rich text has an attachment with the given name, <code>false</code> otherwise 229 */ 230 public boolean hasAttachment(String filename) 231 { 232 return getAttachmentNames().contains(filename); 233 } 234 235 /** 236 * Retrieves the rich text's attachment with the given name, or <code>null</code> if there is no such attachment 237 * @param filename the name of the attachment to retrieve 238 * @return the rich text's attachment with the given name 239 */ 240 public NamedResource getAttachment(String filename) 241 { 242 _fetchAttachment(filename); 243 return _fetchedAttachments.get(filename); 244 } 245 246 private void _fetchAttachment(String filename) 247 { 248 if (!_fetchedAttachments.containsKey(filename) && _folderData != null) 249 { 250 String escapedFilename = Text.escapeIllegalJcrChars(filename); 251 RepositoryData attachmentData = ResourceElementTypeHelper.getRepositoryData(_folderData, RepositoryConstants.FILE_NODETYPE, escapedFilename, StringUtils.EMPTY); 252 253 if (attachmentData != null) // The attachment to fetch could be missing in the repository 254 { 255 RepositoryData resourceData = ResourceElementTypeHelper.getRepositoryData(attachmentData, RepositoryConstants.RESOURCE_NODETYPE, __ATTACHMENT_CONTENT_IDENTIFIER, ResourceElementTypeHelper.METADATA_PREFIX); 256 257 NamedResource attachment = new NamedResource(resourceData); 258 ResourceElementTypeHelper.readResourceData(resourceData, attachment); 259 attachment.setFilename(filename); 260 261 _fetchedAttachments.put(filename, attachment); 262 } 263 } 264 } 265 266 /** 267 * Adds an attachment to the rich text 268 * @param attachment the attachment to add 269 */ 270 public void addAttachment(NamedResource attachment) 271 { 272 String filename = attachment.getFilename(); 273 274 _fetchedAttachments.put(filename, attachment); 275 276 if (_removedAttachments.contains(filename)) 277 { 278 _removedAttachments.remove(filename); 279 } 280 } 281 282 /** 283 * Remove an attachment from the rich text 284 * @param filename the name of the attachment to remove 285 */ 286 public void removeAttachment(String filename) 287 { 288 _removedAttachments.add(filename); 289 _fetchedAttachments.remove(filename); 290 } 291 292 /** 293 * Remove all the attachments from the rich text 294 */ 295 public void removeAttachments() 296 { 297 _removedAttachments.addAll(getAttachmentNames()); 298 _fetchedAttachments = new HashMap<>(); 299 } 300}