001/* 002 * Copyright 2012 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; 017 018import java.util.Date; 019import java.util.List; 020import java.util.Locale; 021import java.util.Map; 022 023import javax.jcr.Node; 024import javax.jcr.RepositoryException; 025 026import org.apache.avalon.framework.component.Component; 027import org.apache.avalon.framework.service.ServiceException; 028import org.apache.avalon.framework.service.ServiceManager; 029import org.apache.avalon.framework.service.Serviceable; 030 031import org.ametys.cms.content.references.OutgoingReferences; 032import org.ametys.cms.contenttype.ContentTypesHelper; 033import org.ametys.cms.contenttype.MetadataDefinition; 034import org.ametys.cms.contenttype.MetadataType; 035import org.ametys.core.user.UserIdentity; 036import org.ametys.plugins.repository.AmetysRepositoryException; 037import org.ametys.plugins.repository.RepositoryConstants; 038 039 040/** 041 * Provides helper methods to use the {@link ModifiableContent} API on {@link DefaultContent}s. 042 */ 043public class ModifiableContentHelper implements Component, Serviceable 044{ 045 /** The Avalon role */ 046 public static final String ROLE = ModifiableContentHelper.class.getName(); 047 048 /** Constants for the root outgoing references node */ 049 public static final String METADATA_ROOT_OUTGOING_REFERENCES = "root-outgoing-references"; 050 051 /** Constants for the outgoing references node */ 052 public static final String METADATA_OUTGOING_REFERENCES = "outgoing-references"; 053 054 /** Constants for the outgoing references path property */ 055 public static final String METADATA_OUTGOING_REFERENCES_PATH_PROPERTY = "path"; 056 057 /** Constants for the outgoing reference property */ 058 public static final String METADATA_OUTGOING_REFERENCE_PROPERTY = "reference"; 059 060 /** Constants for the outgoing reference property */ 061 public static final String METADATA_OUTGOING_REFERENCE_NODETYPE = "reference"; 062 063 /** Constants for language Metadata* */ 064 public static final String METADATA_LANGUAGE = "language"; 065 066 /** Constants for title Metadata* */ 067 public static final String METADATA_TITLE = "title"; 068 069 /** Constants for author Metadata* */ 070 public static final String METADATA_CREATOR = "creator"; 071 072 /** Constants for lastModified Metadata* */ 073 public static final String METADATA_CREATION = "creationDate"; 074 075 /** Constants for lastValidationDate Metadata* */ 076 public static final String METADATA_LAST_VALIDATION = "lastValidationDate"; 077 078 /** Constants for lastMajorValidationDate Metadata* */ 079 public static final String METADATA_LAST_MAJORVALIDATION = "lastMajorValidationDate"; 080 081 /** Constants for last contributor Metadata* */ 082 public static final String METADATA_CONTRIBUTOR = "contributor"; 083 084 /** Constants for lastModified Metadata* */ 085 public static final String METADATA_MODIFIED = "lastModified"; 086 087 private ContentTypesHelper _contentTypesHelper; 088 089 public void service(ServiceManager manager) throws ServiceException 090 { 091 _contentTypesHelper = (ContentTypesHelper) manager.lookup(ContentTypesHelper.ROLE); 092 } 093 094 /** 095 * Set a {@link DefaultContent} type. 096 * @param content the {@link DefaultContent} to set. 097 * @param type the type. 098 * @throws AmetysRepositoryException if an error occurs. 099 */ 100 public void setType(DefaultContent content, String type) throws AmetysRepositoryException 101 { 102 setTypes(content, new String[] {type}); 103 } 104 105 /** 106 * Set {@link DefaultContent} types. 107 * @param content the {@link DefaultContent} to set. 108 * @param types the types. 109 * @throws AmetysRepositoryException if an error occurs. 110 */ 111 public void setTypes(DefaultContent content, String[] types) throws AmetysRepositoryException 112 { 113 Node node = content.getNode(); 114 115 try 116 { 117 content._checkLock(); 118 node.setProperty(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + DefaultContent.METADATA_CONTENTTYPE, types); 119 } 120 catch (RepositoryException e) 121 { 122 throw new AmetysRepositoryException("Unable to set contentType property", e); 123 } 124 } 125 126 /** 127 * Set {@link DefaultContent} mixins. 128 * @param content the {@link DefaultContent} to set. 129 * @param mixins the mixins. 130 * @throws AmetysRepositoryException if an error occurs. 131 */ 132 public void setMixinTypes(DefaultContent content, String[] mixins) throws AmetysRepositoryException 133 { 134 Node node = content.getNode(); 135 136 try 137 { 138 content._checkLock(); 139 node.setProperty(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + DefaultContent.METADATA_MIXINCONTENTTYPES, mixins); 140 } 141 catch (RepositoryException e) 142 { 143 throw new AmetysRepositoryException("Unable to set mixins property", e); 144 } 145 } 146 147 /** 148 * Set a {@link DefaultContent} language. 149 * @param content the {@link DefaultContent} to modify. 150 * @param language the language to set. 151 * @throws AmetysRepositoryException if an error occurs. 152 */ 153 public void setLanguage(DefaultContent content, String language) throws AmetysRepositoryException 154 { 155 Node node = content.getNode(); 156 157 try 158 { 159 content._checkLock(); 160 node.setProperty(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + METADATA_LANGUAGE, language); 161 } 162 catch (RepositoryException e) 163 { 164 throw new AmetysRepositoryException("Unable to set language property", e); 165 } 166 } 167 168 /** 169 * Set a {@link DefaultContent} title for the given locale. 170 * Be careful ! Can be invoked only if the metadata title already exists. 171 * @param content the {@link DefaultContent} to set. 172 * @param title the title to set. 173 * @param locale The locale. Can be null if the content is not a multilingual content. 174 * @throws AmetysRepositoryException if an error occurs. 175 */ 176 public void setTitle(DefaultContent content, String title, Locale locale) throws AmetysRepositoryException 177 { 178 MetadataDefinition metaDef = _contentTypesHelper.getMetadataDefinition(METADATA_TITLE, content); 179 if (metaDef == null) 180 { 181 throw new IllegalArgumentException("Mandatory title metadata definition is not defined"); 182 } 183 184 MetadataType type = metaDef.getType(); 185 if (MetadataType.MULTILINGUAL_STRING.equals(type)) 186 { 187 if (locale == null) 188 { 189 throw new IllegalArgumentException("Cannot set a title with null locale on a multilingual title"); 190 } 191 content.getMetadataHolder().setMetadata(METADATA_TITLE, title, locale); 192 } 193 else 194 { 195 content.getMetadataHolder().setMetadata(METADATA_TITLE, title); 196 } 197 } 198 199 /** 200 * Set the title of non-multilingual {@link DefaultContent}. 201 * Be careful ! Use only if content's title is not a multilingual string. If not sure use {@link #setTitle(DefaultContent, String, Locale)} instead. 202 * @param content the {@link DefaultContent} to set. 203 * @param title the title to set. 204 * @throws AmetysRepositoryException if an error occurs. 205 */ 206 public void setTitle(DefaultContent content, String title) throws AmetysRepositoryException 207 { 208 setTitle(content, title, null); 209 } 210 211 /** 212 * Copy the title of the source content to the target content 213 * @param srcContent The source content 214 * @param targetContent The target content 215 * @throws AmetysRepositoryException if an error occurs. 216 */ 217 public void copyTitle(Content srcContent, ModifiableContent targetContent) throws AmetysRepositoryException 218 { 219 org.ametys.plugins.repository.metadata.CompositeMetadata.MetadataType type = srcContent.getMetadataHolder().getType(METADATA_TITLE); 220 if (MetadataType.MULTILINGUAL_STRING.equals(type)) 221 { 222 targetContent.getMetadataHolder().setMetadata(METADATA_TITLE, srcContent.getMetadataHolder().getMultilingualString(METADATA_TITLE)); 223 } 224 else 225 { 226 targetContent.getMetadataHolder().setMetadata(METADATA_TITLE, srcContent.getTitle()); 227 } 228 } 229 230 /** 231 * Set a {@link DefaultContent} user. 232 * @param content the {@link DefaultContent} to set. 233 * @param user the user to set. 234 * @throws AmetysRepositoryException if an error occurs. 235 */ 236 public void setCreator(DefaultContent content, UserIdentity user) throws AmetysRepositoryException 237 { 238 try 239 { 240 Node creatorNode = null; 241 if (content.getNode().hasNode(RepositoryConstants.NAMESPACE_PREFIX + ':' + METADATA_CREATOR)) 242 { 243 creatorNode = content.getNode().getNode(RepositoryConstants.NAMESPACE_PREFIX + ':' + METADATA_CREATOR); 244 } 245 else 246 { 247 creatorNode = content.getNode().addNode(RepositoryConstants.NAMESPACE_PREFIX + ':' + METADATA_CREATOR, RepositoryConstants.USER_NODETYPE); 248 } 249 creatorNode.setProperty(RepositoryConstants.NAMESPACE_PREFIX + ":login", user.getLogin()); 250 creatorNode.setProperty(RepositoryConstants.NAMESPACE_PREFIX + ":population", user.getPopulationId()); 251 } 252 catch (RepositoryException e) 253 { 254 throw new AmetysRepositoryException("Error setting the creator property.", e); 255 } 256 } 257 258 /** 259 * Set a {@link DefaultContent} creation date. 260 * @param content the {@link DefaultContent} to set. 261 * @param creationDate the creation date to set. 262 * @throws AmetysRepositoryException if an error occurs. 263 */ 264 public void setCreationDate(DefaultContent content, Date creationDate) throws AmetysRepositoryException 265 { 266 content.getMetadataHolder().setMetadata(METADATA_CREATION, creationDate); 267 } 268 269 /** 270 * Set a {@link DefaultContent} contributor. 271 * @param content the {@link DefaultContent} to set. 272 * @param user the contributor to set. 273 * @throws AmetysRepositoryException if an error occurs. 274 */ 275 public void setLastContributor(DefaultContent content, UserIdentity user) throws AmetysRepositoryException 276 { 277 try 278 { 279 Node creatorNode = null; 280 if (content.getNode().hasNode(RepositoryConstants.NAMESPACE_PREFIX + ':' + METADATA_CONTRIBUTOR)) 281 { 282 creatorNode = content.getNode().getNode(RepositoryConstants.NAMESPACE_PREFIX + ':' + METADATA_CONTRIBUTOR); 283 } 284 else 285 { 286 creatorNode = content.getNode().addNode(RepositoryConstants.NAMESPACE_PREFIX + ':' + METADATA_CONTRIBUTOR, RepositoryConstants.USER_NODETYPE); 287 } 288 creatorNode.setProperty(RepositoryConstants.NAMESPACE_PREFIX + ":login", user.getLogin()); 289 creatorNode.setProperty(RepositoryConstants.NAMESPACE_PREFIX + ":population", user.getPopulationId()); 290 } 291 catch (RepositoryException e) 292 { 293 throw new AmetysRepositoryException("Error setting the last contributor property.", e); 294 } 295 } 296 297 /** 298 * Set a {@link DefaultContent} last modification date. 299 * @param content the {@link DefaultContent} to set. 300 * @param lastModified the last modification date to set. 301 * @throws AmetysRepositoryException if an error occurs. 302 */ 303 public void setLastModified(DefaultContent content, Date lastModified) throws AmetysRepositoryException 304 { 305 content.getMetadataHolder().setMetadata(METADATA_MODIFIED, lastModified); 306 } 307 308 /** 309 * Set a {@link DefaultContent} last validation date. 310 * @param content the {@link DefaultContent} to set. 311 * @param validationDate the last validation date. 312 * @throws AmetysRepositoryException if an error occurs. 313 */ 314 public void setLastValidationDate(DefaultContent content, Date validationDate) throws AmetysRepositoryException 315 { 316 content.getMetadataHolder().setMetadata(METADATA_LAST_VALIDATION, validationDate); 317 } 318 319 /** 320 * Set a {@link DefaultContent} last major validation date. 321 * @param content the {@link DefaultContent} to set. 322 * @param validationDate the last major validation date. 323 * @throws AmetysRepositoryException if an error occurs. 324 */ 325 public void setLastMajorValidationDate(DefaultContent content, Date validationDate) throws AmetysRepositoryException 326 { 327 content.getMetadataHolder().setMetadata(METADATA_LAST_MAJORVALIDATION, validationDate); 328 } 329 330 /** 331 * Store the outgoing references on the content. 332 * @param content The content concerned by these outgoing references. 333 * @param references A non null map of outgoing references grouped by metadata (key are metadata path) 334 * @throws AmetysRepositoryException if an error occurs. 335 */ 336 public void setOutgoingReferences(DefaultContent content, Map<String, OutgoingReferences> references) throws AmetysRepositoryException 337 { 338 try 339 { 340 Node contentNode = content.getNode(); 341 if (contentNode.hasNode(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + METADATA_ROOT_OUTGOING_REFERENCES)) 342 { 343 contentNode.getNode(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + METADATA_ROOT_OUTGOING_REFERENCES).remove(); 344 } 345 346 if (references.size() != 0) 347 { 348 Node rootOutgoingRefsNode = contentNode.addNode(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + METADATA_ROOT_OUTGOING_REFERENCES); 349 for (String path : references.keySet()) 350 { 351 // Outgoing references node creation (for the given path) 352 Node outgoingRefsNode = rootOutgoingRefsNode.addNode(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + METADATA_OUTGOING_REFERENCES); 353 outgoingRefsNode.setProperty(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + METADATA_OUTGOING_REFERENCES_PATH_PROPERTY, path); 354 355 // Reference nodes per type 356 OutgoingReferences outgoingReferences = references.get(path); 357 358 for (String type : outgoingReferences.keySet()) 359 { 360 List<String> referenceValues = outgoingReferences.get(type); 361 if (referenceValues != null && !referenceValues.isEmpty()) 362 { 363 Node outgoingReferenceNode = outgoingRefsNode.addNode(type, RepositoryConstants.NAMESPACE_PREFIX + ':' + METADATA_OUTGOING_REFERENCE_NODETYPE); 364 outgoingReferenceNode.setProperty(RepositoryConstants.NAMESPACE_PREFIX + ':' + METADATA_OUTGOING_REFERENCE_PROPERTY, referenceValues.toArray(new String[referenceValues.size()])); 365 } 366 } 367 } 368 } 369 } 370 catch (RepositoryException e) 371 { 372 throw new AmetysRepositoryException(e); 373 } 374 } 375}