PSC at IBM Connect Next Week

 

If you are going to IBM Connect next week in San Francisco, consider meeting our PSC Representatives John Head and Kathy Brown for a free consultation on how PSC can assist with your company with your collaboration roadmap.

Follow this link  to set up a meeting.  If you don’t already know John and Kathy, think of this as a networking opportunity.  Chat up Kathy about her new Mini and John about, well anything really.

I wish I was going to be there too but someone has to stay home and pay the bills 🙂

Advertisement

Creating an SFTP Utility in SSJS using JSch

I recently had a requirement to write some ssjs utilities to handle basic SFTP functions.  I’m not FTP expert so it was new to me that FTPS has functions built into the Apache Commons FTP library but SFTP does not.  (For more on FTP vs FTP/S vs SFTP, check out this article.)

To create the desired library I found Java Secure Channel by JCraft.com that would allow me to use java libraries in my ssjs to create the desired utility functions.  Using java classes in ssjs is a useful way to extend the functionality of ssjs if your requirements are such that you can’t have your business logic in java.

  1. Import the file into your NSF
  2.  Download the .jar file for JSch.
    • Right-click on your file in Package Explorer
    • Choose Build P ath –> Configure Build Path
    • Choose the “Add JARs” button
    • Navigate to your .jar file from step 1 and select OK
  3. Create a ssjs library for your utility functions.  I’ve functions to do the following tasks:
    • Connect to a server
    • Close the connection
    • Put a file on the server
    • Delete a file from the server
    • Get a list of file names from the server that match a pattern
    • Get a file from the server and return the contents as an input stream

The time consuming part here was in the conversion of the java objects to ssjs.  Aside from that the code is mostly straightforward.


var utilFTP = {

  /*
  * Initialize global variables used in all FTP methods
  * Code uses Java Secure Channel from JCraft
  * Shean McManus | PSC Group | September 2016
  */
  ftp:org.apache.commons.net.ftp.FTP,
  ftpClient:org.apache.commons.net.ftp.FTPClient,
  ftpClientConfig:org.apache.commons.net.ftp.FTPClientConfig,
  ssh:com.jcraft.jsch.JSch,
  sftpSession:com.jcraft.jsch.Session,
  sftpChannel:com.jcraft.jsch.ChannelSftp,

  /*
  * Initialize FTP session
  */
  connect: function(ftpServer:string, ftpUsername:string, ftpPassword:string, ftpKeyChecking:boolean, ftpKeyString:string) {
    try {
      // initialize JSCH object
      this.ssh = new com.jcraft.jsch.JSch();
      // create sftp session
      this.sftpSession = this.ssh.getSession(ftpUsername,ftpServer);
      // set the password
      this.sftpSession.setPassword(ftpPassword);
      // read boolean to implement key checking or not
      if (ftpKeyChecking == true) {
        // get host key string from calling function
        var his:java.io.InputStream = new java.io.ByteArrayInputStream(ftpKeyString.getBytes());
        // add key to known hosts
        this.ssh.setKnownHosts(his);
      } else {
        // disable key checking
        var config:java.util.Properties = new java.util.Properties();
        config.put("StrictHostKeyChecking","no");
        this.sftpSession.setConfig(config);
      }
      // connect to server
      this.sftpSession.connect();
      // get sftp channel
      this.sftpChannel = this.sftpSession.openChannel("sftp");
      // connect
      this.sftpChannel.connect();
      // return value
      return true;
    } catch(e) {
      // INSERT YOUR ERROR LOGGING CODE HERE
      return false;
    }
  },

  /*
  * Close FTP session
  */
  close: function() {
    try {
      // disconnect channel
      if (this.sftpChannel != null) {
        this.sftpChannel.disconnect();
      }
      // disconnect session
      if (this.sftpSession != null) {
        this.sftpSession.disconnect();
      }
      return true;
    } catch(e) {
      // INSERT YOUR ERROR LOGGING CODE HERE
      return false;
    }
  },

  /*
  * Delete file on FTP Server
  */
  deleteFile: function(ftpFilePath,ftpFileName) {
    try {
      this.sftpChannel.rm(ftpFilePath + "\\" + ftpFileName);
      return true;
    } catch(e) {
      // INSERT YOUR ERROR LOGGING CODE HERE
      return false;
    }
  },

  /*
  * Transfer file to FTP server
  */
  putFile: function(ftpFromFilePath:string, ftpFileName:string, ftpToFilePath:string) {
    try {
      // add the file to the ftp server, using double slashes to account for escaping
      this.sftpChannel.put(ftpFromFilePath + "\\" + ftpFileName, ftpToFilePath + "\\" + ftpFileName);
      return true;
    } catch (e) {
      // INSERT YOUR ERROR LOGGING CODE HERE return false;
    }
  },

  /*
  * Retrieve file from FTP server and return the input stream of the file
  */
  getFile: function(ftpFilePath:string,ftpFileName:string) {
    try {
      // get the file and return it into an input stream
      var myInputStream:java.io.FileInputStream = this.sftpChannel.get(ftpFilePath + "\\" + ftpFileName);
      return myInputStream
    } catch(e) {
      // INSERT YOUR ERROR LOGGING CODE HERE return false;
    }
  },

  /*
  * Returns an array of file names that match the prefix string
  */
  getFileNames: function(ftpMode:string,ftpFilePath:string,ftpFilePrefix:string) {
    try {
      // get a list of files that are in the file path
      var ls:java.util.Vector = this.sftpChannel.ls(ftpFilePath);
      var matchedFiles = [];
      // loop through list
      for (entry in ls) {
        // set up pattern to match
        var thisPattern:java.util.regex.Pattern = java.util.regex.Pattern.compile(ftpFilePrefix + '[0-9]{8}.csv');
        // get each entry
        var thisEntry:java.lang.String = entry.getFilename();
        // check if the entry matches the pattern
        var m:java.util.regex.Matcher = thisPattern.matcher(thisEntry);
        // initialize the temporary sequence input stream
        var sis:java.io.SequenceInputStream = null;
        if (m.matches()) {
          // if the file matches, push the contents into the matched files array
          matchedFiles.push(thisEntry)
       }
     }
     // sort the files by filename - most recent will be last
     return matchedFiles.sort();
    } catch(e) {
      // INSERT YOUR ERROR LOGGING CODE HERE return false;
    }
  }
}

MWLUG 2016 Recap

Attending MWLUG  last week in Austin, Texas was an enjoyable and educating experience despite the undercurrent of uncertainty that was felt throughout most of the event.  The decision of IBM to drop the release of Domino 9.0.2 from the 2016 roadmap had many attendees discussing the ramification of that decision on the future of their work and careers.  Despite all of that, I feel very positive about the future.   For current xPages developers, there will likely be plenty of work in that space for years to come even if future releases are fix packs on top of the 9.0.1 codestream.  Furthermore, there are many options available to modernize xPages applications in varying levels of complexity and reliance on the current xPages stack.    My co-worker John Head addressed that topic in his session on application modernization, Kathy and I provided platform-neutral ideas on dashboards and data visualizations, and many other sessions offered platform-neutral content valuable in and beyond Domino.

Some of the other specific highlights (in no particular order) of interesting content included:

  • Roy Rumaner presenting on vision accessibility in web pages
  • Kato Mann and Keith Strickland talking about Web Components
  • Howard Greenberg offering details on using maps, trees, and collections in Java
  • Eric McCormick talking about automating application builds and webpack
  • Jesse Gallagher talking about Darwino and modern application structures

The slides for my session with Kathy titled Using Dashboards to Transform Application Data Visualization can be found at SlideShare.

Finally, I can’t speak about MWLUG without talking about the wonderful friendships I have made attending these events.  This year was no exception.  New friends were made and plenty of time was spent socializing with many of the people that make this such a special community.  Special thanks to Richard Moy and Amanda Bauman for all their hard work making this event happen.  I hope we are all able to get together like this in some fashion or another for years to come.

 

 

MWLUG Registration Complete

A couple weeks ago I was honored to be selected to speak at MWLUG next month in Austin, Texas.  Attending my first conference as consultant with PSC, I will be once again speaking…this time with my co-worker Kathy Brown.  Having such a terrific and experienced speaker like Kathy as my co-presenter is comfort as we prepare over the next six weeks to provide a quality session on the use of dashboards in modern web applications.

Using Dashboards to Transform Application Data Visualization

When you’re driving a car, you need a lot of data quickly. How fast are you going? Are there any problems you need to address? Where do you get that data at a glance? Your dashboard, of course. All the info you need at your finger tips, easily understood. Your applications have a lot of data and important information, but can your users get to it quickly? Can they easily spot issues or know what they need to do right now? Shean and Kathy will take you for a test drive through some XPage application dashboards. They will demonstrate how to move away from email-driven tasks and buried information to show you how you can easily visualize your critical business data. Come see how an application can provide a visually striking, functional, and easy to use Data Visualization for common workflow actions, important info, charts, and key metrics. Put your users in the driver’s seat!

If you have not attended an MWLUG conference before I strongly urge you to consider attending.  It’s a fantastic event, full of great content (we hope to live up to that standard), great friends, and plenty of productive conversations.  I can’t wait!

 

 

Using jQuery.resize to Enhance Responsive Design

Twitter Bootstrap provides some convenient grid scaffolding to use for responsive design applications.  The xs-sm-md and lg classes allow you to adjust your design based on the screen real estate available.  Sometimes, however, this isn’t enough.  Recently when working on laying out a couple portlets horizontally on a page I discovered that I needed some additional control over the display (or lack thereof) of some columns in my portlets. In short, I wanted to hide some of those columns when the width of the screen was at a certain horizontal threshold.  The standard bootstrap classes were not granular enough for my needs.    Time for some jQuery wizardry.

Plan:

  1. Use $(window).resize to listen for the window size changing
  2. Check the size of the desired elements against certain thresholds
  3. Perform DOM manipulation as necessary to add/remove elements
	<div class="panel panel-primary panel-heading-custom">
		<div class="panel-heading">
			<h2 class="">
				Extending Responsive Design with jQuery.resize()
			</h2>
		</div>
		<div class="row row1">
			<div class="col-lg-6 col-md-6 col-sm-6 col-xs-6 panel-body-header">Panel Left</div>
			<div class="col-lg-6 col-md-6 col-sm-6 col-xs-6 panel-body-header">Panel Right</div>
		</div>
		<div class="row">
			<div class="col-lg-6 col-md-6 col-sm-6 col-xs-6 panel-body-header">
				Width:<span id="panel-left-width"></span>
			</div>
			<div class="col-lg-6 col-md-6 col-sm-6 col-xs-6 panel-body-header">
				Width:<span id="panel-right-width"></span>
			</div>
		</div>
		<div class="row">
			<div id="panel-left" class="col-lg-6 col-md-6 col-sm-6 col-xs-6 panel-body-custom">
				<div class="panel-body-inner-left">
					<table>
						<thead>
							<th class="c1">Column 1</th>
							<th class="c2">Column 2</th>
							<th class="c3">Column 3</th>
							<th class="c4">Column 4</th>
						</thead>
						<tbody>
							<tr>
								<td class="c1"></td>
								<td class="c2"></td>
								<td class="c3"></td>
								<td class="c4"></td>
							</tr>
						</tbody>
					</table>
				</div>
			</div>
			<div id="panel-right" class="col-lg-6 col-md-6 col-sm-6 col-xs-6  panel-body-custom">
				<div class="panel-body-inner-right">
					<table>
						<thead>
							<th class="c1">Column 1</th>
							<th class="c2">Column 2</th>
							<th class="c3">Column 3</th>
							<th class="c4">Column 4</th>
						</thead>
						<tbody>
							<tr>
								<td class="c1"></td>
								<td class="c2"></td>
								<td class="c3"></td>
								<td class="c4"></td>
							</tr>
						</tbody>
					</table>
				</div>
			</div>
		</div>
	</div>

The following bit of javascript will check the size of the panels and remove a column if the width of the panel becomes less than 500 (and then 400) pixels.

$('document').ready( function() {
	// initialize panel width display
	resizer();

	$(window).resize(function(){
	    resizer();
	});
});

function resizer() {
    $('#panel-left-width').text($('#panel-left').width());
    $('#panel-right-width').text($('#panel-right').width());
    plw = $('#panel-left').width();
    prw = $('#panel-right').width();
    $('#panel-left-width').text(plw);
    $('#panel-right-width').text(prw);
    if (plw &lt; 500) {
	$('.c4').hide();
    } else {
	$('.c4').show();
    }
    if (prw &lt; 500) {
	$('.c4').hide();
    } else {
	$('.c4').show();
    }
    if (plw &lt; 400) {
	$('.c3').hide();
    } else {
	$('.c3').show();
    }
    if (prw &lt; 400) {
	$('.c3').hide();
    } else {
	$('.c3').show();
    }
}

Simple and effective. This could be extended to other uses as well, taking into account different devices in addition to screen size.

See a working demo here.

An Effective Use of the ‘attr’ Property on xPage Elements

JQuery is a common javascript library today that can be used for any number of client side operations.  Sometimes it can be difficult to construct the right selector with xPage elements due to the way the engine renders the id tag.  Sure it is common to use the elements class attribute as a selector but sometimes that can only serve to muck up your design as you may already have several style sheets used for design.  That is where I have found a nice use for the ‘attr’ tag in xPage elements.

Consider the following use case.  I have a timesheet form that has a repeat control nested inside another repeat control.  The first repeat loops through the days of the week while the second loops through a set number of possible project entries per day.  I want to have all but the first row hidden for each day on load of a new timesheet, then have buttons to add a line and remove a line.  I can use jQuery but instead of trying to add the row number to my class attribute of each row, or trying to parse out the id I can use a new attribute called “rowNum” and grab that on the client side.

xPage Markup

<xp:repeat id="timesheetCard" indexVar="idx" var="rowNum" repeatControls="true" rows="7">
 <xp:this.value><![CDATA[#{javascript:var arrRow = [0,1,2,3,4,5,6]; return arrRow;}]]></xp:this.value>
 <xp:tr>
 <xp:this.attrs>
 <xp:attr name="row">
 <xp:this.value><![CDATA[#{javascript:rowNum}]]></xp:this.value>
 </xp:attr>
 </xp:this.attrs>
 .
 .
 .
 </xp:tr>
 </xp:repeat>

So here I have used the ‘attr’ property of the xpage element to calculate an attribute I called “row”.  I could have done this using straight HTML but by converting the tag to an xPage tag I could access it though the designer properties section and get myself a javascript editor to use.

Now, on the client side wherever I want to have access to this row I can access it via jQuery as shown here:

$('[row=5]').show()

The above code will find the row with a row=5 and show that row.  This would be useful if I had that row hidden previously.

The above is not the only possible use case for ‘attr’.  You can use it anywhere you want to add additional metadata to your markup for use client-side.  A nice tool to have in your toolbox!

When ‘font-weight: bold’ Is Not Quite Right

Lately I’ve been doing a lot of front-end UI work and in doing so there have been situations where I want to call attention to a piece of text.  Sometimes displaying the text in a BOLD font-weight is just right, but other times that feels more like cracking a nut with a sledge hammer.  CSS has supported a more granular approach to the font-weight attribute for some time but it’s a feature that can be easily forgotten.  In addition to normal, bold, and italics designations, you can use relative terms like “lighter” or “bolder” which apply the weight relative to the parent element. Perhaps more useful is the numeric designations available.  CSS supports font-weight’s of 100 to 900 in even increments of 100.   The catch is that the font you are using needs to support that level of granularity.

I had planned to code out a whole series of examples but I found an article that has already done this so no need to go through that exercise.  Read here to see examples of what this looks like and more details on how it’s applied to fonts that only support the normal and bold font-weights.

CSS Tricks: Font-Weight

Converting HTML Tags to XPage Tags

As xPages becomes a common platform for other modern web frameworks, you may find yourself writing a lot of standard HTML (such as Twitter Boostrap markup) in your Domino Designer source panel.  Here is a convenient reminder that most HTML tags can be converted to xPage tags by adding the xp: prefix.

So,

<div>...</div>

becomes

 

 <xp:div>...</xp:div>

Now it may not be immediately obvious why this would be a useful pattern but xPage tags have with them all the API hooks that are available for the standard xPages controls.  One use case could be to leverage server side logic to selectively render certain parts of your code.   For example you might have some mark up that resembles this:

<div id="show-when-editable" class="row">
<div class="col-md-2">...</div>
<div class="col-md-4">...</div>
<div class="col-md-6">...</div>
</div>
<div id="hide-when-editable" class="row">
<div class="col-md-2">...</div>
<div class="col-md-4">...</div>
<div class="col-md-6">...</div>
</div>

If you want to show the first div in edit mode and the second otherwise you could convert the above to the following:

<xp:div styleClass="row">
<xp:this.rendered><![CDATA[#{javascript:document.isEditable()==true}]]></xp:this.rendered>
<div class="col-md-2">...</div>
<div class="col-md-4">...</div>
<div class="col-md-6">...</div>
</xp:div>
<xp:div styleClass="row">
<xp:this.rendered><![CDATA[#{javascript:document.isEditable()==false}]]></xp:this.rendered>
<div class="col-md-2">...</div>
<div class="col-md-4">...</div>
<div class="col-md-6">...</div>
</xp:div>

A couple things to note about this type of tag conversion:

–  <xp:div> tags don’t respect normal HTML attributes.  For this reason, you need to also convert class="class-name" to styleClass="class-name"

– To use other attributes you can add an attribute block.  For example, code to add a data-title="My Title" to your tag would look like this:

<xp:this.attrs>
<xp:attr name="data-title">
<xp:this.value><![CDATA[#{javascript:'My Title'}]]></xp:this.value>
</xp:attr>
</xp:this.attrs>

– I haven’t tested this with all legal HTML tags, so there may be some incompatibilities but it does work well with most of the standard building block tags used for construction Boostrap grids and tables.  If you know of other limitations, please feel free to share.

Using a placeholder with Select2 in xPages

Select2 is a fantastic component that provides for a very nice alternative to the classic combo box.  While working with it this week I ran into an issue where I was unable to get the placeholder option to work.  Turns out you need to seed your values array with a null value or else the placeholder won’t display.

Using this select2 initialization code:

//Initialize Select2 Drop Down
$(‘.select2’).select2( {
placeholder: “Select value”,
allowClear: true,
minimumResultsForSearch:-1
})

This works:

var arr = new Array(“”);
var values = @DbColumn()  \\Insert your own code here
var list = arr.concat(values);
return list;

This does not work:

var values = @DbColumn()  \\Insert your own code here
return values;

A subtle but important distinction.  Thanks to Johnny Oldenberger for this tip.  I found this helpful information on one of his blog posts