Tag cloud with data from another application

For a project I needed a tag cloud to provide some alternative navigation to a view. Easy I thought; such control is part of the XPages extension library. However it turns out that this control does not support to use a view from another application/database as the data-source.

Some examples I found (1), (2), (3) required some work anyway so I wrote a quick prototype based upon the tag cloud component described in the OneUI documentation. The tag cloud is really simple but for now it does the job (as it appears). Here is the code.

<?xml version=”1.0″ encoding=”UTF-8″?>
<xp:view xmlns:xp=”http://www.ibm.com/xsp/core”&gt;
<xp:panel tagName=”div” styleClass=”lotusTagCloud lotusChunk”
style=” overflow: hidden”>
<xp:text escape=”false” id=”txCloud”>
<xp:this.value><![CDATA[#{javascript:importPackage(java.util);

var viewPrefs: NotesView = database.getView(“$v-preferences”);
var docPrefs: NotesDocument = viewPrefs.getFirstDocument();
var serv = docPrefs.getItemValueString(“tx_server”);
var faqdb = docPrefs.getItemValueString(“tx_loc_faq”);

var db:NotesDatabase = session.getDatabase(serv,faqdb);
var vw:NotesView = db.getView(compositeData.vwSource);

var coll:NotesDocumentCollection = vw.getAllEntries();
var totalNum:Integer = coll.getCount();
var myNum:Integer;

var result:String = “”;

var ref = compositeData.ref;

var nav:NotesViewNavigator = vw.createViewNav();
var ve:NotesViewEntry = nav.getFirst();

//some calculation first…
var high:Integer = 0;
var low:Integer = 1000;
var max:Integer = 5;
while (ve != null) {

if (ve.isCategory()){
if (ve.getChildCount() > high){
high = ve.getChildCount()
}
if (ve.getChildCount() < low){
low = ve.getChildCount();
}
}

// Get the next entry and recycle the current one
var tmpentry = nav.getNext();
ve.recycle();
ve = tmpentry;
}

var range:Integer = high-low;
var factor:Integer = max*low;
var ceil:Integer = max-1;

//create cloud…
var nav:NotesViewNavigator = vw.createViewNav();
var ve:NotesViewEntry = nav.getFirst();

while (ve != null) {

if (ve.isCategory()){
cat=ve.getColumnValues().elementAt(0);
myNum=ve.getChildCount();
var myScore = Math.round(((ceil / range) * myNum) + (high – factor) / range);
result+=”<li><a class=’lotusF” + myScore + “‘ href='” + ref + cat + “‘>” + cat + “[” + myNum + “]</a></li>”
}

// Get the next entry and recycle the current one
var tmpentry = nav.getNext();
ve.recycle();
ve = tmpentry;
}

return “<ul>” + result + “</ul>”;}]]></xp:this.value>
<xp:this.attrs>
<xp:attr name=”role” value=”navigation”></xp:attr>
</xp:this.attrs>
</xp:text>
</xp:panel>
</xp:view>

Code walkthrough

The code resides in a custom control which uses 2 properties; one for the view to use as data-source and the link to use to redirect to. The links will direct to a page with a view that will be filtered by the ‘selected text’.

The code resides in an application that reads data from another NSF that does not contain any XPages code yet. In the project will deliver a new web interface for an existing Domino application and I wanted to avoid code collision. The ultimate goal would be to remove all the “old-spice” Domino code one day. So for now I have a preferences document where I store the location of the data-source application.

I go through the data-source view twice :-? since the application does not have so many documents this is (still) acceptable. The values for the tags are calculated and at the end we write the values back to the screen. The CSS in the OneUI does the nice styling job.

Screenshot_2

Go Connect Go!

With IBMConnect about to start I updated my collection of blog posts about the XPages technology. I started collecting in 2009, directly after Lotusphere where it was one of the highlights at the show. I remember that the blogosphere exploded after/during Declan Sciolla-Lynch‘s tutorial.

The earliest post about XPages I have collected is  HND 102: IBM Lotus Domino Designer in Eclipse: Create and Work with XPages Hands On (January 2008). The latest post that I added yesterday is ‘Changing the Output Tag on a Computed Field in XPages’ by Brad Balassaitis.

The counter for my XPages db is at 1993 entries at the moment. I am 100% sure it will the ‘magical’ 2000 number during IBMConnect 2014. Therefor I cannot wait for the event to kick off!!

xpagesKC

Hmm reminds me I should update the following tribute music-clip: Teachers.

Using Bootstrap in XPages – The ultimate incomplete guide

This document describes how to integrate the popular front-end framework Twitter Bootstrap with XPages. The purpose is to provide developers an introduction to use Twitter Bootstrap in XPages development projects by building an actual application.

Below you can find a document/manual I started to write a while ago but I never finished (I guess there is no finish since new boundaries are explored every day). At least the document should give you a head start with using ‘raw’ (Twitter) Bootstrap in XPages. I also recommend you to take a look at the Bootstrap4XPages project on OpenNTF since this project offers lots of ‘out of the box’ conversions of components from the Extension Library (e.g. data view control).

Bootstrap in XPages

Enjoy reading!

eCard XPages app – Time for your seasonal greetings !

The last days I have spent some hours in the evenings to learn me Bootstrap in combination with XPages as bit more. The result is a basic eCard application on XPages which utilizes Bootstrap 2 for UI.

Since the Advent Calendar has started I guess you are allowed to start sending out your seasonal greetings :-)

create

You can find the app as a project on OpenNTF (link to project). I noticed a card is not working properly in IE yet (what did I expect?). I will see if I can fix this on the short term. Of course you may contribute to the fix.

Here you can find some inspiration for your messages: http://www.brainyquote.com/quotes/topics/topic_christmas.html

Capturing geodata in XPages

In XPages it is not default possible to capture geodata information with any control so you are depending on services (not a problem if they provide the correct data).

Here is an example how to capture a visitor’s geodata via http://ipinfo.io/ and store the information in a Notes document in the following order:

  • Run client side JavaScript  (jQuery get method) and load data from an external server.
  • Post the data via client side JavaScript (jQuery post method) to an XPage.
  • Process the data and store it in a Notes document.

Place the following code on an XPage:

<script language=”javascript” type=”text/javascript” src=”jquery.js”></script>
<xp:scriptBlock id=”scriptBlock1″>
<xp:this.value>
<![CDATA[$(document).ready(
$.get(‘http://ipinfo.io&#8217;, function (response) {
$(‘#ip’).html(‘IP: ‘ + response.ip);
$(‘#address’).html(‘Location: ‘ + response.city + ‘, ‘ + response.region);
$(‘#details’).html(JSON.stringify(response, null, 4));
$.ajax({
type: ‘POST’,
url: ‘postCreateCountMe.xsp?city=’ + response.city + ‘&region=’ + response.region + ‘&country=’ + response.country + ‘&loc=’ + response.loc
})
}, ‘jsonp’)
);
]]>
</xp:this.value>
</xp:scriptBlock>
<h3>
Client side IP geolocation using
<a href=”http://ipinfo.io”>ipinfo.io</a&gt;
</h3>
<hr />
<div id=”ip”></div>
<div id=”address”></div>
<hr />
Full response:
<pre id=”details”></pre>

Walkthrough

We assume you have jQuery stored in your application. Add it as a resource to your XPage. When the document is fully loaded we make a call to the external service, the response received we process. For demo purpose we also display it on the screen:

testscreen

At the end we make a post to a second XPage which will process the received data into a Notes document. We attach the data as parameters to the URL. The code for this XPage looks as followed:

<xp:this.resources>
<xp:script src=”/xpCGIVariables.jss” clientSide=”false”></xp:script>
</xp:this.resources>
<xp:this.afterRenderResponse><![CDATA[#{javascript:var dt:NotesDateTime = session.createDateTime(“Today”);
dt.setNow();
function createDoc(){
var doc = database.createDocument(); // Create the document
doc.appendItemValue(“Form”,”count”); // Assign the form
doc.appendItemValue(“date”,dt); // Append something to a field

var udt:NotesDateTime = session.createDateTime(“Today”);
var unixDate = parseInt(udt.toJavaDate().getTime()/1000);
//the time format usable in plotter:
var unixDateOnly = parseInt(udt.toJavaDate().getTime()/100000000)*100000*1000;

doc.appendItemValue(“dateunix”,unixDate); // Append something to a field
doc.appendItemValue(“dateonlyunix”,unixDateOnly); // Append something to a field

var cgi = new CGIVariables()
var browser = cgi.HTTP_USER_AGENT
doc.appendItemValue(“browser”,browser); // Append something to a field
var user = @UserName();
doc.appendItemValue(“user”,user); // Append something to a field

var page = facesContext.getExternalContext().getRequest().getRequestURI();
var page = view.getPageName()
doc.appendItemValue(“pagename”,@RightBack(page,”/”)); // Append something to a field

var _param1 = param.get( ‘city’ );
var _param2 = param.get( ‘region’ );
var _param3 = param.get( ‘country’ );
var _param4 = param.get( ‘loc’ );
doc.appendItemValue(“city”,_param1); // Append something to a field
doc.appendItemValue(“region”,_param2);
doc.appendItemValue(“country”,_param3);
doc.appendItemValue(“location”,_param4);
doc.save();
}
createDoc();

}]]></xp:this.afterRenderResponse>

Walkthrough

We use the infamous CGI variables SSJS library written by Thomas Gumz, IBM so we can add some additional CGI variables on the Notes document. So add this library to the XPage resources section.

After the page is rendered we execute code. Besides some CGI variables we process the data in the included parameters.

In the end your Notes document could look as followed:

resultdoc

 

 

Feedback

The idea is simple, also the implementation. Now I just need to find a nice GeoData chart to visualize the collected data. Do you got any suggestion(s)?

Ps. Does anyone read the information below???

Job Wanted

Looking for a creative brain? Choose me!

job-wanted

Visitors per day statistics with zooming and weekends (prototype)

I created a visitor statistics prototype for an application. With the statistics we would like to check if an application is used frequently or if it’s sensitive to corporate news updates.

The application help the user to select what type of meeting they should setup according to several parameters the user provide (e.g.  number of participants, duration, share & edit, see others, participants outside firewall, type of communication (top down, horizontal), type of meeting subject).

The application also is intended to promote the usage of electronic solutions instead of “expensive” “on-site” visits.

Conditions

  • In the first step we are only interested in the number of visits. In the next phase we are also interested what parameters the user provide when searching.
  • Only unique sessions are recorded.
  • Users are expected to be anonymous.
  • An export should be possible for spreadsheet addicts (not mentioning a specific Office product).

Architecture

The solution exists of the following (design) elements:

  • A custom control to record the visit.
    • Each unique visit is a Notes document storing date, browser, user, dateunix, page name.
      • We record also the browser type using the CGIVariables() function by Thomas Gumz.
  • A custom control to display the statistics. We have chosen to use a plot a time serie using Flot as a starter.
    • A Notes view categorized by a unix timestamp to provide data for the chart.
  • An XPage for the providing a spreadsheet output.
    • A Notes view that is used by the XPage for providing data for the output.

Implementation

  • Download and install the Flot resources via Package Explorer.
  • Create a custom control e.g. ccVisitRegister:

<xp:this.resources>
<xp:script src=”/xpCGIVariables.jss” clientSide=”false”></xp:script>
<xp:script src=”/xpFunctions.jss” clientSide=”false”></xp:script>
</xp:this.resources>
<xp:this.beforePageLoad><![CDATA[#{javascript:var v = sessionScope.get(“visit”);
var dt:NotesDateTime = session.createDateTime(“Today”);
dt.setNow();
function createDoc(){
var doc = database.createDocument(); // Create the document
doc.appendItemValue(“Form”,”count”); // Assign the form
doc.appendItemValue(“date”,dt); // Append something to a field

var udt:NotesDateTime = session.createDateTime(“Today”);

var unixDate = udt.toJavaDate().getTime()/1000;

doc.appendItemValue(“dateunix”,unixDate); // Append something to a field

var cgi = new CGIVariables()
var browser = cgi.HTTP_USER_AGENT
doc.appendItemValue(“browser”,browser); // Append something to a field
var user = @UserName();
doc.appendItemValue(“user”,user); // Append something to a field

var page = facesContext.getExternalContext().getRequest().getRequestURI();
doc.appendItemValue(“pagename”,@RightBack(page,”/”)); // Append something to a field

doc.save();
}
if (v == null){
//New session -> register
sessionScope.put(“visit”,dt);
sessionScope.put(“create”,”yes”);
if (isAuthor()==true){
createDoc();
}
}
else{
sessionScope.put(“visit”,dt);
sessionScope.put(“create”,”no”);
}}]]></xp:this.beforePageLoad>

xpFunctions is a library with helper functions e.g. the isAuthor() function:

function isAuthor(){
var level = database.getCurrentAccessLevel();
if (level >= NotesACL.LEVEL_AUTHOR) {
return true;
} else if (level < NotesACL.LEVEL_AUTHOR) {
return false;
}
}

  • Create a custom control to display the statistics:

<?xml version=”1.0″ encoding=”UTF-8″?>
<xp:view xmlns:xp=”http://www.ibm.com/xsp/core”&gt;
<link href=”flot/examples/examples.css” rel=”stylesheet” type=”text/css”></link>
<!–[if lte IE 8]><script language=”javascript” type=”text/javascript” src=”flot/excanvas.min.js”></script><![endif]–>
<script language=”javascript” type=”text/javascript” src=”flot/jquery.js”></script>
<script language=”javascript” type=”text/javascript” src=”flot/jquery.flot.js”></script>
<script language=”javascript” type=”text/javascript” src=”flot/jquery.flot.time.js”></script>
<script language=”javascript” type=”text/javascript” src=”flot/jquery.flot.selection.js”></script>
<xp:text escape=”false” id=”scriptBlock”>
<xp:this.value><![CDATA[#{javascript:
var v:NotesView = database.getView(“countsbyunixdate”);
v.setAutoUpdate(false);
var nav:NotesViewNavigator = v.createViewNav();
var entry:NotesViewEntry = nav.getFirst();
var nEntry:NotesViewEntry = null;
var newArr = new Array();
while(entry != null){
nEntry = nav.getNextCategory();
if(entry.isCategory()){
var values = entry.getColumnValues();
var children = entry.getChildCount();
newArr.push (“[” + values.get(0) + “,” + children + “]”);
}
try{
entry.recycle();
}catch(e){
}
entry = nEntry;
}
try{
nav.recycle();
v.recycle();
}catch(e){
}
var newArrString:String = newArr.join(“,”);

“<script type=\”text/javascript\”>”+
“$(function() {” +
“var d = [” + newArrString + “];” +
“for (var i = 0; i < d.length; ++i) {d[i][0] += 60 * 60 * 1000;}” +
“function weekendAreas(axes) {“+
“var markings = [],”+
“d = new Date(axes.xaxis.min);”+

“d.setUTCDate(d.getUTCDate() – ((d.getUTCDay() + 1) % 7));”+
“d.setUTCSeconds(0);”+
“d.setUTCMinutes(0);”+
“d.setUTCHours(0);”+
“var i = d.getTime();”+
“do {“+
“markings.push({ xaxis: { from: i, to: i + 2 * 24 * 60 * 60 * 1000} });”+
“i += 7 * 24 * 60 * 60 * 1000;”+
“} while (i < axes.xaxis.max);”+
“return markings;”+
“}”+
“var options = {“+
“xaxis: {“+
“mode: \”time\”,”+
“tickLength: 5″+
“},”+
“selection: {“+
“mode: \”x\””+
“},”+
“grid: {“+
“markings: weekendAreas”+
“}”+
“};”+

“var plot = $.plot(\”#placeholder\”, [d], options);”+
“var overview = $.plot(\”#overview\”, [d], {“+
“series: {“+
“lines: {“+
“show: true,”+
“lineWidth: 1″+
“},”+
“shadowSize: 0″+
“},”+
“xaxis: {“+
“ticks: [],”+
“mode: \”time\””+
“},”+
“yaxis: {“+
“ticks: [],”+
“min: 0,”+
“autoscaleMargin: 0.1″+
“},”+
“selection: {“+
“mode: \”x\””+
“}”+
“});”+
“$(\”#placeholder\”).bind(\”plotselected\”, function (event, ranges) {“+
“plot = $.plot(\”#placeholder\”, [d], $.extend(true, {}, options, {“+
“xaxis: {“+
“min: ranges.xaxis.from,”+
“max: ranges.xaxis.to”+
“}”+
“}));”+
“overview.setSelection(ranges, true);”+
“});”+
“$(\”#overview\”).bind(\”plotselected\”, function (event, ranges) {“+
“plot.setSelection(ranges);”+
“});”+
“$(\”#footer\”).prepend(\”Flot \” + $.plot.version + \” &ndash; \”);”+
“});”+

“</script>”

}]]>
</xp:this.value>
</xp:text>
<xp:panel tagName=”h1″>Visitors</xp:panel>
<xp:panel id=”content” tagName=”div”>
<div class=”demo-container”>
<div id=”placeholder” class=”demo-placeholder”></div>
</div>

<div class=”demo-container” style=”height:150px;”>
<div id=”overview” class=”demo-placeholder”></div>
</div>
</xp:panel></xp:view>

  • Create a Notes view categorized by a unix timestamp:

notesview

Result

plot

These are the main items on the statistics page (beside a link to the export function). You can zoom in/out on the time line.

Feedback

The idea is simple, also the implementation. I will create a small project on OpenNTF later this week. In case you have some valuable additions/suggestions please let me know.

Job Wanted

Looking for a creative brain? Choose me!

job-wanted