0001: /* Copyright 2002 The JA-SIG Collaborative. All rights reserved.
0002: * See license distributed with this file and
0003: * available online at http://www.uportal.org/license.html
0004: */
0005:
0006: package org.jasig.portal.layout.simple;
0007:
0008: import java.util.HashSet;
0009: import java.util.Hashtable;
0010: import java.util.Iterator;
0011: import java.util.Enumeration;
0012: import java.util.Random;
0013: import java.util.Set;
0014: import java.util.Vector;
0015:
0016: import javax.servlet.http.HttpServletRequest;
0017: import javax.xml.transform.Transformer;
0018: import javax.xml.transform.TransformerFactory;
0019: import javax.xml.transform.dom.DOMSource;
0020: import javax.xml.transform.sax.SAXResult;
0021:
0022: import javax.xml.xpath.XPath;
0023: import javax.xml.xpath.XPathConstants;
0024: import javax.xml.xpath.XPathExpressionException;
0025: import javax.xml.xpath.XPathFactory;
0026:
0027: import org.jasig.portal.ChannelStaticData;
0028: import org.jasig.portal.PortalException;
0029: import org.jasig.portal.StructureStylesheetUserPreferences;
0030: import org.jasig.portal.ThemeStylesheetUserPreferences;
0031: import org.jasig.portal.UserPreferences;
0032: import org.jasig.portal.UserProfile;
0033: import org.jasig.portal.layout.IUserLayout;
0034: import org.jasig.portal.layout.IUserLayoutManager;
0035: import org.jasig.portal.layout.IUserLayoutStore;
0036: import org.jasig.portal.layout.LayoutEvent;
0037: import org.jasig.portal.layout.LayoutEventListener;
0038: import org.jasig.portal.layout.LayoutMoveEvent;
0039: import org.jasig.portal.layout.node.IUserLayoutChannelDescription;
0040: import org.jasig.portal.layout.node.IUserLayoutFolderDescription;
0041: import org.jasig.portal.layout.node.IUserLayoutNodeDescription;
0042: import org.jasig.portal.layout.node.UserLayoutChannelDescription;
0043: import org.jasig.portal.layout.node.UserLayoutFolderDescription;
0044: import org.jasig.portal.layout.node.UserLayoutNodeDescription;
0045: import org.jasig.portal.security.IPerson;
0046: import org.apache.commons.logging.Log;
0047: import org.apache.commons.logging.LogFactory;
0048: import org.jasig.portal.utils.CommonUtils;
0049: import org.jasig.portal.utils.DocumentFactory;
0050:
0051: import org.jasig.portal.utils.XSLT;
0052: import org.w3c.dom.Document;
0053: import org.w3c.dom.Element;
0054: import org.w3c.dom.Node;
0055: import org.xml.sax.ContentHandler;
0056:
0057: /**
0058: * An implementation of a user layout manager that uses 2.0-release store implementations.
0059: *
0060: * Prior to uPortal 2.5, this class existed in the org.jasig.portal.layout package.
0061: * It was moved to its present package to reflect that it is part of the
0062: * Simple Layout Management implementation.
0063: *
0064: * @author Peter Kharchenko {@link <a href="mailto:pkharchenko@interactivebusiness.com"">pkharchenko@interactivebusiness.com"</a>}
0065: * @version $Revision: 36690 $
0066: */
0067: public class SimpleUserLayoutManager implements IUserLayoutManager {
0068: private static final Log log = LogFactory
0069: .getLog(SimpleUserLayoutManager.class);
0070:
0071: protected final IPerson owner;
0072: protected final UserProfile profile;
0073: protected IUserLayoutStore store = null;
0074: protected Set listeners = new HashSet();
0075:
0076: protected IUserLayout userLayout = null;
0077: protected Document userLayoutDocument = null;
0078: protected Document markedUserLayout = null;
0079:
0080: protected static Random rnd = new Random();
0081: protected String cacheKey = "initialKey";
0082: protected String rootNodeId;
0083:
0084: private boolean dirtyState = false;
0085:
0086: // marking mode variables
0087: private String markingMode = null; // null means markings are turned off
0088: private String markingNode;
0089:
0090: // The names for marking nodes
0091: private static final String ADD_COMMAND = "add";
0092: private static final String MOVE_COMMAND = "move";
0093:
0094: // marking stylesheet
0095: private static final String MARKING_XSLT_URI = "/org/jasig/portal/layout/MarkUserLayout.xsl";
0096:
0097: private IUserLayoutNodeDescription newNodeDescription = null;
0098:
0099: public SimpleUserLayoutManager(IPerson owner, UserProfile profile,
0100: IUserLayoutStore store) throws PortalException {
0101: if (owner == null) {
0102: throw new PortalException(
0103: "A non-null owner needs to be specified.");
0104: }
0105:
0106: if (profile == null) {
0107: throw new PortalException(
0108: "A non-null profile needs to be specified.");
0109: }
0110:
0111: this .owner = owner;
0112: this .profile = profile;
0113: this .rootNodeId = null;
0114: this .setLayoutStore(store);
0115: this .loadUserLayout();
0116: this .markingMode = null;
0117: this .markingNode = null;
0118: }
0119:
0120: public IUserLayout getUserLayout() throws PortalException {
0121: // Temporary until we use IUserLayout for real
0122: return new SimpleLayout(String.valueOf(profile.getLayoutId()),
0123: this .userLayoutDocument);
0124: }
0125:
0126: public void setUserLayout(IUserLayout userLayout)
0127: throws PortalException {
0128: // Temporary until we use IUserLayout for real
0129: Document doc = DocumentFactory.getNewDocument();
0130: try {
0131: userLayout.writeTo(doc);
0132: } catch (PortalException pe) {
0133: }
0134: this .userLayoutDocument = doc;
0135: this .markedUserLayout = null;
0136: this .updateCacheKey();
0137: }
0138:
0139: private void setUserLayoutDOM(Document doc) {
0140: this .userLayoutDocument = doc;
0141: this .markedUserLayout = null;
0142: this .updateCacheKey();
0143: }
0144:
0145: public Document getUserLayoutDOM() {
0146: return this .userLayoutDocument;
0147: }
0148:
0149: public void getUserLayout(ContentHandler ch) throws PortalException {
0150: Document ulm = this .getUserLayoutDOM();
0151: if (ulm == null) {
0152: throw new PortalException(
0153: "User layout has not been initialized");
0154: } else {
0155: getUserLayout(ulm, ch);
0156: }
0157: }
0158:
0159: public void getUserLayout(String nodeId, ContentHandler ch)
0160: throws PortalException {
0161: Document ulm = this .getUserLayoutDOM();
0162:
0163: if (ulm == null) {
0164: throw new PortalException(
0165: "User layout has not been initialized");
0166: } else {
0167: Node rootNode = ulm.getElementById(nodeId);
0168:
0169: if (rootNode == null) {
0170: throw new PortalException(
0171: "A requested root node (with id=\"" + nodeId
0172: + "\") is not in the user layout.");
0173: } else {
0174: getUserLayout(rootNode, ch);
0175: }
0176: }
0177: }
0178:
0179: protected void getUserLayout(Node n, ContentHandler ch)
0180: throws PortalException {
0181: // do a DOM2SAX transformation, invoking marking transformation if necessary
0182: try {
0183: if (markingMode != null) {
0184: Hashtable stylesheetParams = new Hashtable(1);
0185: stylesheetParams.put("operation", markingMode);
0186: if (markingNode != null) {
0187: stylesheetParams.put("targetId", markingNode);
0188: }
0189: XSLT xslt = XSLT.getTransformer(this );
0190: xslt.setXML(n);
0191: xslt.setTarget(ch);
0192: xslt.setStylesheetParameters(stylesheetParams);
0193: xslt.setXSL(MARKING_XSLT_URI);
0194: xslt.transform();
0195: } else {
0196: Transformer emptyt = TransformerFactory.newInstance()
0197: .newTransformer();
0198: emptyt.transform(new DOMSource(n), new SAXResult(ch));
0199: }
0200: } catch (Exception e) {
0201: throw new PortalException(
0202: "Encountered an exception trying to output user layout",
0203: e);
0204: }
0205: }
0206:
0207: public void setLayoutStore(IUserLayoutStore store) {
0208: this .store = store;
0209: }
0210:
0211: protected IUserLayoutStore getLayoutStore() {
0212: return this .store;
0213: }
0214:
0215: public void loadUserLayout() throws PortalException {
0216: if (this .getLayoutStore() == null) {
0217: throw new PortalException(
0218: "Store implementation has not been set.");
0219: } else {
0220: try {
0221: Document uli = this .getLayoutStore().getUserLayout(
0222: this .owner, this .profile);
0223: if (uli != null) {
0224: this .setUserLayoutDOM(uli);
0225: clearDirtyFlag();
0226: // inform listeners
0227: for (Iterator i = listeners.iterator(); i.hasNext();) {
0228: LayoutEventListener lel = (LayoutEventListener) i
0229: .next();
0230: lel.layoutLoaded();
0231: }
0232: updateCacheKey();
0233: } else {
0234: throw new PortalException(
0235: "Null user layout returned for ownerId=\""
0236: + owner.getID()
0237: + "\", profileId=\""
0238: + profile.getProfileId()
0239: + "\", layoutId=\""
0240: + profile.getLayoutId() + "\"");
0241: }
0242: } catch (PortalException pe) {
0243: throw pe;
0244: } catch (Exception e) {
0245: throw new PortalException(
0246: "Exception encountered while reading a layout for userId="
0247: + this .owner.getID() + ", profileId="
0248: + this .profile.getProfileId(), e);
0249: }
0250: }
0251: }
0252:
0253: public void saveUserLayout() throws PortalException {
0254: if (isLayoutDirty()) {
0255: Document ulm = this .getUserLayoutDOM();
0256: if (ulm == null) {
0257: throw new PortalException(
0258: "UserLayout has not been initialized.");
0259: } else {
0260: if (this .getLayoutStore() == null) {
0261: throw new PortalException(
0262: "Store implementation has not been set.");
0263: } else {
0264: try {
0265: this .getLayoutStore().setUserLayout(this .owner,
0266: this .profile, ulm, true);
0267: // inform listeners
0268: for (Iterator i = listeners.iterator(); i
0269: .hasNext();) {
0270: LayoutEventListener lel = (LayoutEventListener) i
0271: .next();
0272: lel.layoutSaved();
0273: }
0274: } catch (PortalException pe) {
0275: throw pe;
0276: } catch (Exception e) {
0277: throw new PortalException(
0278: "Exception encountered while trying to save a layout for userId="
0279: + this .owner.getID()
0280: + ", profileId="
0281: + this .profile.getProfileId(),
0282: e);
0283: }
0284: }
0285: }
0286: }
0287: }
0288:
0289: public IUserLayoutNodeDescription getNode(String nodeId)
0290: throws PortalException {
0291: Document ulm = this .getUserLayoutDOM();
0292: if (ulm == null) {
0293: throw new PortalException(
0294: "UserLayout has not been initialized.");
0295: }
0296:
0297: // find an element with a given id
0298: Element element = (Element) ulm.getElementById(nodeId);
0299:
0300: if (element == null) {
0301: throw new PortalException("Element with ID=\"" + nodeId
0302: + "\" doesn't exist.");
0303: }
0304: return UserLayoutNodeDescription
0305: .createUserLayoutNodeDescription(element);
0306: }
0307:
0308: /**
0309: * Returns the depth of a node in the layout tree.
0310: *
0311: * @param nodeId a <code>String</code> value
0312: * @return a depth value
0313: * @exception PortalException if an error occurs
0314: */
0315: public int getDepth(String nodeId) throws PortalException {
0316: int depth = 0;
0317: for (String parentId = getParentId(nodeId); parentId != null; parentId = getParentId(parentId), depth++)
0318: ;
0319: return depth;
0320: }
0321:
0322: public IUserLayoutNodeDescription addNode(
0323: IUserLayoutNodeDescription node, String parentId,
0324: String nextSiblingId) throws PortalException {
0325: boolean isChannel = false;
0326: IUserLayoutNodeDescription parent = this .getNode(parentId);
0327: if (canAddNode(node, parent, nextSiblingId)) {
0328: // assign new Id
0329:
0330: if (this .getLayoutStore() == null) {
0331: throw new PortalException(
0332: "Store implementation has not been set.");
0333: } else {
0334: try {
0335: if (node instanceof IUserLayoutChannelDescription) {
0336: isChannel = true;
0337: node.setId(this .getLayoutStore()
0338: .generateNewChannelSubscribeId(owner));
0339: } else {
0340: node.setId(this .getLayoutStore()
0341: .generateNewFolderId(owner));
0342: }
0343: } catch (PortalException pe) {
0344: throw pe;
0345: } catch (Exception e) {
0346: throw new PortalException(
0347: "Exception encountered while generating new usre layout node Id for userId="
0348: + this .owner.getID());
0349: }
0350: }
0351:
0352: Document ulm = this .getUserLayoutDOM();
0353: Element childElement = node.getXML(ulm);
0354: Element parentElement = (Element) ulm
0355: .getElementById(parentId);
0356: if (nextSiblingId == null) {
0357: parentElement.appendChild(childElement);
0358: } else {
0359: Node nextSibling = ulm.getElementById(nextSiblingId);
0360: parentElement.insertBefore(childElement, nextSibling);
0361: }
0362: markLayoutDirty();
0363:
0364: // register element id
0365:
0366: childElement.setIdAttribute("ID", true);
0367: childElement.setAttribute("ID", node.getId());
0368:
0369: this .updateCacheKey();
0370: this .clearMarkings();
0371:
0372: // inform the listeners
0373: LayoutEvent ev = new LayoutEvent(this , node);
0374: for (Iterator i = listeners.iterator(); i.hasNext();) {
0375: LayoutEventListener lel = (LayoutEventListener) i
0376: .next();
0377: if (isChannel) {
0378: lel.channelAdded(ev);
0379: } else {
0380: lel.folderAdded(ev);
0381: }
0382: }
0383: return node;
0384: }
0385: return null;
0386: }
0387:
0388: public void clearMarkings() {
0389: markingMode = null;
0390: markingNode = null;
0391: }
0392:
0393: public boolean moveNode(String nodeId, String parentId,
0394: String nextSiblingId) throws PortalException {
0395:
0396: IUserLayoutNodeDescription parent = this .getNode(parentId);
0397: IUserLayoutNodeDescription node = this .getNode(nodeId);
0398: String oldParentNodeId = getParentId(nodeId);
0399: if (canMoveNode(node, parent, nextSiblingId)) {
0400: // must be a folder
0401: Document ulm = this .getUserLayoutDOM();
0402: Element childElement = (Element) ulm.getElementById(nodeId);
0403: Element parentElement = (Element) ulm
0404: .getElementById(parentId);
0405: if (nextSiblingId == null) {
0406: parentElement.appendChild(childElement);
0407: } else {
0408: Node nextSibling = ulm.getElementById(nextSiblingId);
0409: parentElement.insertBefore(childElement, nextSibling);
0410: }
0411: markLayoutDirty();
0412: clearMarkings();
0413: updateCacheKey();
0414:
0415: // inform the listeners
0416: boolean isChannel = false;
0417: if (node instanceof IUserLayoutChannelDescription) {
0418: isChannel = true;
0419: }
0420: LayoutMoveEvent ev = new LayoutMoveEvent(this , node,
0421: oldParentNodeId);
0422: for (Iterator i = listeners.iterator(); i.hasNext();) {
0423: LayoutEventListener lel = (LayoutEventListener) i
0424: .next();
0425: if (isChannel) {
0426: lel.channelMoved(ev);
0427: } else {
0428: lel.folderMoved(ev);
0429: }
0430: }
0431: return true;
0432: } else {
0433: return false;
0434: }
0435: }
0436:
0437: public boolean deleteNode(String nodeId) throws PortalException {
0438: if (canDeleteNode(nodeId)) {
0439: IUserLayoutNodeDescription nodeDescription = this
0440: .getNode(nodeId);
0441: String parentNodeId = this .getParentId(nodeId);
0442:
0443: Document ulm = this .getUserLayoutDOM();
0444: Element childElement = (Element) ulm.getElementById(nodeId);
0445: Node parent = childElement.getParentNode();
0446: if (parent != null) {
0447: parent.removeChild(childElement);
0448: } else {
0449: throw new PortalException("Node \"" + nodeId
0450: + "\" has a NULL parent !");
0451: }
0452: markLayoutDirty();
0453: // clearMarkings(); // this one is questionable
0454: this .updateCacheKey();
0455:
0456: // inform the listeners
0457: boolean isChannel = false;
0458: if (nodeDescription instanceof IUserLayoutChannelDescription) {
0459: isChannel = true;
0460: }
0461: LayoutMoveEvent ev = new LayoutMoveEvent(this ,
0462: nodeDescription, parentNodeId);
0463: for (Iterator i = listeners.iterator(); i.hasNext();) {
0464: LayoutEventListener lel = (LayoutEventListener) i
0465: .next();
0466: if (isChannel) {
0467: lel.channelDeleted(ev);
0468: } else {
0469: lel.folderDeleted(ev);
0470: }
0471: }
0472:
0473: return true;
0474: } else {
0475: return false;
0476: }
0477: }
0478:
0479: public String getRootFolderId() {
0480: try {
0481: if (this .rootNodeId == null) {
0482: String expression = "//layout/folder";
0483: XPathFactory fac = XPathFactory.newInstance();
0484: XPath xpath = fac.newXPath();
0485: Element rootNode = (Element) xpath.evaluate(expression,
0486: this .getUserLayoutDOM(), XPathConstants.NODE);
0487:
0488: this .rootNodeId = rootNode.getAttribute("ID");
0489: }
0490: } catch (Exception e) {
0491: log.error("Exception getting root folder id.", e);
0492: }
0493: return this .rootNodeId;
0494: }
0495:
0496: public synchronized boolean updateNode(
0497: IUserLayoutNodeDescription node) throws PortalException {
0498: if (canUpdateNode(node)) {
0499: // normally here, one would determine what has changed
0500: // but we'll just make sure that the node type has not
0501: // changed and then regenerate the node Element from scratch,
0502: // and attach any children it might have had to it.
0503:
0504: String nodeId = node.getId();
0505: String nextSiblingId = getNextSiblingId(nodeId);
0506: Element nextSibling = null;
0507: if (nextSiblingId != null) {
0508: Document ulm = this .getUserLayoutDOM();
0509: nextSibling = ulm.getElementById(nextSiblingId);
0510: }
0511:
0512: IUserLayoutNodeDescription oldNode = getNode(nodeId);
0513:
0514: if (oldNode instanceof IUserLayoutChannelDescription) {
0515: if (node instanceof IUserLayoutChannelDescription) {
0516: Document ulm = this .getUserLayoutDOM();
0517: // generate new XML Element
0518: Element newChannelElement = node.getXML(ulm);
0519: Element oldChannelElement = (Element) ulm
0520: .getElementById(nodeId);
0521: Node parent = oldChannelElement.getParentNode();
0522: parent.removeChild(oldChannelElement);
0523: parent.insertBefore(newChannelElement, nextSibling);
0524:
0525: // register new child instead
0526: newChannelElement.setIdAttribute("ID", true);
0527: newChannelElement.setAttribute("ID", node.getId());
0528:
0529: // inform the listeners
0530: LayoutEvent ev = new LayoutEvent(this , node);
0531: for (Iterator i = listeners.iterator(); i.hasNext();) {
0532: LayoutEventListener lel = (LayoutEventListener) i
0533: .next();
0534: lel.channelUpdated(ev);
0535: }
0536: } else {
0537: throw new PortalException(
0538: "Change channel to folder is not allowed by updateNode() method!");
0539: }
0540: } else {
0541: // must be a folder
0542: if (node instanceof IUserLayoutFolderDescription) {
0543: Document ulm = this .getUserLayoutDOM();
0544: // generate new XML Element
0545: Element newFolderElement = node.getXML(ulm);
0546: Element oldFolderElement = (Element) ulm
0547: .getElementById(nodeId);
0548: Node parent = oldFolderElement.getParentNode();
0549:
0550: // move children
0551: Vector children = new Vector();
0552: for (Node n = oldFolderElement.getFirstChild(); n != null; n = n
0553: .getNextSibling()) {
0554: children.add(n);
0555: }
0556:
0557: for (int i = 0; i < children.size(); i++) {
0558: newFolderElement.appendChild((Node) children
0559: .get(i));
0560: }
0561:
0562: // replace the actual node
0563: parent.removeChild(oldFolderElement);
0564: parent.insertBefore(newFolderElement, nextSibling);
0565:
0566: // register new child instead
0567: newFolderElement.setIdAttribute("ID", true);
0568: newFolderElement.setAttribute("ID", node.getId());
0569:
0570: // inform the listeners
0571: LayoutEvent ev = new LayoutEvent(this , node);
0572: for (Iterator i = listeners.iterator(); i.hasNext();) {
0573: LayoutEventListener lel = (LayoutEventListener) i
0574: .next();
0575: lel.folderUpdated(ev);
0576: }
0577: }
0578: }
0579: markLayoutDirty();
0580: this .updateCacheKey();
0581:
0582: return true;
0583: } else {
0584: return false;
0585: }
0586: }
0587:
0588: public boolean canAddNode(IUserLayoutNodeDescription node,
0589: String parentId, String nextSiblingId)
0590: throws PortalException {
0591: return this .canAddNode(node, this .getNode(parentId),
0592: nextSiblingId);
0593: }
0594:
0595: protected boolean canAddNode(IUserLayoutNodeDescription node,
0596: IUserLayoutNodeDescription parent, String nextSiblingId)
0597: throws PortalException {
0598: // make sure sibling exists and is a child of nodeId
0599: if (nextSiblingId != null) {
0600: IUserLayoutNodeDescription sibling = getNode(nextSiblingId);
0601: if (sibling == null) {
0602: throw new PortalException(
0603: "Unable to find a sibling node with id=\""
0604: + nextSiblingId + "\"");
0605: }
0606: if (!parent.getId().equals(getParentId(nextSiblingId))) {
0607: throw new PortalException("Given sibling (\""
0608: + nextSiblingId
0609: + "\") is not a child of a given parentId (\""
0610: + parent.getId() + "\")");
0611: }
0612: }
0613:
0614: return (parent != null
0615: && parent instanceof IUserLayoutFolderDescription && !parent
0616: .isImmutable());
0617: }
0618:
0619: public boolean canMoveNode(String nodeId, String parentId,
0620: String nextSiblingId) throws PortalException {
0621: return this .canMoveNode(this .getNode(nodeId), this
0622: .getNode(parentId), nextSiblingId);
0623: }
0624:
0625: protected boolean canMoveNode(IUserLayoutNodeDescription node,
0626: IUserLayoutNodeDescription parent, String nextSiblingId)
0627: throws PortalException {
0628: // is the current parent immutable ?
0629: IUserLayoutNodeDescription currentParent = getNode(getParentId(node
0630: .getId()));
0631: if (currentParent == null) {
0632: throw new PortalException(
0633: "Unable to determine a parent node for node with id=\""
0634: + node.getId() + "\"");
0635: }
0636: return (!currentParent.isImmutable() && canAddNode(node,
0637: parent, nextSiblingId));
0638: }
0639:
0640: public boolean canDeleteNode(String nodeId) throws PortalException {
0641: return canDeleteNode(this .getNode(nodeId));
0642: }
0643:
0644: protected boolean canDeleteNode(IUserLayoutNodeDescription node)
0645: throws PortalException {
0646: return !(node == null || node.isUnremovable());
0647: }
0648:
0649: public boolean canUpdateNode(String nodeId) throws PortalException {
0650: return canUpdateNode(this .getNode(nodeId));
0651: }
0652:
0653: public boolean canUpdateNode(IUserLayoutNodeDescription node) {
0654: return !(node == null || node.isImmutable());
0655: }
0656:
0657: public void markAddTargets(IUserLayoutNodeDescription node)
0658: throws PortalException {
0659: if (node != null) {
0660: this .markingMode = ADD_COMMAND;
0661: } else {
0662: clearMarkings();
0663: }
0664: }
0665:
0666: public void markMoveTargets(String nodeId) throws PortalException {
0667: if (nodeId != null) {
0668: this .markingMode = MOVE_COMMAND;
0669: this .markingNode = nodeId;
0670: } else {
0671: clearMarkings();
0672: }
0673: }
0674:
0675: public String getParentId(String nodeId) throws PortalException {
0676: Document ulm = this .getUserLayoutDOM();
0677: Element nelement = (Element) ulm.getElementById(nodeId);
0678: if (nelement != null) {
0679: Node parent = nelement.getParentNode();
0680: if (parent != null) {
0681: if (parent.getNodeType() != Node.ELEMENT_NODE) {
0682: throw new PortalException(
0683: "Node with id=\""
0684: + nodeId
0685: + "\" is attached to something other then an element node.");
0686: } else {
0687: Element e = (Element) parent;
0688: return e.getAttribute("ID");
0689: }
0690: } else {
0691: return null;
0692: }
0693: } else {
0694: throw new PortalException("Node with id=\"" + nodeId
0695: + "\" doesn't exist.");
0696: }
0697: }
0698:
0699: public String getNextSiblingId(String nodeId)
0700: throws PortalException {
0701: Document ulm = this .getUserLayoutDOM();
0702: Element nelement = (Element) ulm.getElementById(nodeId);
0703: if (nelement != null) {
0704: Node nsibling = nelement.getNextSibling();
0705: // scroll to the next element node
0706: while (nsibling != null
0707: && nsibling.getNodeType() != Node.ELEMENT_NODE) {
0708: nsibling = nsibling.getNextSibling();
0709: }
0710: if (nsibling != null) {
0711: Element e = (Element) nsibling;
0712: return e.getAttribute("ID");
0713: } else {
0714: return null;
0715: }
0716: } else {
0717: throw new PortalException("Node with id=\"" + nodeId
0718: + "\" doesn't exist.");
0719: }
0720: }
0721:
0722: public String getPreviousSiblingId(String nodeId)
0723: throws PortalException {
0724: Document ulm = this .getUserLayoutDOM();
0725: Element nelement = (Element) ulm.getElementById(nodeId);
0726: if (nelement != null) {
0727: Node nsibling = nelement.getPreviousSibling();
0728: // scroll to the next element node
0729: while (nsibling != null
0730: && nsibling.getNodeType() != Node.ELEMENT_NODE) {
0731: nsibling = nsibling.getNextSibling();
0732: }
0733: if (nsibling != null) {
0734: Element e = (Element) nsibling;
0735: return e.getAttribute("ID");
0736: } else {
0737: return null;
0738: }
0739: } else {
0740: throw new PortalException("Node with id=\"" + nodeId
0741: + "\" doesn't exist.");
0742: }
0743: }
0744:
0745: public Enumeration getChildIds(String nodeId)
0746: throws PortalException {
0747: Vector v = new Vector();
0748: IUserLayoutNodeDescription node = getNode(nodeId);
0749: if (node instanceof IUserLayoutFolderDescription) {
0750: Document ulm = this .getUserLayoutDOM();
0751: Element felement = (Element) ulm.getElementById(nodeId);
0752: for (Node n = felement.getFirstChild(); n != null; n = n
0753: .getNextSibling()) {
0754: if (n.getNodeType() == Node.ELEMENT_NODE) {
0755: Element e = (Element) n;
0756: if (e.getAttribute("ID") != null) {
0757: v.add(e.getAttribute("ID"));
0758: }
0759: }
0760: }
0761: }
0762: return v.elements();
0763: }
0764:
0765: public String getCacheKey() {
0766: if (markingMode == null) {
0767: return this .cacheKey;
0768: } else {
0769: if (markingNode != null) {
0770: return this .cacheKey + this .markingMode
0771: + this .markingNode;
0772: } else {
0773: return this .cacheKey + this .markingMode;
0774: }
0775: }
0776: }
0777:
0778: /**
0779: * This is outright cheating ! We're supposed to analyze the user layout tree
0780: * and return a key that corresponds uniqly to the composition and the sturcture of the tree.
0781: * Here we just return a different key wheneever anything changes. So if one was to move a
0782: * node back and forth, the key would always never (almost) come back to the original value,
0783: * even though the changes to the user layout are cyclic.
0784: *
0785: */
0786: private void updateCacheKey() {
0787: this .cacheKey = Long.toString(rnd.nextLong());
0788: }
0789:
0790: public int getLayoutId() {
0791: return profile.getLayoutId();
0792: }
0793:
0794: /**
0795: * Returns a subscription id given a functional name.
0796: *
0797: * @param fname the functional name to lookup.
0798: * @return a <code>String</code> subscription id.
0799: */
0800: public String getSubscribeId(String fname) throws PortalException {
0801: try {
0802: String expression = "//channel[@fname=\'" + fname + "\']";
0803: XPathFactory fac = XPathFactory.newInstance();
0804: XPath xpath = fac.newXPath();
0805: Element fnameNode = (Element) xpath.evaluate(expression,
0806: this .getUserLayoutDOM(), XPathConstants.NODE);
0807: if (fnameNode != null) {
0808: return fnameNode.getAttribute("ID");
0809: } else {
0810: return null;
0811: }
0812: } catch (XPathExpressionException e) {
0813: log
0814: .error(
0815: "SimpleUserLayoutManager::getSubcribeId() : "
0816: + "encountered exception while trying to identify subscribe channel id for the fname=\""
0817: + fname + "\" : ", e);
0818: return null;
0819: }
0820: }
0821:
0822: public boolean addLayoutEventListener(LayoutEventListener l) {
0823: return listeners.add(l);
0824: }
0825:
0826: public boolean removeLayoutEventListener(LayoutEventListener l) {
0827: return listeners.remove(l);
0828: }
0829:
0830: /**
0831: * A factory method to create an empty <code>IUserLayoutNodeDescription</code> instance
0832: *
0833: * @param nodeType a node type value
0834: * @return an <code>IUserLayoutNodeDescription</code> instance
0835: * @exception PortalException if the error occurs.
0836: */
0837: public IUserLayoutNodeDescription createNodeDescription(int nodeType)
0838: throws PortalException {
0839: switch (nodeType) {
0840: case IUserLayoutNodeDescription.FOLDER:
0841: return new UserLayoutFolderDescription();
0842: case IUserLayoutNodeDescription.CHANNEL:
0843: return new UserLayoutChannelDescription();
0844: default:
0845: return null;
0846: }
0847: }
0848:
0849: protected boolean isLayoutDirty() {
0850: return dirtyState;
0851: }
0852:
0853: private void markLayoutDirty() {
0854: dirtyState = true;
0855: }
0856:
0857: private void clearDirtyFlag() {
0858: dirtyState = false;
0859: }
0860:
0861: /* (non-Javadoc)
0862: * @see org.jasig.portal.layout.IUserLayoutManager#processLayoutParameters(org.jasig.portal.security.IPerson, org.jasig.portal.UserPreferences, javax.servlet.http.HttpServletRequest)
0863: */
0864: public void processLayoutParameters(IPerson person,
0865: UserPreferences userPrefs, HttpServletRequest req)
0866: throws PortalException {
0867: try {
0868: String newNodeId = null;
0869:
0870: // Sending the theme stylesheets parameters based on the user security context
0871: ThemeStylesheetUserPreferences themePrefs = userPrefs
0872: .getThemeStylesheetUserPreferences();
0873: StructureStylesheetUserPreferences structPrefs = userPrefs
0874: .getStructureStylesheetUserPreferences();
0875:
0876: String authenticated = String.valueOf(person
0877: .getSecurityContext().isAuthenticated());
0878: structPrefs.putParameterValue("authenticated",
0879: authenticated);
0880: String userName = person.getFullName();
0881: if (userName != null && userName.trim().length() > 0)
0882: themePrefs.putParameterValue("userName", userName);
0883: try {
0884: if (ChannelStaticData.getAuthorizationPrincipal(person)
0885: .canPublish()) {
0886: themePrefs.putParameterValue(
0887: "authorizedFragmentPublisher", "true");
0888: themePrefs.putParameterValue(
0889: "authorizedChannelPublisher", "true");
0890: }
0891: } catch (Exception e) {
0892: log.error("Exception determining publish rights for "
0893: + person, e);
0894: }
0895:
0896: String[] values;
0897:
0898: if ((values = req
0899: .getParameterValues("uP_request_move_targets")) != null) {
0900: if (values[0].trim().length() == 0)
0901: values[0] = null;
0902: this .markMoveTargets(values[0]);
0903: } else {
0904: this .markMoveTargets(null);
0905: }
0906:
0907: if ((values = req
0908: .getParameterValues("uP_request_add_targets")) != null) {
0909: String value;
0910: int nodeType = values[0].equals("folder") ? IUserLayoutNodeDescription.FOLDER
0911: : IUserLayoutNodeDescription.CHANNEL;
0912: IUserLayoutNodeDescription nodeDesc = this
0913: .createNodeDescription(nodeType);
0914: nodeDesc.setName("Unnamed");
0915: if (nodeType == IUserLayoutNodeDescription.CHANNEL
0916: && (value = req
0917: .getParameter("channelPublishID")) != null) {
0918: String contentPublishId = value.trim();
0919: if (contentPublishId.length() > 0) {
0920: ((IUserLayoutChannelDescription) nodeDesc)
0921: .setChannelPublishId(contentPublishId);
0922: themePrefs.putParameterValue(
0923: "channelPublishID", contentPublishId);
0924: }
0925: }
0926: newNodeDescription = nodeDesc;
0927: this .markAddTargets(newNodeDescription);
0928: } else {
0929: this .markAddTargets(null);
0930: }
0931:
0932: if ((values = req.getParameterValues("uP_add_target")) != null) {
0933: String[] values1, values2;
0934: String value = null;
0935: values1 = req.getParameterValues("targetNextID");
0936: if (values1 != null && values1.length > 0)
0937: value = values1[0];
0938: if ((values2 = req.getParameterValues("targetParentID")) != null) {
0939: if (newNodeDescription != null) {
0940: if (CommonUtils.nvl(value).trim().length() == 0)
0941: value = null;
0942:
0943: // Adding a new node
0944: newNodeId = this .addNode(newNodeDescription,
0945: values2[0], value).getId();
0946: }
0947: }
0948: newNodeDescription = null;
0949: }
0950:
0951: if ((values = req.getParameterValues("uP_move_target")) != null) {
0952: String[] values1, values2;
0953: String value = null;
0954: values1 = req.getParameterValues("targetNextID");
0955: if (values1 != null && values1.length > 0)
0956: value = values1[0];
0957: if ((values2 = req.getParameterValues("targetParentID")) != null) {
0958: if (CommonUtils.nvl(value).trim().length() == 0)
0959: value = null;
0960: this .moveNode(values[0], values2[0], value);
0961: }
0962: }
0963:
0964: if ((values = req.getParameterValues("uP_rename_target")) != null) {
0965: String[] values1;
0966: if ((values1 = req.getParameterValues("uP_target_name")) != null) {
0967: IUserLayoutNodeDescription nodeDesc = this
0968: .getNode(values[0]);
0969: if (nodeDesc != null) {
0970: String oldName = nodeDesc.getName();
0971: nodeDesc.setName(values1[0]);
0972: if (!this .updateNode(nodeDesc))
0973: nodeDesc.setName(oldName);
0974: }
0975: }
0976: }
0977:
0978: if ((values = req.getParameterValues("uP_remove_target")) != null) {
0979: for (int i = 0; i < values.length; i++) {
0980: this .deleteNode(values[i]);
0981: }
0982: }
0983:
0984: String param = req.getParameter("uP_cancel_targets");
0985: if (param != null && param.equals("true")) {
0986: this .markAddTargets(null);
0987: this .markMoveTargets(null);
0988: newNodeDescription = null;
0989: }
0990:
0991: param = req.getParameter("uP_reload_layout");
0992: if (param != null && param.equals("true")) {
0993: this .loadUserLayout();
0994: }
0995:
0996: // If we have created a new node we need to let the structure XSL know about it
0997: structPrefs.putParameterValue("newNodeID", CommonUtils
0998: .nvl(newNodeId));
0999: } catch (Exception e) {
1000: throw new PortalException(e);
1001: }
1002: }
1003: }
|