Complete frontend extension sample

This sample shows how to quickly extend OpenKM features with JSP, HTML, CSS and JavaScript technologies using OpenKM Java API and how to use JavaScript functions of the exposed frontend JAVA methods.

Download the extension sample code complete_extension_sample.zip.

Description

The sample is shown into a new Workspace tab.

The main screen is shown as a tool bar with tree buttons:

  • The first button goes to main screen of the sample.
  • The second button show sample 1
  • The second button show sample 2.

Project structure:

Installation

  • Stop the application.
  • Open the content of the download file into $TOMCAT_HOME/webapps/OpenKM folder ( or better into OpenKM.war )
  • Delete $TOMCAT_HOME/work/Catalina/localhost
  • Start the application.
  • Check the url http://localhost:8080/OpenKM/sample.

The screen shots has been done in combination with "ExtraTab" frontend extension feature, also could be used TabWorkspaceExtension.

When you change the contents of the OpenKM.war the file is deployed as a new version of the application and all the contents into $TOMCAT_HOME/webapps/OpenKM will be refreshed and you will lost any change you made directly in the webapps/OpenKM folder.

Implementation details

index.jsp

Index.jsp control the iframe height with a JavaScript code.

Other important fact is, for example after uploading a document ( Sample 2 ), is used the parameter urlToOpen to indicate which url must be opened.

String urlToOpen = URLDecoder.decode(WebUtils.getString(request, "urlToOpen"),"UTF-8");
if (urlToOpen.equals("")) {
  urlToOpen = "home.jsp";
}

Has the menu url definitions.  

home.jsp

Set the main home screen.

Sample 1

Is composed by 4 files:

  • action.jsp ( the main controller where is set the logic ).
  • distributor.jsp ( the main layer with two columns at 50% ).
  • full_list.jsp ( show full list with no JavaScript special control ).
  • filtered_list.jsp ( show filtered list with no JavaScript special control ).

action.jsp

The main controller.

<%@page import="java.util.Map"%>
<%@page import="java.util.HashMap"%>
<%@page import="java.util.List"%>
<%@page import="java.util.ArrayList"%>
<%@ page import="java.text.SimpleDateFormat" %>
<%@ page import="java.io.IOException" %>
<%@ page import="org.slf4j.Logger" %>
<%@ page import="org.slf4j.LoggerFactory" %>
<%@ page import="com.openkm.bean.Document" %>
<%@ page import="com.openkm.util.WebUtils" %>
<%@ page import="com.openkm.api.OKMDocument" %>
<%@ page extends="com.openkm.extension.servlet.BaseServlet" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%!
private static Logger log = LoggerFactory.getLogger("com.openkm.sample");
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException,
		ServletException {
	String action = WebUtils.getString(request, "action");		
	try {
		list(request, response);
	} catch (Exception e) {
		log.error(e.getMessage(), e);
		sendErrorRedirect(request, response, e);
	}
}
 
// Used by fileupload return and filter
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException,
ServletException {
	String action = WebUtils.getString(request, "action");
	try {
		list(request, response);
	} catch (Exception e) {
		log.error(e.getMessage(), e);
		sendErrorRedirect(request, response, e);
	}
}
 
private void list(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, Exception {
	SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
	String docPath = (String) request.getAttribute("docPath");
	String nameFilter = WebUtils.getString(request,"nameFilter");
 
	// Pending docs
	List<Map<String,String>> pendingDocMaps = new ArrayList<Map<String,String>>();
	for ( Document doc : OKMDocument.getInstance().getChildren(null, "/okm:root")) {
		Map<String,String> docMap = new HashMap<String,String>();
		docMap.put("name",doc.getPath().substring(doc.getPath().lastIndexOf("/")+1));
		docMap.put("uuid",doc.getUuid());
		docMap.put("path",doc.getPath());
		docMap.put("mimeType",doc.getMimeType());
		docMap.put("lastModified", df.format(doc.getLastModified().getTime()));
		pendingDocMaps.add(docMap);
	}
 
	ServletContext sc = getServletContext();
	sc.setAttribute("docPath",docPath);
	sc.setAttribute("nameFilter",nameFilter);
	sc.setAttribute("pendingDocuments",pendingDocMaps);
	sc.getRequestDispatcher("/sample/sample2/distributor.jsp").forward(request, response);
}%>

distributor.jsp

The main layer.

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <link rel="stylesheet" type="text/css" href="<%=request.getContextPath() %>/sample/css/style.css" />
  <script type="text/javascript" src="<%=request.getContextPath() %>/js/jquery-1.7.1.min.js"></script>
  <script type="text/javascript" src="<%=request.getContextPath() %>/js/vanadium-min.js" ></script>
</head>
<body>
	<table width="100%">
		<tbody>
		<tr>
			<td valign="top" width="50%">
				<jsp:include page="/sample/sample1/full_list.jsp"></jsp:include>
			</td>
			<td valign="top" width="10"></td>
			<td valign="top" width="50%">
				<jsp:include page="/sample/sample1/filtered_list.jsp"></jsp:include>
			</td>
		</tr>
		</tbody>
	</table>
</body>
</html>

full_list.jsp

Show document list.

Pay attention at JavaScript function "go", which call parent.parent.jsOpenPathByUuid(uuid). The "jsOpenPathByUuid" is a GWT public function used to jump to some specific document, folder, etc. Here Exposed frontend JavaScript API there's a complete list of  JavaScript functions.

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<script type="text/javascript">
	// Note the parent.parent call because iframe is into other iframe
    function go(uuid) {
        alert('Document UUID:'+uuid);
    	parent.parent.jsOpenPathByUuid(uuid);
    }
</script>
<h1>Full list of /okm:root documents</h1>
<table class="results" style="white-space: nowrap;" cellpadding="3" width="100%">
    <thead>
      <tr>
        <th></th><th>Author</th><th>Document</th><th>Date</th><th></th>
      </tr>
    </thead>
    <tbody>
    	<c:forEach var="document" items="${leftDocsMaps}" varStatus="row">
    	<tr>
    		<td valign="center"><img src="<%=request.getContextPath() %>/mime/${document.mimeType}"/></td>
    		<td>${document.author}</td>
    		<td>${document.name}</td>
    		<td>${document.lastModified}</td>
    		<td align="center">
    		 <a href="javascript:go('${document.uuid}')"><img src="<%=request.getContextPath() %>/sample/img/action/goto_document.gif" alt="Show document" title="Show document"/></a>
    		 </td>
    	</tr>
    	</c:forEach>
    </tbody>
</table>

Screenshot of sample1 view

 

Sample 2

Is composed by 4 files:

  • action.jsp ( the main controller where is set the logic ).
  • add_form.jsp ( form to upload a new document )
  • distributor.jsp ( the main layer with two columns at 50% with JavaScript ).
  • full_list.jsp ( show full list with JavaScript special control for table height).
  • pending.jsp ( static content ).

action.jsp

The main controller.

<%@page import="java.util.Map"%>
<%@page import="java.util.HashMap"%>
<%@page import="java.util.List"%>
<%@page import="java.util.ArrayList"%>
<%@ page import="java.text.SimpleDateFormat" %>
<%@ page import="java.io.IOException" %>
<%@ page import="org.slf4j.Logger" %>
<%@ page import="org.slf4j.LoggerFactory" %>
<%@ page import="com.openkm.bean.Document" %>
<%@ page import="com.openkm.util.WebUtils" %>
<%@ page import="com.openkm.api.OKMDocument" %>
<%@ page extends="com.openkm.extension.servlet.BaseServlet" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%!
 
private static Logger log = LoggerFactory.getLogger("com.openkm.sample");
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException,
		ServletException {
	String action = WebUtils.getString(request, "action");		
	try {
		list(request, response);
	} catch (Exception e) {
		log.error(e.getMessage(), e);
		sendErrorRedirect(request, response, e);
	}
}
 
// Used by fileupload return and filter
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException,
ServletException {
	String action = WebUtils.getString(request, "action");
	try {
		list(request, response);
	} catch (Exception e) {
		log.error(e.getMessage(), e);
		sendErrorRedirect(request, response, e);
	}
}
 
private void list(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, Exception {
	SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
	String docPath = (String) request.getAttribute("docPath");
	String nameFilter = WebUtils.getString(request,"nameFilter");
	// Pending docs
	List<Map<String,String>> pendingDocMaps = new ArrayList<Map<String,String>>();
	for ( Document doc : OKMDocument.getInstance().getChildren(null, "/okm:root")) {
		Map<String,String> docMap = new HashMap<String,String>();
		docMap.put("name",doc.getPath().substring(doc.getPath().lastIndexOf("/")+1));
		docMap.put("uuid",doc.getUuid());
		docMap.put("path",doc.getPath());
		docMap.put("mimeType",doc.getMimeType());
		docMap.put("lastModified", df.format(doc.getLastModified().getTime()));
		pendingDocMaps.add(docMap);
	}
	ServletContext sc = getServletContext();
	sc.setAttribute("docPath",docPath);
	sc.setAttribute("nameFilter",nameFilter);
	sc.setAttribute("pendingDocuments",pendingDocMaps);
	sc.getRequestDispatcher("/sample/sample2/distributor.jsp").forward(request, response);
}%>

distributor.jsp

The main layer.

Here JavaScript is used to control the height and width of the layer.

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <link rel="stylesheet" type="text/css" href="<%=request.getContextPath() %>/sample/css/style.css" />
  <link rel="stylesheet" type="text/css" href="<%=request.getContextPath() %>/css/jquery.tablescroll.css"/>
  <script type="text/javascript" src="<%=request.getContextPath() %>/js/jquery-1.7.1.min.js"></script>
  <script type="text/javascript" src="<%=request.getContextPath() %>/js/vanadium-min.js" ></script>
  <script type="text/javascript" src="<%=request.getContextPath() %>/js/jquery.tablescroll.js"></script>
</head>
<body>
	<div id="leftPanel" style="float: left;">
		<jsp:include page="/sample/sample2/add_form.jsp"></jsp:include>
		<jsp:include page="/sample/sample2/full_list.jsp"></jsp:include>
	</div>
	<div id="centerPanel" style="float: left; width: 12px;">
		<div id="verticalSeparator" class="verticalSeparator"></div>
	</div>
	<div id="rightPanel" style="float: left;">
        <jsp:include page="/sample/sample2/pending.jsp"></jsp:include>
	</div>
 
	<script type="text/javascript">
		var width =  $(window).width()-12; // Deleting middle panel
		var height = $(window).height();
 
		// Setting height
		$('#verticalSeparator').height(height);
		$('#leftPanel').height(height);
		$('#rightPanel').height(height);
		// Setting width
		$('#leftPanel').width(parseInt(width/2));
		$('#centerPanel').width(12);
		$('#rightPanel').width((width-parseInt((width/2))));
	</script>
</body>
</html>

add_form.jsp

Form to upload a new document.

<%@ page import="com.openkm.frontend.client.constants.ui.UIFileUploadConstants" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<h2 id="headerTitle_new_register">Upload new file</h2>
<form enctype="multipart/form-data" method="post" action="<%=request.getContextPath() %>/frontend/FileUpload">
	<input type="hidden" name="path" value="/okm:root">
	<input type="hidden" name="action" value="<%=UIFileUploadConstants.ACTION_INSERT%>">
	<input type="hidden" name="message" value="">
	<input type="hidden" name="comment" value="">
	<input type="hidden" name="redirect" value="/sample/sample2/action.jsp">
	<table class="form">
    <tbody>
    	<c:if test="${docPath != null && docPath != ''}">
    	<tr>
    		<td><div class="ok">Document sended</div></td>
    	</tr>
		</c:if>
    	<tr>
    		<td>
    			<div id="file_error" style="display:none; color : red;">File is mandatory.</div>
    			<input type="file" name="file" class=":required;;file_error :only_on_blur">
    		</td>
    	</tr>
    	<tr>
      		<td align="center"><input type="submit" value="Send"></td>
    	</tr>
  	</tbody>
  	</table>
</form>

full_list.jsp

Show a list of documents.

Take a look at the ends where the JavaScript is used to control the scroll of the table.

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<script type="text/javascript">
	// Note the parent.parent call because iframe is into other iframe
    function go(uuid) {
        alert('Document UUID:'+uuid);
    	parent.parent.jsOpenPathByUuid(uuid);
    }
</script>
<h2 class="top">Full document list from /okm:root</h2>
<div align="center" style="padding: 0px 20px 0px 5px">
	<div class="tablescroll">
		<table id="docsPendingTable" width="100%" cellspacing="0" style="white-space: nowrap;">
		    <thead>
		      <tr>
		        <td>Id</td><td>Document</td><td>Date</td><td></td>
		      </tr>
		    </thead>
		    <tbody>
		    	<c:set var="first" value="true"></c:set>
		    	<c:forEach var="document" items="${pendingDocuments}" varStatus="row">
		    		<c:choose>
			    	 	<c:when test="${first==true}">
			    	 		<tr class="first">
			    	 	</c:when>
			    	 	<c:otherwise>
			    	 		<tr>
			    	 	</c:otherwise>
			    	</c:choose>
		    			<td valign="center"><img src="<%=request.getContextPath() %>/mime/${document.mimeType}"/></td>
		    			<td>${document.name}</td>
		    			<td>${document.lastModified}</td>
		    			<td align="center"><a href="javascript:go('${document.uuid}')"><img src="<%=request.getContextPath() %>/sample/img/action/goto_document.gif" alt="Show document" title="Show document"/></a></td>
		    		</tr>
		    		<c:set var="first" value="false"></c:set>
		    	</c:forEach>
		    </tbody>
		</table>
	</div>
</div>
 
<script type="text/javascript">
//Calculating table height removing from total other elements height
var pendingTableHeight = $(window).height() - (parseInt($('#docsPendingTable').offset().top) + 35);
/*<![CDATA[*/
jQuery(document).ready(function($)
{
	$('#docsPendingTable').tableScroll({height:pendingTableHeight});
});
/*]]>*/
</script>

Screenshot of sample2 view