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.web.repository.content.jcr; 017 018import java.time.ZonedDateTime; 019import java.util.Date; 020import java.util.List; 021import java.util.Locale; 022import java.util.Map; 023 024import javax.jcr.Node; 025import javax.jcr.Property; 026import javax.jcr.RepositoryException; 027 028import org.ametys.cms.content.references.OutgoingReferences; 029import org.ametys.cms.data.holder.ModifiableIndexableDataHolder; 030import org.ametys.cms.repository.ModifiableContentDataHolder; 031import org.ametys.cms.repository.ModifiableWorkflowAwareContent; 032import org.ametys.cms.repository.comment.Comment; 033import org.ametys.cms.repository.comment.CommentableContent; 034import org.ametys.cms.repository.comment.contributor.ContributorCommentableContent; 035import org.ametys.cms.trash.ContentTrashElementType; 036import org.ametys.core.user.UserIdentity; 037import org.ametys.plugins.repository.AmetysRepositoryException; 038import org.ametys.plugins.repository.RepositoryIntegrityViolationException; 039import org.ametys.plugins.repository.data.extractor.xml.XMLValuesExtractorAdditionalDataGetter; 040import org.ametys.plugins.repository.data.holder.ModifiableDataHolder; 041import org.ametys.plugins.repository.data.repositorydata.impl.JCRRepositoryData; 042import org.ametys.plugins.repository.jcr.DublinCoreHelper; 043import org.ametys.plugins.repository.jcr.JCRTraversableAmetysObject; 044import org.ametys.plugins.repository.jcr.NameHelper; 045import org.ametys.plugins.repository.jcr.NameHelper.NameComputationMode; 046import org.ametys.plugins.repository.jcr.NodeHelper; 047import org.ametys.plugins.repository.lock.LockableAmetysObject; 048import org.ametys.plugins.repository.tag.TaggableAmetysObjectHelper; 049import org.ametys.plugins.repository.trash.TrashElement; 050import org.ametys.plugins.repository.trash.TrashableAmetysObject; 051import org.ametys.plugins.repository.trash.UnknownParentException; 052import org.ametys.runtime.model.type.ModelItemTypeConstants; 053import org.ametys.web.repository.content.ModifiableWebContent; 054 055/** 056 * Modifiable version of {@link DefaultWebContent}. 057 * @param <F> the actual type of factory. 058 */ 059public class ModifiableDefaultWebContent<F extends ModifiableDefaultWebContentFactory> extends DefaultWebContent<F> implements ModifiableWebContent, ModifiableWorkflowAwareContent, LockableAmetysObject, CommentableContent, ContributorCommentableContent, TrashableAmetysObject//, CopiableAmetysObject, JCRTraversableAmetysObject 060{ 061 private boolean _lockAlreadyChecked; 062 063 /** 064 * Creates a {@link ModifiableDefaultWebContent}. 065 * @param node the node backing this {@link ModifiableDefaultWebContent}. 066 * @param parentPath the parent path in the Ametys hierarchy. 067 * @param factory the {@link DefaultWebContentFactory} which creates the AmetysObject. 068 */ 069 public ModifiableDefaultWebContent(Node node, String parentPath, F factory) 070 { 071 super(node, parentPath, factory); 072 } 073 074 @Override 075 public void remove() throws AmetysRepositoryException, RepositoryIntegrityViolationException 076 { 077 _getFactory().getSharedContentManager().switchSharedContentReferences(this); 078 super.remove(); 079 } 080 081 @Override 082 public void setSiteName(String siteName) 083 { 084 setLockInfoOnCurrentContext(); 085 getInternalDataHolder().setValue(METADATA_SITE, siteName, ModelItemTypeConstants.STRING_TYPE_ID); 086 } 087 088 @Override 089 public void setPrivacyLevel(String privacy) 090 { 091 setLockInfoOnCurrentContext(); 092 getUnversionedDataHolder().setValue(PROPERTY_PRIVACY, privacy, ModelItemTypeConstants.STRING_TYPE_ID); 093 } 094 095 @Override 096 public void setTitle(String title, Locale locale) throws AmetysRepositoryException 097 { 098 _getFactory().getModifiableContentHelper().setTitle(this, title, locale); 099 } 100 101 @Override 102 @Deprecated 103 public void setTitle(String title) throws AmetysRepositoryException 104 { 105 _getFactory().getModifiableContentHelper().setTitle(this, title); 106 } 107 108 public void setCreator(UserIdentity user) throws AmetysRepositoryException 109 { 110 _getFactory().getModifiableContentHelper().setCreator(this, user); 111 } 112 113 public void setCreationDate(ZonedDateTime creationDate) throws AmetysRepositoryException 114 { 115 _getFactory().getModifiableContentHelper().setCreationDate(this, creationDate); 116 } 117 118 public void setLastContributor(UserIdentity user) 119 { 120 _getFactory().getModifiableContentHelper().setLastContributor(this, user); 121 } 122 123 public void setLastModified(ZonedDateTime lastModified) throws AmetysRepositoryException 124 { 125 _getFactory().getModifiableContentHelper().setLastModified(this, lastModified); 126 } 127 128 public void setFirstValidator(UserIdentity user) throws AmetysRepositoryException 129 { 130 _getFactory().getModifiableContentHelper().setFirstValidator(this, user); 131 } 132 133 @Override 134 public void setFirstValidationDate(ZonedDateTime validationDate) throws AmetysRepositoryException 135 { 136 _getFactory().getModifiableContentHelper().setFirstValidationDate(this, validationDate); 137 } 138 139 public void setLastValidator(UserIdentity user) throws AmetysRepositoryException 140 { 141 _getFactory().getModifiableContentHelper().setLastValidator(this, user); 142 } 143 144 @Override 145 public void setLastValidationDate(ZonedDateTime validationDate) throws AmetysRepositoryException 146 { 147 _getFactory().getModifiableContentHelper().setLastValidationDate(this, validationDate); 148 } 149 150 public void setLastMajorValidator(UserIdentity user) throws AmetysRepositoryException 151 { 152 _getFactory().getModifiableContentHelper().setLastMajorValidator(this, user); 153 } 154 155 @Override 156 public void setLastMajorValidationDate(ZonedDateTime validationDate) throws AmetysRepositoryException 157 { 158 _getFactory().getModifiableContentHelper().setLastMajorValidationDate(this, validationDate); 159 } 160 161 @Override 162 public void setOutgoingReferences(Map<String, OutgoingReferences> references) throws AmetysRepositoryException 163 { 164 _getFactory().getModifiableContentHelper().setOutgoingReferences(this, references); 165 _outgoingReferences = references; 166 } 167 168 // ------------------- Tags management -------------------- // 169 @Override 170 public void tag(String tag) throws AmetysRepositoryException 171 { 172 TaggableAmetysObjectHelper.tag(this, tag); 173 } 174 175 @Override 176 public void untag(String tag) throws AmetysRepositoryException 177 { 178 TaggableAmetysObjectHelper.untag(this, tag); 179 } 180 181 // Lock management. // 182 183 @Override 184 public void lock() throws AmetysRepositoryException 185 { 186 _getFactory().getLockComponent().lock(this); 187 188 // the lock component immediately detached the lock token, so we have to check it again at next usage 189 _lockAlreadyChecked = false; 190 } 191 192 @Override 193 public void unlock() throws AmetysRepositoryException 194 { 195 _getFactory().getLockComponent().unlock(this); 196 197 // the lock component removed the lock token on unlock 198 _lockAlreadyChecked = true; 199 } 200 201 @Override 202 public void setLockInfoOnCurrentContext() throws AmetysRepositoryException 203 { 204 if (!_lockAlreadyChecked) 205 { 206 _getFactory().getLockComponent().setLockTokenOnCurrentSession(this); 207 _lockAlreadyChecked = true; 208 } 209 } 210 211 @Override 212 public boolean isLocked() throws AmetysRepositoryException 213 { 214 return _getFactory().getLockComponent().isLocked(this); 215 } 216 217 @Override 218 public UserIdentity getLockOwner() throws AmetysRepositoryException 219 { 220 return _getFactory().getLockComponent().getLockOwner(this); 221 } 222 223 // Dublin Core metadata. // 224 225 @Override 226 public void setDCTitle(String title) throws AmetysRepositoryException 227 { 228 DublinCoreHelper.setDCTitle(this, title); 229 } 230 231 @Override 232 public void setDCCreator(String creator) throws AmetysRepositoryException 233 { 234 DublinCoreHelper.setDCCreator(this, creator); 235 } 236 237 @Override 238 public void setDCSubject(String[] subject) throws AmetysRepositoryException 239 { 240 DublinCoreHelper.setDCSubject(this, subject); 241 } 242 243 @Override 244 public void setDCDescription(String description) throws AmetysRepositoryException 245 { 246 DublinCoreHelper.setDCDescription(this, description); 247 } 248 249 @Override 250 public void setDCPublisher(String publisher) throws AmetysRepositoryException 251 { 252 DublinCoreHelper.setDCPublisher(this, publisher); 253 } 254 255 @Override 256 public void setDCContributor(String contributor) throws AmetysRepositoryException 257 { 258 DublinCoreHelper.setDCContributor(this, contributor); 259 } 260 261 @Override 262 public void setDCDate(Date date) throws AmetysRepositoryException 263 { 264 DublinCoreHelper.setDCDate(this, date); 265 } 266 267 @Override 268 public void setDCType(String type) throws AmetysRepositoryException 269 { 270 DublinCoreHelper.setDCType(this, type); 271 } 272 273 @Override 274 public void setDCFormat(String format) throws AmetysRepositoryException 275 { 276 DublinCoreHelper.setDCFormat(this, format); 277 } 278 279 @Override 280 public void setDCIdentifier(String identifier) throws AmetysRepositoryException 281 { 282 DublinCoreHelper.setDCIdentifier(this, identifier); 283 } 284 285 @Override 286 public void setDCSource(String source) throws AmetysRepositoryException 287 { 288 DublinCoreHelper.setDCSource(this, source); 289 } 290 291 @Override 292 public void setDCLanguage(String language) throws AmetysRepositoryException 293 { 294 DublinCoreHelper.setDCLanguage(this, language); 295 } 296 297 @Override 298 public void setDCRelation(String relation) throws AmetysRepositoryException 299 { 300 DublinCoreHelper.setDCRelation(this, relation); 301 } 302 303 @Override 304 public void setDCCoverage(String coverage) throws AmetysRepositoryException 305 { 306 DublinCoreHelper.setDCCoverage(this, coverage); 307 } 308 309 @Override 310 public void setDCRights(String rights) throws AmetysRepositoryException 311 { 312 DublinCoreHelper.setDCRights(this, rights); 313 } 314 315 @Override 316 public Comment createComment() 317 { 318 return new Comment(_getFactory().getModifiableContentHelper().getCommentsDataHolder(this, true)); 319 } 320 321 public Comment createComment(String commentId, ZonedDateTime creationDate) 322 { 323 return new Comment(_getFactory().getModifiableContentHelper().getCommentsDataHolder(this, true), commentId, creationDate); 324 } 325 326 @Override 327 public Comment getComment(String commentId) throws AmetysRepositoryException 328 { 329 ModifiableDataHolder commentsDataHolder = _getFactory().getModifiableContentHelper().getCommentsDataHolder(this, false); 330 return commentsDataHolder != null ? Comment.getComment(commentsDataHolder, commentId) : null; 331 } 332 333 @Override 334 public List<Comment> getComments(boolean includeNotValidatedComments, boolean includeValidatedComments) throws AmetysRepositoryException 335 { 336 ModifiableDataHolder commentsDataHolder = _getFactory().getModifiableContentHelper().getCommentsDataHolder(this, false); 337 return commentsDataHolder != null ? Comment.getComments(commentsDataHolder, includeNotValidatedComments, includeValidatedComments) : List.of(); 338 } 339 340 @Override 341 public Comment createContributorComment() 342 { 343 return new Comment(_getFactory().getModifiableContentHelper().getContributorCommentsDataHolder(this, true)); 344 } 345 346 public Comment createContributorComment(String commentId, ZonedDateTime creationDate) 347 { 348 return new Comment(_getFactory().getModifiableContentHelper().getContributorCommentsDataHolder(this, true), commentId, creationDate); 349 } 350 351 @Override 352 public Comment getContributorComment(String commentId) throws AmetysRepositoryException 353 { 354 ModifiableDataHolder commentsDataHolder = _getFactory().getModifiableContentHelper().getContributorCommentsDataHolder(this, false); 355 return commentsDataHolder != null ? Comment.getComment(commentsDataHolder, commentId) : null; 356 } 357 358 @Override 359 public List<Comment> getContributorComments() throws AmetysRepositoryException 360 { 361 ModifiableDataHolder commentsDataHolder = _getFactory().getModifiableContentHelper().getContributorCommentsDataHolder(this, false); 362 return commentsDataHolder != null ? Comment.getComments(commentsDataHolder, false, true) : List.of(); 363 } 364 365 @Override 366 protected ModifiableIndexableDataHolder getDataHolder() 367 { 368 // It is necessary to instantiate the repository data because of locks: 369 // when the content is locked, the lock token is released to be taken by anyone 370 // in repository data there is a lock checked boolean that need to be reset in order to take the token 371 // this boolean is only reset at instantiation 372 return new ModifiableContentDataHolder(this, _getFactory().getContentDataHelper(), new JCRRepositoryData(getNode()), _getFactory().getModel(this)); 373 } 374 375 public void fillContent(org.w3c.dom.Node node, XMLValuesExtractorAdditionalDataGetter additionalDataGetter) throws Exception 376 { 377 _getFactory().getContentExtractor().fillContent(this, node, additionalDataGetter); 378 } 379 380 public TrashElement moveToTrash() throws AmetysRepositoryException 381 { 382 TrashElement trashElement = _getFactory()._getTrashElementDAO().createTrashElement(this, getTitle()); 383 trashElement.setValue(ContentTrashElementType.CONTENT_TYPES, getTypes()); 384 385 // Clone the ametys object from the original session to trash session 386 Node storedNode = NodeHelper.cloneNode(getNode(), trashElement.getNode()); 387 388 try 389 { 390 storedNode.setProperty("ametys-internal:path", this.getParentPath()); 391 } 392 catch (RepositoryException e) 393 { 394 throw new AmetysRepositoryException("Fail to store the content path", e); 395 } 396 397 // Remove the node from the original session 398 remove(); 399 400 return trashElement; 401 } 402 403 public ModifiableDefaultWebContent restoreFromTrash(MergeComputationMode mergeComputationMode) throws UnknownParentException 404 { 405 try 406 { 407 Property property = getNode().getProperty("ametys-internal:path"); 408 String parentPath = property.getValue().getString(); 409 property.remove(); 410 JCRTraversableAmetysObject parent = _getFactory()._getAmetysObjectResolver().resolveByPath(parentPath); 411 // Get the node name, can be adjust if another content has already the same node name 412 String nodeName = NameHelper.getUniqueAmetysObjectName(parent, getName(), NameComputationMode.GENERATED_KEY, true); 413 414 // Create the hashed nodes 415 Node hashNode = NodeHelper.getOrCreateFinalHashNode(parent.getNode(), nodeName); 416 417 // Clone the ametys object from the trash session to default session 418 NodeHelper.cloneNode(getNode(), hashNode, nodeName); 419 420 // Remove the node from the trash session 421 remove(); 422 423 ModifiableDefaultWebContent restoredContent = parent.getChild(nodeName); 424 restoredContent.saveChanges(); 425 restoredContent.checkpoint(); 426 return restoredContent; 427 } 428 catch (RepositoryException e) 429 { 430 throw new AmetysRepositoryException("failed to retrieve the content path", e); 431 } 432 } 433}