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