/** * Licensed to Apereo under one or more contributor license agreements. See the NOTICE file * distributed with this work for additional information regarding copyright ownership. Apereo * licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use * this file except in compliance with the License. You may obtain a copy of the License at the * following location: * * <p>http://www.apache.org/licenses/LICENSE-2.0 * * <p>Unless required by applicable law or agreed to in writing, software distributed under the * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing permissions and * limitations under the License. */ package org.apereo.portal.layout.dlm; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apereo.portal.PortalException; import org.apereo.portal.layout.IUserLayoutStore; import org.apereo.portal.security.IPerson; import org.apereo.portal.spring.locator.UserLayoutStoreLocator; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; /** * Handles ILF node edit directives recorded in the PLF. * * @since 2.5 */ public class EditManager { private static IUserLayoutStore dls = null; private static final Log LOG = LogFactory.getLog(EditManager.class); /** * Hands back the single instance of RDBMDistributedLayoutStore. There is already a method for * aquiring a single instance of the configured layout store so we delegate over there so that * all references refer to the same instance. This method is solely for convenience so that we * don't have to keep calling UserLayoutStoreFactory and casting the resulting class. */ private static IUserLayoutStore getDLS() { if (dls == null) { dls = UserLayoutStoreLocator.getUserLayoutStore(); } return dls; } /** * Get the edit set if any stored in the passed in node. If not found and if the create flag is * true then create a new edit set and add it as a child to the passed in node. Then return it. */ private static Element getEditSet(Element node, Document plf, IPerson person, boolean create) throws PortalException { Node child = node.getFirstChild(); while (child != null) { if (child.getNodeName().equals(Constants.ELM_EDIT_SET)) return (Element) child; child = child.getNextSibling(); } if (create == false) return null; String ID = null; try { ID = getDLS().getNextStructDirectiveId(person); } catch (Exception e) { throw new PortalException( "Exception encountered while " + "generating new edit set node " + "Id for userId=" + person.getID(), e); } Element editSet = plf.createElement(Constants.ELM_EDIT_SET); editSet.setAttribute(Constants.ATT_TYPE, Constants.ELM_EDIT_SET); editSet.setAttribute(Constants.ATT_ID, ID); node.appendChild(editSet); return editSet; } /** * Create and append an edit directive to the edit set if not there. This only records that the * attribute was changed and the value in the plf copy node should be used, if allowed, during * the merge at login time. */ static void addEditDirective(Element plfNode, String attributeName, IPerson person) throws PortalException { addDirective(plfNode, attributeName, Constants.ELM_EDIT, person); } /** * Create and append a user preferences edit directive to the edit set if not there. This only * records that the attribute was changed. The value will be in the user preferences object for * the user. */ public static void addPrefsDirective(Element plfNode, String attributeName, IPerson person) throws PortalException { addDirective(plfNode, attributeName, Constants.ELM_PREF, person); } /** Create and append an edit directive to the edit set if not there. */ private static void addDirective( Element plfNode, String attributeName, String type, IPerson person) throws PortalException { Document plf = (Document) person.getAttribute(Constants.PLF); Element editSet = getEditSet(plfNode, plf, person, true); // see if attributes has already been marked as being edited Element child = (Element) editSet.getFirstChild(); Element edit = null; while (child != null && edit == null) { if (child.getNodeName().equals(type) && child.getAttribute(Constants.ATT_NAME).equals(attributeName)) edit = child; child = (Element) child.getNextSibling(); } if (edit == null) // if not found then newly mark as edited { String ID = null; try { ID = getDLS().getNextStructDirectiveId(person); } catch (Exception e) { throw new PortalException( "Exception encountered while " + "generating new edit node " + "Id for userId=" + person.getID(), e); } edit = plf.createElement(type); edit.setAttribute(Constants.ATT_TYPE, type); edit.setAttribute(Constants.ATT_ID, ID); edit.setAttribute(Constants.ATT_NAME, attributeName); editSet.appendChild(edit); } } /** * Evaluate whether attribute changes exist in the ilfChild and if so apply them. Returns true * if some changes existed. If changes existed but matched those in the original node then they * are not applicable, are removed from the editSet, and false is returned. */ public static boolean applyEditSet(Element plfChild, Element original) { // first get edit set if it exists Element editSet = null; try { editSet = getEditSet(plfChild, null, null, false); } catch (Exception e) { // should never occur unless problem during create in getEditSet // and we are telling it not to create. return false; } if (editSet == null || editSet.getChildNodes().getLength() == 0) return false; if (original.getAttribute(Constants.ATT_EDIT_ALLOWED).equals("false")) { // can't change anymore so discard changes plfChild.removeChild(editSet); return false; } Document ilf = original.getOwnerDocument(); boolean attributeChanged = false; Element edit = (Element) editSet.getFirstChild(); while (edit != null) { String attribName = edit.getAttribute(Constants.ATT_NAME); Attr attr = plfChild.getAttributeNode(attribName); // preferences are only updated at preference storage time so // if a preference change exists in the edit set assume it is // still valid so that the node being edited will persist in // the PLF. if (edit.getNodeName().equals(Constants.ELM_PREF)) attributeChanged = true; else if (attr == null) { // attribute removed. See if needs removing in original. attr = original.getAttributeNode(attribName); if (attr == null) // edit irrelevant, editSet.removeChild(edit); else { // edit differs, apply to original original.removeAttribute(attribName); attributeChanged = true; } } else { // attribute there, see if original is also there Attr origAttr = original.getAttributeNode(attribName); if (origAttr == null) { // original attribute isn't defined so need to add origAttr = (Attr) ilf.importNode(attr, true); original.setAttributeNode(origAttr); attributeChanged = true; } else { // original attrib found, see if different if (attr.getValue().equals(origAttr.getValue())) { // they are the same, edit irrelevant editSet.removeChild(edit); } else { // edit differs, apply to original origAttr.setValue(attr.getValue()); attributeChanged = true; } } } edit = (Element) edit.getNextSibling(); } return attributeChanged; } /** * Searches for a dlm:pref command which indicates that a user preference was change and if * found removes it from the user's PLF. */ public static void removePreferenceDirective( IPerson person, String elementId, String attributeName) { removeDirective(elementId, attributeName, Constants.ELM_PREF, person); } /** * Searches for a dlm:edit command which indicates that a node attribute was reset to the value * in the fragment and if found removes it from the user's PLF. */ public static void removeEditDirective(String elementId, String attributeName, IPerson person) { removeDirective(elementId, attributeName, Constants.ELM_EDIT, person); } /** Searches for a command of the passed-in type and if found removes it from the user's PLF. */ private static void removeDirective( String elementId, String attributeName, String type, IPerson person) { Document plf = (Document) person.getAttribute(Constants.PLF); Element node = plf.getElementById(elementId); if (node == null) return; Element editSet = null; try { editSet = getEditSet(node, plf, person, false); } catch (Exception e) { /* * we should never get here since we are calling getEditSet passing * create=false meaning that the only portion of that method that * tosses an exception will not be reached with this call. But if a * runtime exception somehow occurs we will log it so that we don't * lose the information. */ LOG.error(e, e); return; } // if no edit set then the edit can't be there either if (editSet == null) return; Node child = editSet.getFirstChild(); while (child != null) { if (child.getNodeName().equals(type)) { Attr attr = ((Element) child).getAttributeNode(Constants.ATT_NAME); if (attr != null && attr.getValue().equals(attributeName)) { // we found it, remove it editSet.removeChild(child); break; } } child = child.getNextSibling(); } // if that was the last on in the edit set then delete it if (editSet.getFirstChild() == null) node.removeChild(editSet); } }