Neues Verfahren für das Publizieren von Assoziationen zwischen ContentItems. Noch nicht integriert.
git-svn-id: https://svn.libreccm.org/ccm/trunk@1543 8810af33-2d31-482b-a856-94f89814c4dfmaster
parent
b00e272ffa
commit
451b67580d
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
@ -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<String, HandledProperty> handledProperties =
|
||||
new HashMap<String, HandledProperty>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<String, Object> assocAttrs = new HashMap<String, Object>();
|
||||
|
||||
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<String, Object> 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<String, Object> assocAttrs;
|
||||
|
||||
data = new ByteArrayInputStream(attributes);
|
||||
try {
|
||||
in = new ObjectInputStream(data);
|
||||
assocAttrs = (Map<String, Object>) in.readObject();
|
||||
} catch (IOException ex) {
|
||||
throw new UncheckedWrapperException(ex);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
throw new UncheckedWrapperException(ex);
|
||||
}
|
||||
|
||||
if (assocAttrs != null) {
|
||||
for(Map.Entry<String, Object> entry : assocAttrs.entrySet()) {
|
||||
if(association.getObjectType().hasDeclaredProperty(entry.getKey())
|
||||
&& association.getSession() != null) {
|
||||
association.set(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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 "
|
||||
|
|
|
|||
Loading…
Reference in New Issue