Menu management is an important part of today’s software development. If we develop an enterprise software with 100s pages, it would be stupidity to write code for menu at every page. We have to have a common menu configuration file, where we can change menu elements. But, there are also some problem, file may be deleted by any developer. After deployment the project, we cannot change menu items at run time. Moreover its very difficult to give roles to menu items at file. Its mandatory to create database driven menu for a dynamic, role based enterprise project.
Here we are going to discuss about building a database driven menu step by step.
1. Create a user table with login name, password and other static information of a user.
2. Create a ROLE_MASTER table with all role_id, role_description and maker_id.
3. Create a ROLE_DETAILS table, which contains mapping of role_id and page link. Columns this table will be ROLE_ID & ROLE_FUNCTION. This table will contain which role get which page. One page may be assigned to multiple role_id.
4. Now you have to create a table named EM_MENUMAS with the column of SLNO,NM,PARENTNM,NODETYPE & PAGE_NAME. Here, SLNO is just row number, NM is menu item name which will be shown at pages, PARENTNM is the NM under which this sub-menu is used. if this item is not under any item, then, this column will be blank. NODETYPE will M or L. if it is leaf item or sub menu, then use L other which use M. PAGE_NAME is page link for this menu item. This will be exactly same with ROLE_FUNCTION of ROLE_DETAILS table.
5. Create a table USERROLES with column of USERID & ROLEID. Here we map roles to users.
6. Create a stored function named TREE_MENU. Code of the function is :
CREATE OR REPLACE function DBBL.tree_menu ( v_user_id in varchar2 )
return varchar2 is
v_ret varchar2(5000) := ”;
t_ret varchar2(5000);
type t_rec is record (lvl number, slno number, nm varchar2(50), nodetype varchar2(10));
type t_tab is table of t_rec index by binary_integer;
v_tab t_tab;
pos integer := 1;
begin
/*for i in (select level lvl, slno, nm, lpad(‘ ‘, level * 14, ‘ ‘) || nm menu, parentnm,nodetype, page_name from em_menumas
start with parentnm is null
connect by prior nm = parentnm
order siblings by slno)*/
for i in (select level lvl, slno, nm, lpad(‘ ‘, level * 14, ‘ ‘) || nm menu, parentnm,nodetype, page_name from em_menumas
where (page_name in ( select role_function from role_detail
where role_id in ( select roleid from userroles where userid= v_user_id )
) and nodetype=’L’) or nodetype=’M’ and (parentnm in (select roleid from USERROLES where userid=v_user_id)
or parentnm in (select ‘Remittance’ from dual) or parentnm is null )
and nm not in (select ROLE_ID from ROLE_MASTER where role_id not in (select roleid from USERROLES where userid=v_user_id))
start with parentnm is null
connect by prior nm = parentnm
order by slno)
loop
if i.nodetype = ‘M’ then
if pos> 1 then
for k in reverse 1 .. v_tab.count
loop
if v_tab(k).lvl = i.lvl then
t_ret := t_ret || ‘</ul></li>’;
goto lbl;
end if;
end loop;
end if;
<<lbl>>
v_tab(pos).lvl := i.lvl;
v_tab(pos).slno := i.slno;
v_tab(pos).nm := i.nm;
v_tab(pos).nodetype := i.nodetype;
t_ret := t_ret || ‘<li>’ || i.nm || ‘<ul>’;
pos := pos + 1;
else
–t_ret := t_ret || ‘<li>’ || case when i.PAGE_NAME is not null then ‘<a onclick=”getPage(“‘ || i.PAGE_NAME || ‘”)”>’ else ” end || ” || i.nm || ‘</li>’;
— t_ret := t_ret || ‘<li>’ || case when i.PAGE_NAME is not null then ‘<a onclick=”getPage(”’ || i.PAGE_NAME || ”’)”>’ || i.nm || ‘</a></li>’ else i.nm || ‘</li>’ end;
if i.PAGE_NAME=’ViewProjectDetails.jsp’ then
t_ret := t_ret || ‘<li>’ || case when i.PAGE_NAME is not null then ‘<a onclick=”getPage(”’ || i.PAGE_NAME || ‘?prjID=’ || v_user_id || ”’)”>’ || i.nm || ‘</a></li>’ else i.nm || ‘</li>’ end;
else
t_ret := t_ret || ‘<li>’ || case when i.PAGE_NAME is not null then ‘<a onclick=”getPage(”’ || i.PAGE_NAME || ”’)”>’ || i.nm || ‘</a></li>’ else i.nm || ‘</li>’ end;
end if;
end if;
end loop;
for k in 1 .. v_tab(v_tab.count).lvl
loop
t_ret := t_ret || ‘</ul></li>’;
end loop;
v_ret := t_ret ;
return (v_ret);
end;
/
7. Create a method in JAVA data access layer to call the stored function like this:
public String menuString(String logedInUser)
{
String menu = “”, ret;
Statement stmt = null;
conn = DBConnect.getConnection();
CallableStatement cal_stmt = null;
if(conn != null)
{
try{
stmt = conn.createStatement();
cal_stmt = conn.prepareCall(“{? = call tree_menu(?)}”);
cal_stmt.registerOutParameter(1, Types.VARCHAR);
cal_stmt.setString(2, logedInUser);
cal_stmt.execute();
ret = cal_stmt.getString(1);
menu = “<ul id=\”treemenu1\” class=\”treeview\”>” + ret + “</ul>”;
menu += “<script type=\”text/javascript\”>” +
“ddtreemenu.createTree(\”treemenu1\”, true) ” +
“</script>”;
}
catch(Exception e)
{
menu = e.getMessage();
}
finally
{
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
return menu;
}
8. Take a JSP page like this:
<%
response.setHeader(“Cache-Control”,”no-cache”);
response.setHeader(“Pragma”,”no-cache”);
response.setDateHeader (“Expires”, -1);
%>
<%@ page session=”true” language=”java” contentType=”text/html; charset=ISO-8859-1″
pageEncoding=”ISO-8859-1″
%>
<%@ page language=”java” import=”java.util.*” %>
<%@ page import=”dal.*” %>
<%@page import=”dal.dao.*”%>
<%@ page import=”dal.servlet.*” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd”>
<%@page import=”dal.HelperCls”%><html>
<head>
<meta http-equiv=”Content-Type” content=”text/html; charset=ISO-8859-1″>
<title>Wellcome ! </title>
<script type=”text/javascript” src=”simpletreemenu.js”>
</script>
<script type=”text/javascript” src=”call.js”>
</script>
<link rel=”stylesheet” type=”text/css” href=”../css/style.css”>
<link rel=”stylesheet” type=”text/css” href=”simpletree.css” />
<style type=”text/css”>
div.scroll
{
width:80%;
height:780px;
overflow:scroll;
}
p.one
{
width: 300px; height: 100%;
border-style: solid;
border-width: 1px;
border-color: fuchsia;
border-spacing: 2px;
}
</style>
<script type=”text/javascript”>
function logout()
{
document.location.href = “index.jsp”;
}
</script>
</head>
<body >
<table Border=”0″ cellpadding=”0″ cellspacing=”0″ width=”100%” “>
<tr>
<td width=”100%” align=”center”>
<a href=”<%=basePath%>”><div id=”logo”></div></a>
</td>
</tr>
<tr>
<td align=”right”>
<font color=”red”><b>Status :<i><%=dayStatus %>, </i> </b></font>
<font color=”red”><b>Server Date :<i><%=serverCurrentDt %></i> </b></font>
</td>
</tr>
<tr><td><hr style=”height: 5px; color: green” /></td></tr>
<tr>
<td>
<table Border=”0″ cellpadding=”2″ width=”100%” >
<tr>
<td width=”20%” valign=”top” >
<div>
<%
%>
<%= new HelperCls().menuString(loginID) %>
<p align=”center”>
<input type=”image” src=”logout.jpg” alt=”Log Out” onclick=”logout()”/>
<br>
</p>
</div>
<!– <table width=”80%” border=”1″ align=”left” cellspacing=”2″ cellpadding=”2″>
<tr>
<td colspan=”2″ align=”center”>
<font size=”5″ face=”arial” color=”green”>
<b>Login Branch Code</b></font>
</td>
</tr>
<tr>
<td>
Login User :
</td>
<td >
–>
<font size=”3″ face=”arial” color=”green”>
<!– login name –>
</font>
<!–
</td>
</tr>
<tr>
<td>
Login Role :
</td>
<td >
<font size=”3″ face=”arial” color=”green”> –>
<!– </font>
</td>
</tr>
<tr>
<td>
Login ID :
</td>
<td >
<font size=”3″ face=”arial” color=”green”> –>
<!– </font>
</td>
</tr>
</table> –>
</td>
<td id=”innerContent” width=”100%” valign=”top”>
</td>
</tr>
<tr>
<td colspan=”2″ align=”center”>
<div id=”slice-66″>
© DBBL,2010. All right reserved by Dutch-Bangla Bank Limited.
<br>
Website developed by IT Division | http://www.dutchbanglabank.com </div>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
Here, this page will be used as master page and all other pages will be loaded as inner page.
9. We have to use following CSS and Javascript pages:
a) simpletree.css:
.treeview ul{ /*CSS for Simple Tree Menu*/
margin: 0;
padding: 0;
text-align: left;
white-space: nowrap;
margin: 0px 0px 0px 0px;
padding: 0px;
}
.treeview li{ /*Style for LI elements in general (excludes an LI that contains sub lists)*/
list-style-type: none;
padding-left: 22px;
margin-bottom: 3px;
}
.treeview li.submenu{ /* Style for LI that contains sub lists (other ULs). */
background-color:#D6E7F1;
cursor: hand !important;
cursor: pointer !important;
list-style-image: none;
list-style-type: none;
white-space: nowrap;
margin:0px;
padding: 5px 0px 7px 8px;
width:179px;
font-weight: bold;
border-bottom:1px solid #a1a3a5;
border-left:1px solid #a1a3a5;
border-right:1px solid #a1a3a5;
}
.treeview li.submenu ul{ /*Style for ULs that are children of LIs (submenu) */
display: none; /*Hide them by default. Don’t delete. */
cursor: hand !important;
cursor: pointer !important;
}
.treeview .submenu ul li{ /*Style for LIs of ULs that are children of LIs (submenu) */
cursor: default;
cursor: hand !important;
cursor: pointer !important;
}
/******************** Sub Menu ****************/
.treeview.ul.submenu {
text-align: left;
white-space: nowrap;
margin: 0px 0px 0px 0px;
padding: 5px;
}
.treeview.ul.submenu li {
list-style-image: none;
list-style-type: none;
white-space: nowrap;
margin:0px;
padding: 5px 0px 7px 8px;
width:179px;
font-weight: bold;
border-top:1px solid #a1a3a5;
}
.treeview.ul.submenu li.selected a{color:#0E2199;}
.treeview.ul.submenu li.selected {
border: 1px solid #a3a4a6;
background-color: #ffffff;
}
.treeview.ul.submenu li a {
display: block;
width: 100%;
color: #444;
font-weight: bold;
font-size:13px;
text-decoration: none;
}
.treeview.ul.submenu li a:hover {
text-decoration:none;
color:#0E2199;
}
b) simpletreemenu.js:
var persisteduls=new Object()
var ddtreemenu=new Object()
ddtreemenu.closefolder=”” //set image path to “closed” folder image
ddtreemenu.openfolder=”” //set image path to “open” folder image
//////////No need to edit beyond here///////////////////////////
ddtreemenu.createTree=function(treeid, enablepersist, persistdays){
var ultags=document.getElementById(treeid).getElementsByTagName(“ul”)
if (typeof persisteduls[treeid]==”undefined”)
persisteduls[treeid]=(enablepersist==true && ddtreemenu.getCookie(treeid)!=””)? ddtreemenu.getCookie(treeid).split(“,”) : “”
for (var i=0; i<ultags.length; i++)
ddtreemenu.buildSubTree(treeid, ultags[i], i)
if (enablepersist==true){ //if enable persist feature
var durationdays=(typeof persistdays==”undefined”)? 1 : parseInt(persistdays)
ddtreemenu.dotask(window, function(){ddtreemenu.rememberstate(treeid, durationdays)}, “unload”) //save opened UL indexes on body unload
}
}
ddtreemenu.buildSubTree=function(treeid, ulelement, index){
ulelement.parentNode.className=”submenu”
if (typeof persisteduls[treeid]==”object”){ //if cookie exists (persisteduls[treeid] is an array versus “” string)
if (ddtreemenu.searcharray(persisteduls[treeid], index)){
ulelement.setAttribute(“rel”, “open”)
ulelement.style.display=”block”
ulelement.parentNode.style.backgroundImage=”url(“+ddtreemenu.openfolder+”)”
}
else
ulelement.setAttribute(“rel”, “closed”)
} //end cookie persist code
else if (ulelement.getAttribute(“rel”)==null || ulelement.getAttribute(“rel”)==false) //if no cookie and UL has NO rel attribute explicted added by user
ulelement.setAttribute(“rel”, “closed”)
else if (ulelement.getAttribute(“rel”)==”open”) //else if no cookie and this UL has an explicit rel value of “open”
ddtreemenu.expandSubTree(treeid, ulelement) //expand this UL plus all parent ULs (so the most inner UL is revealed!)
ulelement.parentNode.onclick=function(e){
var submenu=this.getElementsByTagName(“ul”)[0]
if (submenu.getAttribute(“rel”)==”closed”){
submenu.style.display=”block”
submenu.setAttribute(“rel”, “open”)
ulelement.parentNode.style.backgroundImage=”url(“+ddtreemenu.openfolder+”)”
}
else if (submenu.getAttribute(“rel”)==”open”){
submenu.style.display=”none”
submenu.setAttribute(“rel”, “closed”)
ulelement.parentNode.style.backgroundImage=”url(“+ddtreemenu.closefolder+”)”
}
ddtreemenu.preventpropagate(e)
}
ulelement.onclick=function(e){
ddtreemenu.preventpropagate(e)
}
}
ddtreemenu.expandSubTree=function(treeid, ulelement){ //expand a UL element and any of its parent ULs
var rootnode=document.getElementById(treeid)
var currentnode=ulelement
currentnode.style.display=”block”
currentnode.parentNode.style.backgroundImage=”url(“+ddtreemenu.openfolder+”)”
while (currentnode!=rootnode){
if (currentnode.tagName==”UL”){ //if parent node is a UL, expand it too
currentnode.style.display=”block”
currentnode.setAttribute(“rel”, “open”) //indicate it’s open
currentnode.parentNode.style.backgroundImage=”url(“+ddtreemenu.openfolder+”)”
}
currentnode=currentnode.parentNode
}
}
ddtreemenu.flatten=function(treeid, action){ //expand or contract all UL elements
var ultags=document.getElementById(treeid).getElementsByTagName(“ul”)
for (var i=0; i<ultags.length; i++){
ultags[i].style.display=(action==”expand”)? “block” : “none”
var relvalue=(action==”expand”)? “open” : “closed”
ultags[i].setAttribute(“rel”, relvalue)
ultags[i].parentNode.style.backgroundImage=(action==”expand”)? “url(“+ddtreemenu.openfolder+”)” : “url(“+ddtreemenu.closefolder+”)”
}
}
ddtreemenu.rememberstate=function(treeid, durationdays){ //store index of opened ULs relative to other ULs in Tree into cookie
var ultags=document.getElementById(treeid).getElementsByTagName(“ul”)
var openuls=new Array()
for (var i=0; i<ultags.length; i++){
if (ultags[i].getAttribute(“rel”)==”open”)
openuls[openuls.length]=i //save the index of the opened UL (relative to the entire list of ULs) as an array element
}
if (openuls.length==0) //if there are no opened ULs to save/persist
openuls[0]=”none open” //set array value to string to simply indicate all ULs should persist with state being closed
ddtreemenu.setCookie(treeid, openuls.join(“,”), durationdays) //populate cookie with value treeid=1,2,3 etc (where 1,2… are the indexes of the opened ULs)
}
////A few utility functions below//////////////////////
ddtreemenu.getCookie=function(Name){ //get cookie value
var re=new RegExp(Name+”=[^;]+”, “i”); //construct RE to search for target name/value pair
if (document.cookie.match(re)) //if cookie found
return document.cookie.match(re)[0].split(“=”)[1] //return its value
return “”
}
ddtreemenu.setCookie=function(name, value, days){ //set cookei value
var expireDate = new Date()
//set “expstring” to either future or past date, to set or delete cookie, respectively
var expstring=expireDate.setDate(expireDate.getDate()+parseInt(days))
document.cookie = name+”=”+value+”; expires=”+expireDate.toGMTString()+”; path=/”;
}
ddtreemenu.searcharray=function(thearray, value){ //searches an array for the entered value. If found, delete value from array
var isfound=false
for (var i=0; i<thearray.length; i++){
if (thearray[i]==value){
isfound=true
thearray.shift() //delete this element from array for efficiency sake
break
}
}
return isfound
}
ddtreemenu.preventpropagate=function(e){ //prevent action from bubbling upwards
if (typeof e!=”undefined”)
e.stopPropagation()
else
event.cancelBubble=true
}
ddtreemenu.dotask=function(target, functionref, tasktype){ //assign a function to execute to an event handler (ie: onunload)
var tasktype=(window.addEventListener)? tasktype : “on”+tasktype
if (target.addEventListener)
target.addEventListener(tasktype, functionref, false)
else if (target.attachEvent)
target.attachEvent(tasktype, functionref)
}
Enjoy your database driven menu.