Friday 28 October 2011

ADF : Dynamically disabling Command Link component as contained in Rich Tree node stamp

I decided to use a RichCommandLink component as part of my RichTree(Menu) node stamp in order to produce actions to navigate in the application. Later on the requirement came that some sections of the system should be viewable and hence even the link to these sections should be disabled in order to prevent users from going there in the first place. Of course, the question remains why the menu was not configured correctly in the first place, because why show the tree node if it's going to be disabled anyway...but that's besides the point for this post. :-P

Well to dynamically disable the command links as contained in the tree, I've followed the following approach:
  • Set a JSP/JSTL core variable equal to some property in the node binding on which you can base the check for the view privilege. In my case I used a variable called: "mapParameter", with its value as a concatenated string containing the node's view name.
<c:set var="mapParameter" value="isViewable:#{node.ViewName}" />
  • Then, I used the above parameter in the EL Expression as value for the disabled property of the command link. You'll notice I've made the backing bean (called: menuBean) implement the Map interface in order to check for view privileges with the view name as parameter.
<af:commandLink text="#{node.Name}" id="cl1" action="someAction"
          disabled="#{((menuBean.hasPrivilege[mapParameter] == 'false') ? true : false)}"/>


I know Maps are probably not the best way in making things happen in backing beans, but I've managed to do all sorts of weird and wonderful things by using the Map method. :-)

ADF Automatically disabling and setting read only status on UI components based on fine-grained privileges

I had to automatically apply the view/select type of privilege to implemented ADF pages. The idea was to not have to put EL expression as value to all appropriate components' "disabled" or "read only" properties (hence it should happen 100% automatically). So, my solution for now was to implement a phase listener and within the afterPhase() method execute the following java code:
  // checking whether security privileges are applicable in the sense that
  // - the user is logged in
  // - the security privileges have been loaded for the user.
  if(isLoggedIn() && (null != JSFUtils.getFromSession(SessionKeys.VIEW_ENTITY_PRIVS.getKey()))) {
      // first get a reference to the View Root component
      FacesContext fCtx = phaseEvent.getFacesContext();
      UIViewRoot viewRoot = fCtx.getViewRoot();
      // We have a template component which wraps all our implemented ADF forms     
      UIComponent template = viewRoot.findComponent("p_tmpl");
      if (template != null) {
        // the template knows which Form Bean (backing bean) is tied to the current view/response
        FormBean formBean = (FormBean) template.getAttributes().get("formBean"); 
        if (formBean != null) {
          // The Form Bean knows which logical view is in question.
          String viewName = formBean.getViewNameForPrivilege();         
          if (!FormBean.NO_PRIVS_APPLIED.equals(viewName)) {
            boolean privsExist = PrivilegesUtil.doesViewEntityPrivilegesExist(viewName);
            logger.error("View PRIVS Exist?["+privsExist+"]");           
            // then if no view privileges exist for this view...we disable and set to read only all the components
            if (!privsExist) {
              logger.error("Going to cascade!!");
              this.cascadeDisableAndReadOnly(template.getFacetsAndChildren());
                } // end if (privileges don't exist)
          } // end if NOT default view name
        } // end if (formBean != null)
      } // end if (template != null)
  } // end if (isLoggedIn() && Privileges have been loaded)

For completeness sake I've provided the implementation of the cascadeDisableAndReadOnly() method:
private void cascadeDisableAndReadOnly(final Iterator<UIComponent> children) {
    if(children != null) {
      while (children.hasNext()){
          UIComponent child = children.next();              
          // PLEASE NOTE: One can add some skip conditions based on the nature and instance of the child 
          // FOR EXAMPLE: Skip any OraFormsFaces Form as child
          if (child instanceof Form) {
              logger.error("Detected OraFormsFacesForm...");
              continue;
          }
          cascadeReadOnly(child.getFacetsAndChildren());       
          child.getAttributes().put("readOnly", true);
          child.getAttributes().put("disabled", true);
      } // end while (children.hasNext())
    } // en if (children != null)
}

Friday 21 October 2011

ADF Tree : Row Disclosure Listener which expands below the disclosed node

I recently had the scenario where more than one level of a tree node had to be expanded on the disclosure of a particular tree node.

For example, say you have the below tree:
-A
-B
  - B1
  - B2
    - B2a
    - B2b
  - B3
-C
then, on the disclosure of tree node B you would want all the children (B1, B2, B3, B2a, B2b) of tree node B to be expanded.

I've done it as follow. I've added the following property to my ADF Tree:
rowDisclosureListener="#{treeBean.discloseTreeNodeChildren}" 

Then I implemented the referenced method in my "treeBean" backing bean. It assumes a tree binding called: "tree." See the listing below:
public void discloseTreeNodeChildren(RowDisclosureEvent event) {
    RowKeySet addedKeys = event.getAddedSet();
    Iterator addedIt = addedKeys.iterator();
    if(addedIt.hasNext()) {
        Object addedKey = addedIt.next();
        TreeModel treeModel = (TreeModel) this.tree.getValue();
        JUCtrlHierNodeBinding nodeBinding = (JUCtrlHierNodeBinding)
               treeModel.getRowData(addedKey);
        String isLeaf = (String) nodeBinding.getAttribute("isLeaf");
       
          // you have to have a condition to break the series of disclosure events!!
        if(!(new Boolean(isLeaf))) {
            TreeUtility.expandAllNodes(nodeBinding, this.tree.getDisclosedRowKeys(), 0, 1);
            AdfFacesContext.getCurrentInstance().addPartialTarget(this.tree);
        }
    }
}

Now the utility method called: "expandAllNodes()" I've implemented as such:
protected static void expandAllNodes(JUCtrlHierNodeBinding nodeBinding, RowKeySet
          disclosedKeys) {   
    List<JUCtrlHierNodeBinding> childNodes = (List<JUCtrlHierNodeBinding>)
        nodeBinding.getChildren();     
    ArrayList newKeys = new ArrayList();
    if(childNodes != null) {
        for(JUCtrlHierNodeBinding node : childNodes) {           
            newKeys.add(node.getKeyPath());
            // recursive call to the method expandAllNodes()         
            expandAllNodes(node, disclosedKeys);
        }
    }
    disclosedKeys.addAll(newKeys);     

}
A handy addition to the utility method above is to add currentLevel and targetLevel integers in order to apply a boundary to the expansion of nodes. This gives the ability to only expand n levels below the disclosed node...comes in handy in those "big oak trees!!" :-)