001/* 002 * Copyright 2010 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.time.ZonedDateTime; 019import java.util.ArrayList; 020import java.util.Calendar; 021import java.util.Collection; 022import java.util.Date; 023import java.util.HashMap; 024import java.util.LinkedHashSet; 025import java.util.List; 026import java.util.Locale; 027import java.util.Map; 028import java.util.Optional; 029import java.util.Set; 030 031import javax.jcr.Node; 032import javax.jcr.NodeIterator; 033import javax.jcr.RepositoryException; 034import javax.jcr.Value; 035 036import org.xml.sax.ContentHandler; 037import org.xml.sax.SAXException; 038 039import org.ametys.cms.content.references.OutgoingReferences; 040import org.ametys.cms.content.references.OutgoingReferencesHelper; 041import org.ametys.cms.contenttype.ContentType; 042import org.ametys.cms.data.holder.IndexableDataHolder; 043import org.ametys.cms.data.holder.impl.DefaultModelAwareDataHolder; 044import org.ametys.cms.search.model.SystemPropertyExtensionPoint; 045import org.ametys.core.user.UserIdentity; 046import org.ametys.core.util.DateUtils; 047import org.ametys.plugins.explorer.resources.ResourceCollection; 048import org.ametys.plugins.repository.AmetysObject; 049import org.ametys.plugins.repository.AmetysObjectIterable; 050import org.ametys.plugins.repository.AmetysObjectResolver; 051import org.ametys.plugins.repository.AmetysRepositoryException; 052import org.ametys.plugins.repository.CopiableAmetysObject; 053import org.ametys.plugins.repository.ModifiableTraversableAmetysObject; 054import org.ametys.plugins.repository.RepositoryConstants; 055import org.ametys.plugins.repository.RepositoryIntegrityViolationException; 056import org.ametys.plugins.repository.UnknownAmetysObjectException; 057import org.ametys.plugins.repository.data.UnknownDataException; 058import org.ametys.plugins.repository.data.holder.ModifiableModelLessDataHolder; 059import org.ametys.plugins.repository.data.holder.impl.DefaultModifiableModelLessDataHolder; 060import org.ametys.plugins.repository.data.repositorydata.ModifiableRepositoryData; 061import org.ametys.plugins.repository.data.repositorydata.RepositoryData; 062import org.ametys.plugins.repository.data.repositorydata.impl.JCRRepositoryData; 063import org.ametys.plugins.repository.dublincore.DCMITypes; 064import org.ametys.plugins.repository.jcr.DefaultAmetysObject; 065import org.ametys.plugins.repository.jcr.DublinCoreHelper; 066import org.ametys.plugins.repository.jcr.JCRTraversableAmetysObject; 067import org.ametys.plugins.repository.lock.LockableAmetysObject; 068import org.ametys.plugins.repository.metadata.MultilingualString; 069import org.ametys.plugins.repository.tag.TaggableAmetysObjectHelper; 070import org.ametys.runtime.model.View; 071import org.ametys.runtime.model.type.DataContext; 072import org.ametys.runtime.model.type.ModelItemTypeConstants; 073 074/** 075 * Default implementation of a {@link Content}, also versionable, lockable and workflow-aware. 076 * @param <F> the actual type of factory. 077 */ 078public class DefaultContent<F extends ContentFactory> extends DefaultAmetysObject<F> implements Content, CopiableAmetysObject, JCRTraversableAmetysObject, ReactionableObject, ReportableObject 079{ 080 /** Constants for the root outgoing references node */ 081 public static final String METADATA_ROOT_OUTGOING_REFERENCES = "root-outgoing-references"; 082 083 /** Constants for the outgoing references node */ 084 public static final String METADATA_OUTGOING_REFERENCES = "outgoing-references"; 085 086 /** Constants for the outgoing references path property */ 087 public static final String METADATA_OUTGOING_REFERENCES_PATH_PROPERTY = "path"; 088 089 /** Constants for the outgoing reference property */ 090 public static final String METADATA_OUTGOING_REFERENCE_PROPERTY = "reference"; 091 092 /** Constants for the outgoing reference nodetype */ 093 public static final String METADATA_OUTGOING_REFERENCE_NODETYPE = "reference"; 094 095 /** Constants for language Metadata* */ 096 public static final String METADATA_LANGUAGE = "language"; 097 098 /** Constants for author Metadata* */ 099 public static final String METADATA_CREATOR = "creator"; 100 101 /** Constants for lastModified Metadata* */ 102 public static final String METADATA_CREATION = "creationDate"; 103 104 /** Constants for the first validator metadata */ 105 public static final String METADATA_FIRST_VALIDATOR = "firstValidator"; 106 107 /** Constants for firstValidationDate Metadata* */ 108 public static final String METADATA_FIRST_VALIDATION = "firstValidationDate"; 109 110 /** Constants for last validator Metadata* */ 111 public static final String METADATA_LAST_VALIDATOR = "lastValidator"; 112 113 /** Constants for lastValidationDate Metadata* */ 114 public static final String METADATA_LAST_VALIDATION = "lastValidationDate"; 115 116 /** Constants for last major validator Metadata* */ 117 public static final String METADATA_LAST_MAJOR_VALIDATOR = "lastMajorValidator"; 118 119 /** Constants for lastMajorValidationDate Metadata* */ 120 public static final String METADATA_LAST_MAJORVALIDATION = "lastMajorValidationDate"; 121 122 /** Constants for last contributor Metadata* */ 123 public static final String METADATA_CONTRIBUTOR = "contributor"; 124 125 /** Constants for lastModified Metadata* */ 126 public static final String METADATA_MODIFIED = "lastModified"; 127 128 /** Constants for contentType Metadata* */ 129 public static final String METADATA_CONTENTTYPE = "contentType"; 130 131 /** Constants for contentType Metadata* */ 132 public static final String METADATA_MIXINCONTENTTYPES = "mixins"; 133 134 /** Constant for the attachment node name. */ 135 public static final String ATTACHMENTS_NODE_NAME = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":attachments"; 136 137 /** The default locale for content */ 138 public static final Locale DEFAULT_CONTENT_LOCALE = Locale.ENGLISH; 139 140 /** The type identifiers of this content */ 141 protected String[] _types; 142 /** The mixin type identifiers of this content */ 143 protected String[] _mixins; 144 145 /** 146 * Creates a JCR-based Content. 147 * @param node the JCR Node backing this Content. 148 * @param parentPath the parent path in the Ametys hierarchy. 149 * @param factory the corresponding {@link ContentFactory}. 150 */ 151 public DefaultContent(Node node, String parentPath, F factory) 152 { 153 super(node, parentPath, factory); 154 } 155 156 @Override 157 public String[] getTypes() throws AmetysRepositoryException 158 { 159 if (_types == null) 160 { 161 _types = getInternalDataHolder().getValueOfType(METADATA_CONTENTTYPE, ModelItemTypeConstants.STRING_TYPE_ID, new String[0]); 162 } 163 164 return _types; 165 } 166 167 @Override 168 public void setType(String type) throws AmetysRepositoryException 169 { 170 setTypes(new String[] {type}); 171 } 172 173 @Override 174 public void setTypes(String[] types) throws AmetysRepositoryException 175 { 176 getInternalDataHolder().setValue(DefaultContent.METADATA_CONTENTTYPE, types, ModelItemTypeConstants.STRING_TYPE_ID); 177 _types = types; 178 } 179 180 @Override 181 public String[] getMixinTypes() throws AmetysRepositoryException 182 { 183 if (_mixins == null) 184 { 185 _mixins = getInternalDataHolder().getValueOfType(METADATA_MIXINCONTENTTYPES, ModelItemTypeConstants.STRING_TYPE_ID, new String[0]); 186 } 187 return _mixins; 188 } 189 190 @Override 191 public void setMixinTypes(String[] mixins) throws AmetysRepositoryException 192 { 193 getInternalDataHolder().setValue(DefaultContent.METADATA_MIXINCONTENTTYPES, mixins, ModelItemTypeConstants.STRING_TYPE_ID); 194 _mixins = mixins; 195 } 196 197 @Override 198 public String getLanguage() throws AmetysRepositoryException 199 { 200 return getInternalDataHolder().getValueOfType(METADATA_LANGUAGE, ModelItemTypeConstants.STRING_TYPE_ID); 201 } 202 203 @Override 204 public void setLanguage(String language) throws AmetysRepositoryException 205 { 206 getInternalDataHolder().setValue(DefaultContent.METADATA_LANGUAGE, language, ModelItemTypeConstants.STRING_TYPE_ID); 207 } 208 209 public String getTitle(Locale locale) throws UnknownDataException, AmetysRepositoryException 210 { 211 Object value = getValue(ATTRIBUTE_TITLE); 212 if (value == null) 213 { 214 throw new UnknownDataException("Unknown attribute " + ATTRIBUTE_TITLE + " for content " + getId()); 215 } 216 else if (value instanceof MultilingualString) 217 { 218 MultilingualString multilingual = (MultilingualString) value; 219 220 if (locale != null && multilingual.hasLocale(locale)) 221 { 222 return multilingual.getValue(locale); 223 } 224 else if (multilingual.hasLocale(DEFAULT_CONTENT_LOCALE)) 225 { 226 return multilingual.getValue(DEFAULT_CONTENT_LOCALE); 227 } 228 else if (multilingual.getValues().isEmpty()) 229 { 230 throw new UnknownDataException("Unknown attribute " + ATTRIBUTE_TITLE + " for content " + getId()); 231 } 232 else 233 { 234 return multilingual.getValues().get(0); 235 } 236 } 237 else 238 { 239 return (String) value; 240 } 241 } 242 243 public String getTitle() throws UnknownDataException, AmetysRepositoryException 244 { 245 return getTitle(null); 246 } 247 248 @Override 249 public UserIdentity getCreator() throws UnknownDataException, AmetysRepositoryException 250 { 251 try 252 { 253 RepositoryData repositoryData = new JCRRepositoryData(getNode()); 254 RepositoryData creatorReposioryData = repositoryData.getRepositoryData(METADATA_CREATOR); 255 return new UserIdentity(creatorReposioryData.getString("login"), creatorReposioryData.getString("population")); 256 } 257 catch (AmetysRepositoryException e) 258 { 259 throw new AmetysRepositoryException("Error while getting creator property for content " + this, e); 260 } 261 } 262 263 @Override 264 public ZonedDateTime getCreationDate() throws UnknownDataException, AmetysRepositoryException 265 { 266 RepositoryData repositoryData = new JCRRepositoryData(getNode()); 267 Calendar date = repositoryData.getDate(METADATA_CREATION); 268 return DateUtils.asZonedDateTime(date); 269 } 270 271 @Override 272 public UserIdentity getLastContributor() throws UnknownDataException, AmetysRepositoryException 273 { 274 try 275 { 276 RepositoryData repositoryData = new JCRRepositoryData(getNode()); 277 RepositoryData contributorReposioryData = repositoryData.getRepositoryData(METADATA_CONTRIBUTOR); 278 return new UserIdentity(contributorReposioryData.getString("login"), contributorReposioryData.getString("population")); 279 } 280 catch (AmetysRepositoryException e) 281 { 282 throw new AmetysRepositoryException("Error while getting creator property for content " + this, e); 283 } 284 } 285 286 @Override 287 public ZonedDateTime getLastModified() throws UnknownDataException, AmetysRepositoryException 288 { 289 RepositoryData repositoryData = new JCRRepositoryData(getNode()); 290 Calendar date = repositoryData.getDate(METADATA_MODIFIED); 291 return DateUtils.asZonedDateTime(date); 292 } 293 294 public Optional<UserIdentity> getFirstValidator() throws UnknownDataException, AmetysRepositoryException 295 { 296 RepositoryData repositoryData = new JCRRepositoryData(getNode()); 297 if (repositoryData.hasValue(METADATA_FIRST_VALIDATOR)) 298 { 299 try 300 { 301 RepositoryData contributorReposioryData = repositoryData.getRepositoryData(METADATA_FIRST_VALIDATOR); 302 return Optional.of(new UserIdentity(contributorReposioryData.getString("login"), contributorReposioryData.getString("population"))); 303 } 304 catch (AmetysRepositoryException e) 305 { 306 throw new AmetysRepositoryException("Error while getting creator property for content " + this, e); 307 } 308 } 309 return Optional.empty(); 310 } 311 312 @Override 313 public ZonedDateTime getFirstValidationDate() throws UnknownDataException, AmetysRepositoryException 314 { 315 RepositoryData repositoryData = new JCRRepositoryData(getNode()); 316 if (repositoryData.hasValue(METADATA_FIRST_VALIDATION)) 317 { 318 Calendar date = repositoryData.getDate(METADATA_FIRST_VALIDATION); 319 return DateUtils.asZonedDateTime(date); 320 } 321 return null; 322 } 323 324 public Optional<UserIdentity> getLastValidator() throws AmetysRepositoryException 325 { 326 RepositoryData repositoryData = new JCRRepositoryData(getNode()); 327 if (repositoryData.hasValue(METADATA_LAST_VALIDATOR)) 328 { 329 try 330 { 331 RepositoryData contributorReposioryData = repositoryData.getRepositoryData(METADATA_LAST_VALIDATOR); 332 return Optional.of(new UserIdentity(contributorReposioryData.getString("login"), contributorReposioryData.getString("population"))); 333 } 334 catch (AmetysRepositoryException e) 335 { 336 throw new AmetysRepositoryException("Error while getting creator property for content " + this, e); 337 } 338 } 339 return Optional.empty(); 340 } 341 342 @Override 343 public ZonedDateTime getLastValidationDate() throws UnknownDataException, AmetysRepositoryException 344 { 345 RepositoryData repositoryData = new JCRRepositoryData(getNode()); 346 if (repositoryData.hasValue(METADATA_LAST_VALIDATION)) 347 { 348 Calendar date = repositoryData.getDate(METADATA_LAST_VALIDATION); 349 return DateUtils.asZonedDateTime(date); 350 } 351 return null; 352 } 353 354 public Optional<UserIdentity> getLastMajorValidator() throws UnknownDataException, AmetysRepositoryException 355 { 356 RepositoryData repositoryData = new JCRRepositoryData(getNode()); 357 if (repositoryData.hasValue(METADATA_LAST_MAJOR_VALIDATOR)) 358 { 359 try 360 { 361 RepositoryData contributorReposioryData = repositoryData.getRepositoryData(METADATA_LAST_MAJOR_VALIDATOR); 362 return Optional.of(new UserIdentity(contributorReposioryData.getString("login"), contributorReposioryData.getString("population"))); 363 } 364 catch (AmetysRepositoryException e) 365 { 366 throw new AmetysRepositoryException("Error while getting creator property for content " + this, e); 367 } 368 } 369 return Optional.empty(); 370 } 371 372 @Override 373 public ZonedDateTime getLastMajorValidationDate() throws AmetysRepositoryException 374 { 375 RepositoryData repositoryData = new JCRRepositoryData(getNode()); 376 if (repositoryData.hasValue(METADATA_LAST_MAJORVALIDATION)) 377 { 378 Calendar date = repositoryData.getDate(METADATA_LAST_MAJORVALIDATION); 379 return DateUtils.asZonedDateTime(date); 380 } 381 return null; 382 } 383 384 // Tag management. 385 @Override 386 public Set<String> getTags() throws AmetysRepositoryException 387 { 388 return TaggableAmetysObjectHelper.getTags(this); 389 } 390 391 @Override 392 public Map<String, OutgoingReferences> getOutgoingReferences() throws AmetysRepositoryException 393 { 394 Map<String, OutgoingReferences> outgoingReferencesByPath = new HashMap<>(); 395 396 try 397 { 398 Node contentNode = getNode(); 399 if (contentNode.hasNode(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + METADATA_ROOT_OUTGOING_REFERENCES)) 400 { 401 Node rootOutgoingRefsNode = contentNode.getNode(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + METADATA_ROOT_OUTGOING_REFERENCES); 402 403 // Loop on outgoing ref node by (metadata) path. 404 NodeIterator outgoingRefsNodeIterator = rootOutgoingRefsNode.getNodes(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + METADATA_OUTGOING_REFERENCES); 405 while (outgoingRefsNodeIterator.hasNext()) 406 { 407 Node outgoingRefsNode = outgoingRefsNodeIterator.nextNode(); 408 String path = outgoingRefsNode.getProperty(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + METADATA_OUTGOING_REFERENCES_PATH_PROPERTY).getString(); 409 410 // Loop on each outgoing ref node values for each reference type and collecting outgoing references. 411 OutgoingReferences outgoingReferences = new OutgoingReferences(); 412 NodeIterator outgoingReferenceNodeIterator = outgoingRefsNode.getNodes(); 413 while (outgoingReferenceNodeIterator.hasNext()) 414 { 415 Node outgoingReferenceNode = outgoingReferenceNodeIterator.nextNode(); 416 Value[] referenceValues = outgoingReferenceNode.getProperty(RepositoryConstants.NAMESPACE_PREFIX + ':' + METADATA_OUTGOING_REFERENCE_PROPERTY).getValues(); 417 418 List<String> referenceValuesAsList = new ArrayList<>(referenceValues.length); 419 for (Value value : referenceValues) 420 { 421 referenceValuesAsList.add(value.getString()); 422 } 423 424 outgoingReferences.put(outgoingReferenceNode.getName(), referenceValuesAsList); 425 } 426 427 // Updating the outgoing references map 428 if (!outgoingReferences.isEmpty()) 429 { 430 if (outgoingReferencesByPath.containsKey(path)) 431 { 432 outgoingReferencesByPath.get(path).merge(outgoingReferences); 433 } 434 else 435 { 436 outgoingReferencesByPath.put(path, outgoingReferences); 437 } 438 } 439 } 440 } 441 } 442 catch (RepositoryException e) 443 { 444 throw new AmetysRepositoryException(e); 445 } 446 447 return outgoingReferencesByPath; 448 } 449 450 @Override 451 public Collection<Content> getReferencingContents() throws AmetysRepositoryException 452 { 453 return _getReferencingContents(Long.MAX_VALUE).referencingContents(); 454 } 455 456 private ReferencingContentsSearch _getReferencingContents(long limit) throws AmetysRepositoryException 457 { 458 Set<Content> contents = new LinkedHashSet<>(); 459 long total = 0; 460 boolean hasOtherReferences = false; 461 462 try 463 { 464 NodeIterator results = OutgoingReferencesHelper.getContentOutgoingReferences(this); 465 total = results.getSize(); 466 467 AmetysObjectResolver resolver = _getFactory()._getAOResolver(); 468 while (results.hasNext() && contents.size() < limit) 469 { 470 Node node = results.nextNode(); 471 Node contentNode = node.getParent() // go up towards node 'ametys-internal:outgoing-references 472 .getParent() // go up towards node 'ametys-internal:root-outgoing-references 473 .getParent(); // go up towards node of the content 474 Content content = resolver.resolve(contentNode, false); 475 contents.add(content); 476 } 477 478 hasOtherReferences = results.hasNext(); 479 } 480 catch (RepositoryException e) 481 { 482 throw new AmetysRepositoryException("Unable to resolve references for content " + getId(), e); 483 } 484 485 return new ReferencingContentsSearch(total, contents, hasOtherReferences); 486 } 487 488 @Override 489 public ReferencingContentsSearch searchReferencingContents(int limit) throws AmetysRepositoryException 490 { 491 return _getReferencingContents(limit); 492 } 493 494 public boolean hasReferencingContents() throws AmetysRepositoryException 495 { 496 try 497 { 498 return OutgoingReferencesHelper.getContentOutgoingReferences(this).getSize() != 0; 499 } 500 catch (RepositoryException e) 501 { 502 throw new AmetysRepositoryException("A repository exception occured: ", e); 503 } 504 } 505 506 @Override 507 public ModifiableContent copyTo(ModifiableTraversableAmetysObject parent, String name, List<String> restrictTo) throws AmetysRepositoryException 508 { 509 return copyTo(parent, name); 510 } 511 512 @Override 513 public ModifiableContent copyTo(ModifiableTraversableAmetysObject parent, String name) throws AmetysRepositoryException 514 { 515 return copyTo(parent, name, 0, true, true); 516 } 517 518 /** 519 * Copy the current {@link DefaultWorkflowAwareContent} to the given object. Be careful, this method save changes, but do not create a new version (checkpoint) 520 * @param parent The parent of the new object. Can not be null. 521 * @param name Name of the new object. Can be null. If null, the new name will be get from the copied object. 522 * @param initWorkflowActionId The initial workflow action id 523 * @param notifyObservers Set to false to do not fire observer events 524 * @param checkpoint true to check the content in if it is versionable 525 * @return the created object 526 * @throws AmetysRepositoryException if an error occurs. 527 */ 528 public ModifiableContent copyTo(ModifiableTraversableAmetysObject parent, String name, int initWorkflowActionId, boolean notifyObservers, boolean checkpoint) throws AmetysRepositoryException 529 { 530 return _getFactory()._getContentDAO().copy(this, parent, name, null, initWorkflowActionId, notifyObservers, checkpoint, false, false, DataContext.newInstance()); 531 } 532 533 /** 534 * Copy the current {@link DefaultWorkflowAwareContent} to the given object. Be careful, this method save changes, but do not create a new version (checkpoint) 535 * @param parent The parent of the new object. Can not be null. 536 * @param name Name of the new object. Can be null. If null, the new name will be get from the copied object. 537 * @param lang Language of the new object. Can be null. If null, the new language will be get from the copied object. 538 * @param initWorkflowActionId The initial workflow action id 539 * @param waitAsyncObservers if true, waits if necessary for the asynchronous observers to complete 540 * @param copyACL true to copy ACL of source content 541 * @param context The context of the data to copy 542 * @return the created object 543 * @throws AmetysRepositoryException if an error occurs. 544 */ 545 public ModifiableContent copyTo(ModifiableTraversableAmetysObject parent, String name, String lang, int initWorkflowActionId, boolean waitAsyncObservers, boolean copyACL, DataContext context) throws AmetysRepositoryException 546 { 547 return _getFactory()._getContentDAO().copy(this, parent, name, lang, initWorkflowActionId, true, true, waitAsyncObservers, copyACL, context); 548 } 549 550 // Dublin Core metadata. // 551 552 @Override 553 public String getDCTitle() throws AmetysRepositoryException 554 { 555 return DublinCoreHelper.getDCTitle(this, getTitle()); 556 } 557 558 @Override 559 public String getDCCreator() throws AmetysRepositoryException 560 { 561 return DublinCoreHelper.getDCCreator(this, UserIdentity.userIdentityToString(getCreator())); 562 } 563 564 @Override 565 public String[] getDCSubject() throws AmetysRepositoryException 566 { 567 return DublinCoreHelper.getDCSubject(this); 568 } 569 570 @Override 571 public String getDCDescription() throws AmetysRepositoryException 572 { 573 String seoDesc = null; 574 try 575 { 576 RepositoryData repositoryData = new JCRRepositoryData(getNode()); 577 RepositoryData seoReposioryData = repositoryData.getRepositoryData("seo"); 578 seoDesc = seoReposioryData.getString("description"); 579 } 580 catch (UnknownDataException me) 581 { 582 seoDesc = null; 583 } 584 585 return DublinCoreHelper.getDCDescription(this, seoDesc); 586 } 587 588 @Override 589 public String getDCPublisher() throws AmetysRepositoryException 590 { 591 return DublinCoreHelper.getDCPublisher(this, getLastValidator().map(UserIdentity::userIdentityToString).orElse(null)); 592 } 593 594 @Override 595 public String getDCContributor() throws AmetysRepositoryException 596 { 597 return DublinCoreHelper.getDCContributor(this, UserIdentity.userIdentityToString(getLastContributor())); 598 } 599 600 @Override 601 public Date getDCDate() throws AmetysRepositoryException 602 { 603 Date defaultDCDate = Optional.ofNullable(getLastValidationDate()) 604 .map(DateUtils::asDate) 605 .orElse(null); 606 return DublinCoreHelper.getDCDate(this, defaultDCDate); 607 608 } 609 610 @Override 611 public String getDCType() throws AmetysRepositoryException 612 { 613 return DublinCoreHelper.getDCType(this, DCMITypes.TEXT); 614 } 615 616 @Override 617 public String getDCFormat() throws AmetysRepositoryException 618 { 619 return DublinCoreHelper.getDCFormat(this, "text/html"); 620 } 621 622 @Override 623 public String getDCIdentifier() throws AmetysRepositoryException 624 { 625 return DublinCoreHelper.getDCIdentifier(this, getId()); 626 } 627 628 @Override 629 public String getDCSource() throws AmetysRepositoryException 630 { 631 return DublinCoreHelper.getDCSource(this); 632 } 633 634 @Override 635 public String getDCLanguage() throws AmetysRepositoryException 636 { 637 return DublinCoreHelper.getDCLanguage(this, getLanguage()); 638 } 639 640 @Override 641 public String getDCRelation() throws AmetysRepositoryException 642 { 643 return DublinCoreHelper.getDCRelation(this); 644 } 645 646 @Override 647 public String getDCCoverage() throws AmetysRepositoryException 648 { 649 return DublinCoreHelper.getDCCoverage(this, getDCLanguage()); 650 } 651 652 @Override 653 public String getDCRights() throws AmetysRepositoryException 654 { 655 return DublinCoreHelper.getDCRights(this); 656 } 657 658 @Override 659 public ResourceCollection getRootAttachments() throws AmetysRepositoryException 660 { 661 ResourceCollection attachments = null; 662 663 if (hasChild(ATTACHMENTS_NODE_NAME)) 664 { 665 attachments = getChild(ATTACHMENTS_NODE_NAME); 666 } 667 668 return attachments; 669 } 670 671 @Override 672 public boolean hasChild(String name) throws AmetysRepositoryException 673 { 674 return _getFactory().hasChild(this, name); 675 } 676 677 @SuppressWarnings("unchecked") 678 @Override 679 public <A extends AmetysObject> A createChild(String name, String type) throws AmetysRepositoryException, RepositoryIntegrityViolationException 680 { 681 return (A) _getFactory().createChild(this, name, type); 682 } 683 684 @SuppressWarnings("unchecked") 685 @Override 686 public <A extends AmetysObject> A getChild(String path) throws AmetysRepositoryException, UnknownAmetysObjectException 687 { 688 return (A) _getFactory().getChild(this, path); 689 } 690 691 @Override 692 public <A extends AmetysObject> AmetysObjectIterable<A> getChildren() throws AmetysRepositoryException 693 { 694 return _getFactory().getChildren(this); 695 } 696 697 public IndexableDataHolder getDataHolder() 698 { 699 // It is necessary to instantiate the repository data because of locks: 700 // when the content is locked, the lock token is released to be taken by anyone 701 // in repository data there is a lock checked boolean that need to be reset in order to take the token 702 // this boolean is only reset at instantiation 703 RepositoryData repositoryData = new JCRRepositoryData(getNode()); 704 Collection<ContentType> contentTypes = _getFactory().getContentHelper().getContentTypes(this); 705 return new DefaultModelAwareDataHolder(repositoryData, contentTypes, Optional.empty(), Optional.of(this)); 706 } 707 708 public ModifiableModelLessDataHolder getInternalDataHolder() 709 { 710 // It is necessary to instantiate the repository data because of locks: 711 // when the content is locked, the lock token is released to be taken by anyone 712 // in repository data there is a lock checked boolean that need to be reset in order to take the token 713 // this boolean is only reset at instantiation 714 ModifiableRepositoryData repositoryData = new JCRRepositoryData(getNode(), RepositoryConstants.NAMESPACE_PREFIX_INTERNAL); 715 Optional<LockableAmetysObject> lockableAmetysObject = Optional.of(this) 716 .filter(LockableAmetysObject.class::isInstance) 717 .map(LockableAmetysObject.class:: cast); 718 return new DefaultModifiableModelLessDataHolder(_getFactory().getInternalDataTypesExtensionPoint(), repositoryData, lockableAmetysObject); 719 } 720 721 public Optional<SystemPropertyExtensionPoint> getSystemPropertyExtensionPoint() 722 { 723 return Optional.of(_getFactory().getSystemPropertyExtensionPoint()); 724 } 725 726 @Override 727 public void addReaction(UserIdentity user, ReactionType reactionType) 728 { 729 ReactionableObjectHelper.addReaction(getUnversionedDataHolder(), user, reactionType); 730 } 731 732 @Override 733 public void removeReaction(UserIdentity user, ReactionType reactionType) 734 { 735 ReactionableObjectHelper.removeReaction(getUnversionedDataHolder(), user, reactionType); 736 } 737 738 @Override 739 public List<UserIdentity> getReactionUsers(ReactionType reactionType) 740 { 741 return ReactionableObjectHelper.getReactionUsers(getUnversionedDataHolder(), reactionType); 742 } 743 744 public void addReport() 745 { 746 ReportableObjectHelper.addReport(getUnversionedDataHolder()); 747 } 748 749 public void setReportsCount(long reportsCount) 750 { 751 ReportableObjectHelper.setReportsCount(getUnversionedDataHolder(), reportsCount); 752 } 753 754 public void clearReports() 755 { 756 ReportableObjectHelper.clearReports(getUnversionedDataHolder()); 757 } 758 759 public long getReportsCount() 760 { 761 return ReportableObjectHelper.getReportsCount(getUnversionedDataHolder()); 762 } 763 764 public void toSAX(ContentHandler contentHandler, Locale locale, View view, boolean saxWorkflowStep) throws SAXException 765 { 766 _getFactory().getContentSaxer().saxContent(this, contentHandler, locale, view, "content", saxWorkflowStep, false, false, "attributes"); 767 } 768}