package er.modern.directtoweb.components.repetitions; import com.webobjects.appserver.WOActionResults; import com.webobjects.appserver.WOComponent; import com.webobjects.appserver.WOContext; import com.webobjects.directtoweb.D2WContext; import com.webobjects.directtoweb.ERD2WContext; import com.webobjects.eocontrol.EOEnterpriseObject; import com.webobjects.foundation.NSArray; import com.webobjects.foundation.NSDictionary; import com.webobjects.foundation.NSMutableDictionary; import er.extensions.foundation.ERXValueUtilities; /** * Special list page repetition for hierarchical object structures that extends * ERMDListPageRepetition. Allows recursive access to a relationship specified * via the "nestedRelationship" key. * * As an example, you could use this for a hierarchy of locations (think * "continent > country > city > quarter"), with "containedLocations" as the * nestedRelationship key. This will allow a "drill-down" access to locations. * * @binding displayGroup * @binding d2wContext * * @d2wKey baseClassForObjectRow * @d2wKey componentName * @d2wKey displayNameForProperty * @d2wKey extraListComponentName * @d2wKey justification * @d2wKey nestedRelationship * @d2wKey object * @d2wKey propertyIsSortable * @d2wKey showNestedRelationshipEditor * @d2wKey sortCaseInsensitive * @d2wKey sortKeyForList * @author fpeters */ public class ERMDNestingListPageRepetition extends ERMDListPageRepetition { private static final long serialVersionUID = 1L; public interface Keys { public static String nestedRelationship = "nestedRelationship"; public static String showNestedRelationshipEditor = "showNestedRelationshipEditor"; } public ERMDNestingListPageRepetition(WOContext context) { super(context); } @SuppressWarnings({ "rawtypes", "unchecked" }) public NSDictionary childRelationshipBindings() { NSArray masterObjectAndRelationshipKey = new NSArray(d2wContext() .valueForKeyPath("object"), d2wContext().valueForKey( Keys.nestedRelationship)); return new NSMutableDictionary("masterObjectAndRelationshipKey", masterObjectAndRelationshipKey); } public D2WContext childContext() { D2WContext c = ERD2WContext.newContext(d2wContext()); c.setPropertyKey((String) d2wContext().valueForKey(Keys.nestedRelationship)); c.takeValueForKey("ERDEditRelationship", "componentName"); return c; } public boolean hasLeftActions() { boolean hasLeftActions = leftActions() != null && leftActions().count() > 0; return hasLeftActions; } public WOActionResults toggleNestedRelationshipEditor() { // use globalID hash as unique key for this object Boolean showNestedRelationshipEditor = (Boolean) d2wContext().valueForKeyPath( Keys.showNestedRelationshipEditor + uniqueObjectID()); if (showNestedRelationshipEditor == null) { // default to hide showNestedRelationshipEditor = Boolean.FALSE; } d2wContext().takeValueForKeyPath(!showNestedRelationshipEditor, Keys.showNestedRelationshipEditor + uniqueObjectID()); return null; } public Boolean hasNestedRelationship() { return d2wContext().valueForKey(Keys.nestedRelationship) != null; } public boolean showNestedRelationshipEditor() { // use globalID hash as unique key for this object return ERXValueUtilities.booleanValue(d2wContext().valueForKeyPath(Keys. showNestedRelationshipEditor + uniqueObjectID())); } /** * @return the column count for the nested td's colspan */ public int columnCount() { int columnCount = displayPropertyKeyCount(); if (d2wContext().valueForKey(Keys.nestedRelationship) != null) { // additional column for toggle action columnCount++; } if (hasLeftActions()) { columnCount++; } if (hasRightActions()) { columnCount++; } return columnCount; } /** * Computes a unique ID, based on the nesting level. Depending on the object * graph, a unique EOGlobalID may be insufficient as an EO might appear more * than once. * * @return unique ID for the nested update container */ public String idForNestedUpdateContainer() { String idForNestedUpdateContainer = (String) d2wContext().valueForKey( "idForRepetitionContainer"); WOComponent ancestor = parent(); int recursionCounter = 0; while (ancestor != null && ancestor.parent() != null) { ancestor = ancestor.parent(); if (name().equals(ancestor.name())) { recursionCounter++; } } idForNestedUpdateContainer = idForNestedUpdateContainer + "_l" + recursionCounter + "_" + uniqueObjectID(); return idForNestedUpdateContainer; } /** * @return a unique ID, based on the D2WContext's object value */ private String uniqueObjectID() { EOEnterpriseObject object = (EOEnterpriseObject) d2wContext().valueForKey( "object"); String uniqueID = String.valueOf(object.hashCode()); return uniqueID; } /* * Overridden to remove LastObjRow class, as the nestedRowClass will always * be last. */ public String objectRowClass() { String objectRowClass = super.objectRowClass(); objectRowClass = objectRowClass.replace("LastObjRow", ""); return objectRowClass; } /** * Gets class list from object row class, but removes any FirstObjRow * occurrence, as the nested row can never be the first row. * * @return nested row class */ public String nestedRowClass() { String nestedRowClass = super.objectRowClass(); nestedRowClass = nestedRowClass.replace("FirstObjRow", ""); nestedRowClass = nestedRowClass.concat(" NestedObjRow"); return nestedRowClass; } public boolean hasChildren() { EOEnterpriseObject object = (EOEnterpriseObject) d2wContext().valueForKey( "object"); boolean hasChildren = ERXValueUtilities.booleanValue(object.valueForKeyPath( d2wContext().valueForKey(Keys.nestedRelationship) + ".@count")); return hasChildren; } /* * The remainder handles the icon toggling – the same could be achieved by simply * putting the icons in a dependent update container, but that would * potentially introduce a lot of update containers. */ public String idForDoOpenIcon() { return idForNestedUpdateContainer() + "_open"; } public String idForDoCloseIcon() { return idForNestedUpdateContainer() + "_close"; } public String toggleIconsScript() { String toggleIconsScript = "function(){$('" + idForDoOpenIcon() + "', '" + idForDoCloseIcon() + "').invoke('toggle');}"; return toggleIconsScript; } public String initialStyleForDoOpenIcon() { return showNestedRelationshipEditor() ? "display:none;" : ""; } public String initialStyleForDoCloseIcon() { return showNestedRelationshipEditor() ? "" : "display:none;"; } }