/**
* 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;
import org.w3c.dom.NodeList;
/**
* Looks for, applies against the ilf, and updates accordingly within the plf the set of parameter
* edits made against channels incorporated from fragments.
*
* @since 2.6
*/
public class ParameterEditManager {
private static final Log LOG = LogFactory.getLog(ParameterEditManager.class);
private static IUserLayoutStore dls = null;
/**
* 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 parm edit set if any from the plf and process each edit command removing any that
* fail from the set so that the set is self cleaning.
*
* @throws Exception
*/
static void applyAndUpdateParmEditSet(Document plf, Document ilf, IntegrationResult result) {
Element pSet = null;
try {
pSet = getParmEditSet(plf, null, false);
} catch (Exception e) {
LOG.error("Exception occurred while getting user's DLM " + "paramter-edit-set.", e);
}
if (pSet == null) return;
NodeList edits = pSet.getChildNodes();
for (int i = edits.getLength() - 1; i >= 0; i--) {
if (applyEdit((Element) edits.item(i), ilf) == false) {
pSet.removeChild(edits.item(i));
result.setChangedPLF(true);
} else {
result.setChangedILF(true);
}
}
if (pSet.getChildNodes().getLength() == 0) {
plf.getDocumentElement().removeChild(pSet);
result.setChangedPLF(true);
}
}
/**
* Attempt to apply a single channel parameter edit command and return true if it succeeds or
* false otherwise. If the edit is disallowed or the target element no longer exists in the
* document the edit command fails and returns false.
*
* @throws Exception
*/
private static boolean applyEdit(Element edit, Document ilf) {
String nodeID = edit.getAttribute(Constants.ATT_TARGET);
Element channel = ilf.getElementById(nodeID);
if (channel == null) return false;
// now get the name of the parameter to be edited and find that element
String parmName = edit.getAttribute(Constants.ATT_NAME);
String parmValue = edit.getAttribute(Constants.ATT_USER_VALUE);
NodeList ilfParms = channel.getChildNodes();
Element targetParm = null;
for (int i = 0; i < ilfParms.getLength(); i++) {
Element ilfParm = (Element) ilfParms.item(i);
if (ilfParm.getAttribute(Constants.ATT_NAME).equals(parmName)) {
targetParm = ilfParm;
break;
}
}
if (targetParm == null) // parameter not found so we are free to set
{
Element parameter = ilf.createElement("parameter");
parameter.setAttribute("name", parmName);
parameter.setAttribute("value", parmValue);
parameter.setAttribute("override", "yes");
channel.appendChild(parameter);
return true;
}
/* TODO Add support for fragments to set dlm:editAllowed attribute for
* channel parameters. (2005.11.04 mboyd)
*
* In the commented code below, the check for editAllowed will never be
* seen on a parameter element in the
* current database schema approach used by DLM. This is because
* parameters are second class citizens of the layout structure. They
* are not found in the up_layout_struct table but only in the
* up_layout_param table. DLM functionality like dlm:editAllowed,
* dlm:moveAllowed, dlm:deleteAllowed, and dlm:addChildAllowed were
* implemented without schema changes by adding these as parameters to
* structural elements and upon loading any parameter that begins with
* 'dlm:' is placed as an attribute on the containing structural
* element. So any channel parameter entry with dlm:editAllowed has that
* value placed as an attribute on the containing channel not on the
* parameter that was meant to have it.
*
* The only solution would be to add special dlm:parm children below
* channels that would get the editAllowed value and then when creating
* the DOM don't create those as child elements but use them to set the
* attribute on the corresponding parameter by having the name of the
* dlm:parm element be the name of the parameter to which it is to be
* related.
*
* The result of this lack of functionality is that fragments can't
* mark any channel parameters as dlm:editAllowed='false' thereby
* further restricting which channel parameters can be edited beyond
* what the channel definition specifies during publishing.
*/
//Attr editAllowed = targetParm.getAttributeNode( Constants.ATT_EDIT_ALLOWED );
//if ( editAllowed != null && editAllowed.getNodeValue().equals("false"))
// return false;
// target parm found. See if channel definition will still allow changes.
Attr override = targetParm.getAttributeNode(Constants.ATT_OVERRIDE);
if (override != null && !override.getNodeValue().equals(Constants.CAN_OVERRIDE))
return false;
// now see if the change is still needed
if (targetParm.getAttribute(Constants.ATT_VALUE).equals(parmValue))
return false; // user's edit same as fragment or chan def
targetParm.setAttribute("value", parmValue);
return true;
}
/**
* Get the parameter edits set if any stored in the root of the document or create it if
* passed-in create flag is true.
*/
private static Element getParmEditSet(Document plf, IPerson person, boolean create)
throws PortalException {
Node root = plf.getDocumentElement();
Node child = root.getFirstChild();
while (child != null) {
if (child.getNodeName().equals(Constants.ELM_PARM_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 parameter edit set node "
+ "Id for userId="
+ person.getID(),
e);
}
Element parmSet = plf.createElement(Constants.ELM_PARM_SET);
parmSet.setAttribute(Constants.ATT_TYPE, Constants.ELM_PARM_SET);
parmSet.setAttribute(Constants.ATT_ID, ID);
parmSet.setIdAttribute(Constants.ATT_ID, true);
root.appendChild(parmSet);
return parmSet;
}
/**
* Create and append a parameter edit directive to parameter edits set for applying a user
* specified value to a named parameter of the incorporated channel represented by the passed-in
* target id. If one already exists for that node and that name then the value of the existing
* edit is changed to the passed-in value.
*/
public static synchronized void addParmEditDirective(
String targetId, String name, String value, IPerson person)
throws PortalException {
Document plf = (Document) person.getAttribute(Constants.PLF);
Element parmSet = getParmEditSet(plf, person, true);
NodeList edits = parmSet.getChildNodes();
Element existingEdit = null;
for (int i = 0; i < edits.getLength(); i++) {
Element edit = (Element) edits.item(i);
if (edit.getAttribute(Constants.ATT_TARGET).equals(targetId)
&& edit.getAttribute(Constants.ATT_NAME).equals(name)) {
existingEdit = edit;
break;
}
}
if (existingEdit == null) // existing one not found, create a new one
{
addParmEditDirective(targetId, name, value, person, plf, parmSet);
return;
}
existingEdit.setAttribute(Constants.ATT_USER_VALUE, value);
}
/**
* This method does the actual work of adding a newly created parameter edit and adding it to
* the parameter edits set.
*/
private static void addParmEditDirective(
String targetID,
String name,
String value,
IPerson person,
Document plf,
Element parmSet)
throws PortalException {
String ID = null;
try {
ID = getDLS().getNextStructDirectiveId(person);
} catch (Exception e) {
throw new PortalException(
"Exception encountered while "
+ "generating new parameter edit node "
+ "Id for userId="
+ person.getID(),
e);
}
Element parm = plf.createElement(Constants.ELM_PARM_EDIT);
parm.setAttribute(Constants.ATT_TYPE, Constants.ELM_PARM_EDIT);
parm.setAttribute(Constants.ATT_ID, ID);
parm.setIdAttribute(Constants.ATT_ID, true);
parm.setAttributeNS(Constants.NS_URI, Constants.ATT_TARGET, targetID);
parm.setAttribute(Constants.ATT_NAME, name);
parm.setAttribute(Constants.ATT_USER_VALUE, value);
parmSet.appendChild(parm);
}
/**
* Remove a parameter edit directive from the parameter edits set for applying user specified
* values to a named parameter of an incorporated channel represented by the passed-in target
* id. If one doesn't exists for that node and that name then this call returns without any
* effects.
*/
public static void removeParmEditDirective(String targetId, String name, IPerson person)
throws PortalException {
Document plf = (Document) person.getAttribute(Constants.PLF);
Element parmSet = getParmEditSet(plf, person, false);
if (parmSet == null) return; // no set so no edit to remove
NodeList edits = parmSet.getChildNodes();
for (int i = 0; i < edits.getLength(); i++) {
Element edit = (Element) edits.item(i);
if (edit.getAttribute(Constants.ATT_TARGET).equals(targetId)
&& edit.getAttribute(Constants.ATT_NAME).equals(name)) {
parmSet.removeChild(edit);
break;
}
}
if (parmSet.getChildNodes().getLength() == 0) // no more edits, remove
{
Node parent = parmSet.getParentNode();
parent.removeChild(parmSet);
}
}
}