From 451b67580db0fd9bdeedf4518b5feced02ab4ac6 Mon Sep 17 00:00:00 2001 From: jensp Date: Mon, 19 Mar 2012 12:49:10 +0000 Subject: [PATCH] =?UTF-8?q?Neues=20Verfahren=20f=C3=BCr=20das=20Publiziere?= =?UTF-8?q?n=20von=20Assoziationen=20zwischen=20ContentItems.=20Noch=20nic?= =?UTF-8?q?ht=20integriert.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: https://svn.libreccm.org/ccm/trunk@1543 8810af33-2d31-482b-a856-94f89814c4df --- .../content-section/PublishedAssociation.pdl | 14 ++ .../com/arsdigita/cms/AssociationCopier.java | 19 ++ .../cms/AssociationCopierDefaultImpl.java | 104 ++++++++ .../src/com/arsdigita/cms/ContentItem.java | 2 + .../arsdigita/cms/PublishedAssociation.java | 233 ++++++++++++++++++ .../src/com/arsdigita/cms/PublishedLink.java | 2 + .../src/com/arsdigita/cms/VersionCopier.java | 81 +++--- 7 files changed, 419 insertions(+), 36 deletions(-) create mode 100644 ccm-cms/pdl/com/arsdigita/content-section/PublishedAssociation.pdl create mode 100644 ccm-cms/src/com/arsdigita/cms/AssociationCopier.java create mode 100644 ccm-cms/src/com/arsdigita/cms/AssociationCopierDefaultImpl.java create mode 100644 ccm-cms/src/com/arsdigita/cms/PublishedAssociation.java diff --git a/ccm-cms/pdl/com/arsdigita/content-section/PublishedAssociation.pdl b/ccm-cms/pdl/com/arsdigita/content-section/PublishedAssociation.pdl new file mode 100644 index 000000000..13cb5fa1b --- /dev/null +++ b/ccm-cms/pdl/com/arsdigita/content-section/PublishedAssociation.pdl @@ -0,0 +1,14 @@ +//$Id$ + +model com.arsdigita.cms; + +object type PublishedAssociation { + composite ContentItem[1..1] draftA = join cms_published_associations.draft_a to cms_items.item_id; + composite ContentItem[1..1] draftB = join cms_published_associations.draft_b to cms_items.item_id; + String[1..1] propertyA = cms_published_associations.property_a VARCHAR(100); + String[1..1] propertyB = cms_published_associations.property_b VARCHAR(100); + //composite ContentItem[1..1] pendingA = join cms_published_associations.pending_a to cms_items.item_id; + //composite ContentItem[1..1] pendingB = join cms_published_associations.pending_b to cms_items.item_id; + Blob[0..1] associationAttributes = cms_published_associations.association_attributes BLOB; + object key (draftA, draftB, propertyA, propertyB); +} \ No newline at end of file diff --git a/ccm-cms/src/com/arsdigita/cms/AssociationCopier.java b/ccm-cms/src/com/arsdigita/cms/AssociationCopier.java new file mode 100644 index 000000000..c22452ec7 --- /dev/null +++ b/ccm-cms/src/com/arsdigita/cms/AssociationCopier.java @@ -0,0 +1,19 @@ +package com.arsdigita.cms; + +import com.arsdigita.domain.DomainObject; +import com.arsdigita.persistence.metadata.Property; + +/** + * + * @author Jens Pelzetter + * @version $Id$ + */ +public interface AssociationCopier { + + boolean handlesProperty(Property property); + void copy(DomainObject source, + DomainObject target, + DomainObject value, + Property property); + +} diff --git a/ccm-cms/src/com/arsdigita/cms/AssociationCopierDefaultImpl.java b/ccm-cms/src/com/arsdigita/cms/AssociationCopierDefaultImpl.java new file mode 100644 index 000000000..4c98c17e8 --- /dev/null +++ b/ccm-cms/src/com/arsdigita/cms/AssociationCopierDefaultImpl.java @@ -0,0 +1,104 @@ +package com.arsdigita.cms; + +import com.arsdigita.domain.DomainObject; +import com.arsdigita.persistence.metadata.Property; +import java.util.HashMap; +import java.util.Map; + +/** + * + * @author Jens Pelzetter + * @version $Id$ + */ +public class AssociationCopierDefaultImpl implements AssociationCopier { + + final Map handledProperties = + new HashMap(); + + public void addHandledProperty(final String name, + final String type, + final String reverse, + final String reverseType) { + final HandledProperty prop = new HandledProperty(); + prop.setName(name); + prop.setType(type); + prop.setReverse(reverse); + prop.setReverseType(reverseType); + handledProperties.put(name, prop); + } + + public boolean handlesProperty(final Property property) { + return handledProperties.containsKey(property.getName()); + } + + public void copy(final DomainObject source, + final DomainObject target, + final DomainObject value, + final Property property) { + final HandledProperty propData = handledProperties.get( + property.getName()); + if (propData == null) { + throw new IllegalArgumentException(String.format( + "Illegal call for this method. Property '%s' is not handled" + + "by this AssociationCopier.", property.getName())); + } + + //Create PublishedAssociation here. + final HandledProperty handledProperty = handledProperties.get(property. + getName()); + if (handledProperty == null) { + throw new IllegalArgumentException(String.format( + "Property '%s' " + + "is not handled by this AssociationCopier.", + property.getName())); + } + + final ContentItem sourceItem = (ContentItem) source; + final ContentItem valueItem = (ContentItem) value; + + PublishedAssociation.create(sourceItem, + valueItem, + property.getName(), + handledProperty.getReverse()); + } + + private class HandledProperty { + + private String name; + private String type; + private String reverse; + private String reverseType; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getReverse() { + return reverse; + } + + public void setReverse(String reverse) { + this.reverse = reverse; + } + + public String getReverseType() { + return reverseType; + } + + public void setReverseType(String reverseType) { + this.reverseType = reverseType; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + } +} diff --git a/ccm-cms/src/com/arsdigita/cms/ContentItem.java b/ccm-cms/src/com/arsdigita/cms/ContentItem.java index b9df7a1e2..b948801ab 100755 --- a/ccm-cms/src/com/arsdigita/cms/ContentItem.java +++ b/ccm-cms/src/com/arsdigita/cms/ContentItem.java @@ -1144,6 +1144,7 @@ public class ContentItem extends VersionedACSObject implements CustomCopy { oldVersion.delete(); PublishedLink.refreshOnUnpublish(this); + PublishedAssociation.refreshOnUnPublish(this); } if (parent instanceof ContentBundle || parent instanceof Folder) { @@ -1172,6 +1173,7 @@ public class ContentItem extends VersionedACSObject implements CustomCopy { save(); PublishedLink.updateLiveLinks(version); + PublishedAssociation.updateLiveAssoications(version); save(); // publish item (as template or html pages) to the file diff --git a/ccm-cms/src/com/arsdigita/cms/PublishedAssociation.java b/ccm-cms/src/com/arsdigita/cms/PublishedAssociation.java new file mode 100644 index 000000000..5fae3d707 --- /dev/null +++ b/ccm-cms/src/com/arsdigita/cms/PublishedAssociation.java @@ -0,0 +1,233 @@ +package com.arsdigita.cms; + +import com.arsdigita.domain.DomainObject; +import com.arsdigita.persistence.DataCollection; +import com.arsdigita.persistence.DataObject; +import com.arsdigita.persistence.OID; +import com.arsdigita.persistence.Session; +import com.arsdigita.persistence.SessionManager; +import com.arsdigita.persistence.metadata.Property; +import com.arsdigita.util.UncheckedWrapperException; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * + * @author Jens Pelzetter + * @version $Id$ + */ +public class PublishedAssociation extends DomainObject { + + private static final String DRAFT_A = "draftA"; + private static final String DRAFT_B = "draftA"; + private static final String PROPERTY_A = "propertyA"; + private static final String PROPERTY_B = "propertyB"; + //private static final String PENDING_A = "pendingA"; + //private static final String PENDING_B = "pendingA"; + private static final String ATTRIBUTES = "associationAttributes"; + public static final String BASE_DATA_OBJECT_TYPE = + "com.arsdigita.cms.PublishedAssociation"; + + protected PublishedAssociation() { + super(BASE_DATA_OBJECT_TYPE); + } + + protected PublishedAssociation(final DataObject dobj) { + super(dobj); + } + + protected PublishedAssociation(final OID oid) { + super(oid); + } + + @Override + protected String getBaseDataObjectType() { + return BASE_DATA_OBJECT_TYPE; + } + + protected static PublishedAssociation create(final ContentItem draftA, + final ContentItem draftB, + final String propertyA, + final String propertyB) { + //Check if the association is already saved. + final Session session = SessionManager.getSession(); + final DataCollection assocsA = session.retrieve(BASE_DATA_OBJECT_TYPE); + assocsA.addEqualsFilter(DRAFT_A + ".id", draftA.getID()); + assocsA.addEqualsFilter(DRAFT_B + ".id", draftB.getID()); + assocsA.addEqualsFilter(PROPERTY_A, propertyA); + assocsA.addEqualsFilter(PROPERTY_B, propertyB); + + if (assocsA.size() == 1) { + assocsA.next(); + final DataObject dobj = assocsA.getDataObject(); + assocsA.close(); + return new PublishedAssociation(dobj); + } else if (assocsA.size() > 1) { + throw new UncheckedWrapperException( + "Something very strange has occurred. There is more than " + + "one PublishedAssociation for a association."); + } + + //Maybe draftA and draftB are switched + final DataCollection assocsB = session.retrieve(BASE_DATA_OBJECT_TYPE); + assocsB.addEqualsFilter(DRAFT_A + ".id", draftB.getID()); + assocsB.addEqualsFilter(DRAFT_B + ".id", draftA.getID()); + assocsB.addEqualsFilter(PROPERTY_A, propertyB); + assocsB.addEqualsFilter(PROPERTY_B, propertyA); + + if (assocsB.size() == 1) { + assocsB.next(); + final DataObject dobj = assocsB.getDataObject(); + assocsB.close(); + return new PublishedAssociation(dobj); + } else if (assocsB.size() > 1) { + throw new UncheckedWrapperException( + "Something very strange has occurred. There is more than " + + "one PublishedAssociation for a association."); + } + + //No existing entry found, crate new one. + + final PublishedAssociation assoc = new PublishedAssociation(); + assoc.set(DRAFT_A, draftA); + assoc.set(DRAFT_B, draftB); + assoc.set(PROPERTY_A, propertyA); + assoc.set(PROPERTY_B, PROPERTY_B); + + if (draftA.getObjectType().getProperty(propertyA).isCollection()) { + final DataCollection coll = (DataCollection) draftA.get( + propertyA + "@link"); + + while (coll.next()) { + DataObject linkObj = coll.getDataObject(); + + if (draftB.getOID().equals(((DataObject) linkObj.getOID().get( + propertyA)).getOID())) { + assoc.saveAssociationAttributes(linkObj); + coll.close(); + } + } + } + + assoc.save(); + + return assoc; + } + + protected static void updateLiveAssociations(final ContentItem item) { + final Session session = SessionManager.getSession(); + final ContentItem draftItem = item.getDraftVersion(); + final ContentItem liveItem = draftItem.getLiveVersion(); + + final DataCollection assocsA = session.retrieve(BASE_DATA_OBJECT_TYPE); + assocsA.addEqualsFilter(DRAFT_A + ".id", draftItem.getID()); + processAssociations(assocsA, liveItem); + + final DataCollection assocsB = session.retrieve(BASE_DATA_OBJECT_TYPE); + assocsB.addEqualsFilter(DRAFT_B + ".id", draftItem.getID()); + processAssociations(assocsB, liveItem); + } + + private static void processAssociations(final DataCollection associations, + final ContentItem liveItem) { + while (associations.next()) { + processAssociation(new PublishedAssociation( + associations.getDataObject()), liveItem); + } + } + + private static void processAssociation( + final PublishedAssociation association, + final ContentItem liveItem) { + final ContentItem otherDraft = (ContentItem) association.get(DRAFT_B); + final ContentItem otherLive = otherDraft.getLiveVersion(); + + if (otherLive != null) { + createAssociation(liveItem, + (String) association.get(PROPERTY_A), + otherLive, + (byte[]) association.get(ATTRIBUTES)); + } + } + + private static void createAssociation(final ContentItem itemA, + final String propertyA, + final ContentItem itemB, + final byte[] associationAttributes) { + final DataObject association = itemA.add(propertyA, itemB); + setAttributesForLiveAssociation(association, associationAttributes); + } + + protected static void refreshOnUnPublish(final ContentItem item) { + //Nothing to do + } + + private void saveAssociationAttributes(final DataObject assocObj) { + final Iterator properties = assocObj.getObjectType(). + getDeclaredProperties(); + final Map assocAttrs = new HashMap(); + + while (properties.hasNext()) { + processAttribute(assocObj, assocAttrs, (Property) properties.next()); + } + + if (!assocAttrs.isEmpty()) { + final ByteArrayOutputStream data = new ByteArrayOutputStream(); + try { + final ObjectOutputStream out = new ObjectOutputStream(data); + out.writeObject(assocAttrs); + } catch (IOException ex) { + throw new UncheckedWrapperException(ex); + } + + set(ATTRIBUTES, data.toByteArray()); + } + } + + private void processAttribute(final DataObject assocObj, + final Map assocAttrs, + final Property property) { + final String name = property.getName(); + + // Teste Property: Es darf kein Key und muß ein simples Attribute sein + if (property.isAttribute() && !property.isKeyProperty()) { + final Object value = assocObj.get(name); + assocAttrs.put(name, value); + } + } + + private static void setAttributesForLiveAssociation( + final DataObject association, byte[] attributes) { + if (attributes != null) { + final ByteArrayInputStream data; + final ObjectInputStream in; + final Map assocAttrs; + + data = new ByteArrayInputStream(attributes); + try { + in = new ObjectInputStream(data); + assocAttrs = (Map) in.readObject(); + } catch (IOException ex) { + throw new UncheckedWrapperException(ex); + } catch (ClassNotFoundException ex) { + throw new UncheckedWrapperException(ex); + } + + if (assocAttrs != null) { + for(Map.Entry entry : assocAttrs.entrySet()) { + if(association.getObjectType().hasDeclaredProperty(entry.getKey()) + && association.getSession() != null) { + association.set(entry.getKey(), entry.getValue()); + } + + } + } + } + } +} diff --git a/ccm-cms/src/com/arsdigita/cms/PublishedLink.java b/ccm-cms/src/com/arsdigita/cms/PublishedLink.java index d8ea78b98..12d470590 100755 --- a/ccm-cms/src/com/arsdigita/cms/PublishedLink.java +++ b/ccm-cms/src/com/arsdigita/cms/PublishedLink.java @@ -34,6 +34,7 @@ import com.arsdigita.persistence.SessionManager; import com.arsdigita.persistence.metadata.ObjectType; import com.arsdigita.persistence.metadata.Property; import com.arsdigita.util.Assert; +import com.arsdigita.util.UncheckedWrapperException; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -378,6 +379,7 @@ class PublishedLink extends DomainObject { ObjectOutputStream out = new ObjectOutputStream(data); out.writeObject(linkAttributes); } catch (IOException ex) { + throw new UncheckedWrapperException(ex); } set(LINK_ATTRIBUTES, data.toByteArray()); diff --git a/ccm-cms/src/com/arsdigita/cms/VersionCopier.java b/ccm-cms/src/com/arsdigita/cms/VersionCopier.java index 322f75f15..616a3e835 100755 --- a/ccm-cms/src/com/arsdigita/cms/VersionCopier.java +++ b/ccm-cms/src/com/arsdigita/cms/VersionCopier.java @@ -196,47 +196,56 @@ class VersionCopier extends ObjectCopier { item.assertDraft(); } - if (prop.isComponent()) { - s_log.debug("The property is a component; creating a " - + "live or pending version"); - - final ContentItem copy = createVersion(item); - - m_trace.exit("copy", copy); - - return copy; - } else if (m_traversedComponents.contains(object)) { - final DomainObject copy = copy(object); - - m_trace.exit("copy", copy); - - return copy; - } else if (prop.isRequired()) { - Assert.fail( - "1..1 associations to non-component top-level ContentItems are not allowed"); + if ((item instanceof AssociationCopier) + && ((AssociationCopier) item).handlesProperty(prop)) { + final AssociationCopier assocCopier = (AssociationCopier) item; + assocCopier.copy(source, target, target, prop); + return null; } else { - s_log.debug("The property is not a component; creating " - + "PublishedLink for the item"); - if (source instanceof ContentItem) { - PublishedLink.create( - (ContentItem) getCopy(m_topLevelSourceOID), - target, - prop.getName(), - item, - (ContentItem) source); + if (prop.isComponent()) { + s_log.debug("The property is a component; creating a " + + "live or pending version"); + + final ContentItem copy = createVersion(item); + + m_trace.exit("copy", copy); + + return copy; + } else if (m_traversedComponents.contains(object)) { + final DomainObject copy = copy(object); + + m_trace.exit("copy", copy); + + return copy; + } else if (prop.isRequired()) { + Assert.fail( + "1..1 associations to non-component top-level ContentItems are not allowed"); + return null; } else { - PublishedLink.create( - (ContentItem) getCopy(m_topLevelSourceOID), - target, - prop.getName(), - item, - null); - } - m_trace.exit("copy", null); + s_log.debug("The property is not a component; creating " + + "PublishedLink for the item"); - return null; + if (source instanceof ContentItem) { + PublishedLink.create( + (ContentItem) getCopy(m_topLevelSourceOID), + target, + prop.getName(), + item, + (ContentItem) source); + } else { + PublishedLink.create( + (ContentItem) getCopy(m_topLevelSourceOID), + target, + prop.getName(), + item, + null); + } + m_trace.exit("copy", null); + + return null; + } } } else { s_log.debug("The property is not a content item; using "