Welcoming Devin To The Team

A couple of weeks ago Devin mentioned on his blog that he was changing careers and joining myself, John Roling, Mike McGarel and David Leedy here at Czarnowski and earlier this week I had the great pleasure of creating his account on our systems so that we are all ready for him.

Devin is going to make a great addition to the team and we are all looking forward to having him onboard. He brings some great skills to the table that will benefit both the company and the rest of the IT team. I also suspect that Devin will be bringing some of his famous home-brewed beers to the table when we get a chance to meet up. Maybe he’ll do a special CzarBrew to celebrate.

Welcome Devin…

—–

BTW, if you have never heard of FirM, which is what I used to create Devin’s Notes ID in the screenshot above, you really should check it out at the next user group or Lotusphere that you attend or just go visit the HADSL website and check it out. It really makes user management simple, I just had to select a profile, type in Devin’s name and the system did the rest, created the notes ID, created the mailfile, added him to all the relevant groups based on the profile I had selected etc. It really does save a lot of time and it makes sure that all users are created the same each and every time.

Tagged with:
Posted in None

Creating a simple Organizational Chart in XPages

A couple of days ago Jesse Gallagher posted a great entry on how to create a basic organizational chart using the XPages Extension Library navigator control in conjunction with a BeanTreeNode that generates the hierarchy. Truth be told, Jesse actually wrote this code based on the frustration that I had with trying to wrap my head around recursion to do the exact same thing.

While investigating different ways to build an organizational chart I also come across a nice little library from Google called Google Chart Tools which takes in a fairly flat data table and generates from it a chart that looks similar to the following..

Adding the chart to your XPage is very easy, first you need to add a new external JavaScript resource

<xp:this.resources>
	<xp:script src="https://www.google.com/jsapi" clientSide="true">
	</xp:script>
</xp:this.resources>

Then add a script block to the page that loads the orgchart visualization and, once loaded, calls a function to draw the chart into a div called chart_div.

<xp:scriptBlock id="scriptBlock1">
		<xp:this.value><![CDATA[google.load('visualization', '1', {packages:['orgchart']});
google.setOnLoadCallback(drawChart);

function drawChart() {
	var jsonData = dojo.xhrGet({
		url: "./orgChart.xsp/getOrgData",
		handleAs:"json",
		load: function(data){
        	var chartdata = new google.visualization.DataTable(data);
        	var chart = new google.visualization.OrgChart(document.getElementById('chart_div'));
        	chart.draw(chartdata, {allowHtml:true, allowCollapse:true});
    	}
		});
     }]]></xp:this.value>
	</xp:scriptBlock>

While you could directly add the data for the orgchart into the drawChart function I wanted to use a view in a database for the data so I’m loading my data using the Extension Library custom REST service. My REST service returns a JSON data construct that defines the information for the organization chart. The REST service is on the same page is the OrgChart and looks like this. It is a bit ugly as the way the data is defined means you needs arrays of objects inside arrays of other objects inside another array of objects. I am also manually defining the top of the orgChart by creating a single node with no manager.

	<xe:restService id="restService1" pathInfo="getOrgData">
		<xe:this.service>
			<xe:customRestService contentType="application/json">
				<xe:this.doGet><![CDATA[#{javascript:// Columns Array
var columns = new Array();

var col1 = new Object();
col1.type = "string";
col1.id = "";
col1.label = "Name";
columns.push(col1);

var col2 = new Object();
col2.type = "string";
col2.id = "";
col2.label = "Manager";
columns.push(col2);

var col3 = new Object();
col3.type = "string";
col3.id = "";
col3.label = "ToolTip";
columns.push(col3);

// Build Rows Array
var rows = new Array();

// Add TOP
var topRowName = new Object();
topRowName.v = "Mike";
var topRowMgr = new Object();
topRowMgr.v = "";
var topRowTip = new Object();
topRowTip.v = "President";

var topRowArray = new Array();
topRowArray.push(topRowName);
topRowArray.push(topRowMgr);
topRowArray.push(topRowTip);

var topRowObject = new Object();
topRowObject.c = topRowArray;
rows.push(topRowObject);

// Add employees
var empDB:NotesDatabase = session.getDatabase(database.getServer(),"employees.nsf",false);
var empView:NotesView = empDB.getView("lkp_byManager");
var empDoc:NotesDocument = empView.getFirstDocument();
while (empDoc != null){

	var thisRowName = new Object();
	var empName:NotesName = session.createName(empDoc.getItemValueString("FullName"));
	thisRowName.v = empName.getCommon();

	var thisRowMgr = new Object();
	var mgrName:NotesName = session.createName(empDoc.getItemValueString("Manager"));
	thisRowMgr.v = mgrName.getCommon();

	var thisRowTip = new Object();
	thisRowTip.v = empDoc.getItemValueString("JobTitle");

	var thisRowArray = new Array();
	thisRowArray.push(thisRowName);
	thisRowArray.push(thisRowMgr);
	thisRowArray.push(thisRowTip);

	var thisRowObject = new Object();
	thisRowObject.c = thisRowArray;
	rows.push(thisRowObject);

	empDoc = empView.getNextDocument(empDoc);
}

// Build final array
var alldata = new Object();
alldata.cols = columns;
alldata.rows = rows;

// pass it back as JSON
return toJson(alldata);}]]></xe:this.doGet>
			</xe:customRestService>
		</xe:this.service>
	</xe:restService>

The view that I’m looking up has been setup to only display people who have their manager field filled in. Google Chart Tools OrgChart will do some weird stuff if your data is bad, like if a person has a manager who doesn’t exist as an employee then a second org chart will appear to right of the main chart with the employees name linked to the non-existant managers name.

I also found that the chart got very wide when fed in a lot of data so this org chart is really only suited for a small company or on a department by department basis.

While this is a great example of how to use Google Chart Tools to create an OrgChart there are lots of other chart types available in the library and using the same technique to load the data via a JSON REST service using the ExtLib you can easily add charting to any XPage application.

Tagged with: , , , ,
Posted in None

Generating a CSV file from XPages

After spending the day figuring out how to generate an Excel file using the Apache POI library I was informed that I actually needed to generate a comma separated format file ( .csv ). Not wanting to waste any of the code or logic that I had written to build the Excel file I looked for a CSV library and I found the apache.commons.csv library.

This library is very simple to implement, once the jar file is added to your XPage application you just need to create a CSVPrinter object and then add all the values that you need to it. The quickest way to do this is create an xAgent style XPage with the code to create the CSV file in the beforePageLoads event.

Here is a simple example of the code required that will loop thru all the documents in a view and export a couple of fields to the CSV file and then send it all to the browser. Note this is just an example, production code would be full of error trapping etc.

// Load the java packages
importPackage(java.io);
importPackage(org.apache.commons.csv);

// generate CSVPrinter Object
var csvBAOS:ByteArrayOutputStream = new ByteArrayOutputStream();
var csvWriter:OutputStreamWriter = new OutputStreamWriter(csvBAOS);
var csvPrinter:CSVPrinter = new CSVPrinter(csvWriter,CSVFormat.DEFAULT);

// Add the header row
csvPrinter.print("EmployeeID");
csvPrinter.print("Lastname");
csvPrinter.print("Firstname");
csvPrinter.println();

// add each exported document
var exportView:NotesView = database.getView("vw_export");

if (exportView.getEntryCount() != 0){
	var exportDoc:NotesDocument = exportView.getFirstDocument();

	while (exportDoc != null){
		var nextExportDoc:NotesDocument = exportView.getNextDocument(exportDoc);

		csvPrinter.print(exportDoc.getItemValueString("employeeID"));
		csvPrinter.print(exportDoc.getItemValueString("lastName"));
		csvPrinter.print(exportDoc.getItemValueString("firstName"));
		csvPrinter.println();

		exportDoc.recycle();
		var exportDoc:NotesDocument = nextExportDoc;
	}
}

csvPrinter.flush();

// Open the servlet Response
var con = facesContext.getExternalContext();
var response:com.ibm.xsp.webapp.XspHttpServletResponse = con.getResponse();

//setting response headers for browser to recognize data
response.setContentType("text/csv");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", -1);
response.setContentLength(csvBAOS.size());
response.setHeader( "Content-Disposition", "attachment; filename="export.csv"" );

// get a handle on the actual output stream and write the Workbook
var servletOutputStream = response.getOutputStream();
csvBAOS.writeTo(servletOutputStream);
servletOutputStream.flush();
servletOutputStream.close();

// Close the renderer and return the document to the browser
facesContext.responseComplete();
}

Rather then writing directly to the response OutputStream I setup a ByteArrayOutputStream in memory and then, because the CSVPrinter requires an appendable outputstream I create an OutputStreamWriter from it and then setup the CSVPrinter to write to that.

Printing a value is as simple as using the CSVPrinter.print(string) method, each time you call this it adds a new value. When you get to the end of the line you need to use a CSVPrinter.println() to add the newline character and then you finish the file it is a good idea to call the CSVPrinter.flush() method to make sure everything is flushed out into the OutputStreamWriter. Then I send the ByteArrayOutputStream to the response OutputStream which is what sends it down to the browser to be saved.

Very easy to use and another great example of how you can use a prebuilt library to extend the functionality of XPages.

Tagged with:
Posted in None

Some notes for the XPage Controls developers

Ok, so I pointed out a couple of things to the developers full applications based on the entries to the OpenNTF Development Contest that I judged, now it is time for the developers of the controls.

Make use of the OpenNTF Import/Export Plugin.
There is a great project on OpenNTF that allows you to generate a zip file containing your nsf/ntf along with the license files and a special importlist.xml file that specifies exactly which design elements make up your control. Once this is uploaded to OpenNTF other users can use the same plugin to copy those exact design elements to their application, this will make your control much more reusable.

Create A NameSpace for your design elements
Decided on a prefix for all your design elements that make up the control so that when they are copied into another application there is a much lower chance that they will clash with any other existing design elements. For example I might name a graphic as qtzar_alerts_accept.png rather then accept.png so I know I don’t have to worry about another control that also uses accept.png. The developer using your control will thank you as they won’t have to make any changes to the code to avoid a clash.

Is your control more suited for an Extension Library
If you have developed a fairly complex control that has lots of options and external graphic resources etc. then you should take a look at the Starter XSP Kit project on OpenNTF and learn how to convert your control into an Extension Library. Now that OSGi plugins can be easily deployed to the server via an UpdateSite NSF the admin has no excuse for not deploying the library and by putting your control in a library you make it easier for the developer who doesn’t have to worry about copying in all the other required design elements.

Don’t require server file system changes
A couple of the controls that I judged required that I make changes to the file system on the Domino server, This included changing the java security policy file and in some cases also required adding additional file to the CKEditor system. As an admin I’m going to need a very good reason to change/add files at the file system level and mainly because if I do a server upgrade some of these files may be deleted or reverted back making my upgrade process longer then 5 minutes. For a single control this reduces the reusability significantly.

I’m sure there are a few more, if you have suggestions then please feel free to leave them in the comments.

Tagged with:
Posted in None
Archives