Archive | June 2013

(Mis)Adventures in Upgrading

I’ve had an incredibly frustrating few days trying to install Notes 9 on my secondary development machine. I’m sure that a clean installation/upgrade would have been fine, but I’m trying to maintain multiple versions on the machine and it just wouldn’t work like previous installations, where I would temporarily rename the latest version’s directory, let the installation process install into a different directory (while not finding anything in the previous directory to remove) and then restore the directory name of the previous version. R6, R7, and R8.5.2 clients and servers have co-existed peacefully for years, but Notes 9 does not appear to be a friendly neighbor.

Attempts #1-3 – Try What’s Worked Before

I started with what’s worked in the past — renaming my “current” version (8.5.2) directory to something that won’t be listed in registry keys so it would not be eradicated by the Notes 9 installation. Even with that, I set the installation directory to a different path (\Notes9\, because it would be silly for it to install in \Notes8\).

On my first attempt, it let me change the directory names and then started the installation process, unpacked files, ran for a few minutes, and then said it was rolling back and it went away.

On my second and third attempts, it wouldn’t even let me change the installation path away from the \Notes8\ path, but the end result was still the same. (The difference between the two attempts was that I right-clicked on the installation icon and chose “Run as Administrator” on the third one, but there was no difference.)

Attempt #4 – Shotgun Approach to the Registry

I hypothesized that, if I removed all references to Notes 8 in the windows registry, then Notes 9 might think it was doing a clean installation and it would leave the rest of my files alone. So I searched through the registry and removed just about everything that referred to Notes 8.

Bad idea.

My machine was still able to boot up, but I couldn’t so much as open a folder on the operating system without getting an error.

Whoops.

“System Restore is initializing…”. Music to my ears. (Thank you Windows7 for having creating a restore point earlier that morning!)

Attempt #5 — Scalpel Approach to the Registry. Success! But…

Of course, I went right back to hacking away at the registry, but with a fine-tuned approach. I only removed the entries that had to do with the Notes installation.

Success! R9 installed into it’s own directory! Client, Designer, Admin, and Server all work!

BUT…Notes 8.5.2 is not the same. It looks like R7.

I notice that it says it’s running the basic (i.e. non-eclipse) configuration.

After lots of searching the internet, I checked for properties like the Notes.ini setting UseBasicNotes=1. The parameter was not in notes.ini and even adding it (set to 0) didn’t make a difference. I also tried using the -sa parameter in the icon that launches Notes, but it had no effect.

So, I decided to try a different approach.

Attempts #6-8: Sideloading

It works for mobile devices, so I figured I’d give it a try. My plan was to perform a clean installation of 8.5.2 on my wife’s (then my son’s) laptop, then just copy the directories and icons over to my machine.

No joy.

Notes wouldn’t even start after any of those attempts.

End State

So, I’m back to where I was after attempt 5. Notes 8.5.2 FP1 client, designer, admin, and server will all run, but the clients all look like R7 because they’re stuck in basic mode.

At this point, if I want to modify and app that has to work in 8.5.2, I’ll need to use Notes 9 Designer and set the application level property to only use features that work up to that version.

I assume that will do the job, but I’m not happy about it.

Suggestions?

I know, I know. I should be using separate VMs for each version and then I wouldn’t have this issue (and that is the way that my primary work machine is configured). It appears that’s approach I’ll have to take on my personal machine going forward as well.

But if you have any tips on how I can restore standard client mode for my 8.5.2 installation, please let me know!

Advertisements

Upcoming TLCC Webinar: Dojo Grids in XPages

Join me next Thursday (6/27) at 10:30am Eastern for the next TLCC webinar.

I’ll show how you can provide a new look and feel and rich functionality with several variations of Dojo data grids. You’ll learn about the XPages Dojo Data Grid control and its key features, including infinite scrolling, sorting, and editable cells. Then you’ll see how to transform the grid into a Dojo EnhancedGrid and take advantage of enhanced plugins to provide even more functionality, such as multi-rule filtering and context menus. Finally, you’ll get a glimpse of how to bypass the grid control in order to create a categorized Dojo TreeGrid.

Click here to register.

XPages Tip: Alternating Row Colors along with Dynamically Setting Row Colors

In my last past, I showed how you can dynamically set row colors in views and repeats based on data in each entry. In this post, I’ll show how you can take it a step further and use that technique along with providing default alternating row colors.

In my task list, I want to alternate the rows between white and light gray to provide visual separation of the data. But I want to override those defaults and display the row with a yellow background if the task is expiring and a red background if the task is late.

Here’s an example of a repeat control using this combined technique:

Dynamic Row Styling Part 2

Here’s the updated css:

.repeatRow {
background-color: #EEEEEE;
}

.repeatRowAlt {
background-color: #FFFFFF;
}

.yellowRow {
background-color: #FFFF00;
}

.redRow {
background-color: #FF0000;
}

NOTE: It is important to define the red and yellow classes after the default row classes. Whichever ones are defined later will take precedence when multiple classes are assigned to a row.

Here’s the computed style class code to make it work:

var status = varTask.getColumnValue('Status');
var cssClass;

if (rowIndex % 2 == 0) {
cssClass = 'repeatRow';
} else {
cssClass = 'repeatRowAlt';
}

if (status == 'Expiring') {
cssClass += ' yellowRow';
} else if (status == 'Late') {
cssClass += ' redRow';
}

return cssClass;

The code checks the row number and starts with a class of repeatRow or repeatRowAlt. If the status is Expiring or Late, it adds another class to the list. Note that there’s a space before the class name in lines 11 and 13. This is because I’m retaining the first class name and adding a second class to it.

I’m doing it this way because I tend to define font and spacing settings in the repeatRow and repeatRowAlt classes. By assigning two classes to rows as needed, I can retain those settings while overriding the background color when required.

XPages Tip: Dynamically Set Row Colors in a View or Repeat Control Based on Data

In a recent post, Kathy Brown showed how to alternate row colors, in XPages. In this post, I’ll show how to take that a step further and dynamically set the row color based on data in the row.

You can compute the style class of each row in a view panel or repeat control (or data table or data view, etc), so you have the ability to check the data in each view entry and set a style class accordingly. Let’s start with the view panel.

In my example, I have a list of tasks. When a task is about to expire, I want the row to appear with a yellow background. When the task has expired, I want the row to appear with a red background.

Here’s a View Panel using this technique:

Dynamic Row Styling Part 1 - View

Here’s a Repeat Control:
Dynamic Row Styling Part 1

This requires 3 steps:

  1. Create CSS style classes for the row colors (and include the style sheet on the page)
  2. Set the var property of the view panel
  3. Compute the style class to use for the row, based on the data

1. Create CSS Style Classes for the Row Colors

I have defined these styles in a style sheet that is included on my page:

.yellowRow {
background-color: #FFFF00;
}

.redRow {
background-color: #FF0000;
}

2. Set the var property of the view panel

In order to compute the class based on data in each view entry, we need to have the ability to read data from the view entry. The var property of the view panel gives us that handle.

Dynamic Row Styling Part 1 - View Var Property

3. Compute the style class to use for the row, based on the data

Now that I have styles defined and a handle to the view entry, I can compute the style class for each row. To do so, select the view panel properties, select the Style subtab, and select the rowClasses property. Next to the Class property, click on the diamond to open up an SSJS window and add the code to compute the style class.

Dynamic Row Styling Part 1 - View Property

This code will read the value of the ‘status’ column and return a class name to use for the row accordingly.

var status = varTask.getColumnValue('Status');
var cssClass = '';

if (status == 'Expiring') {
  cssClass = 'yellowRow';
} else if (status == 'Late') {
  cssClass = 'redRow';
}

return cssClass;

Working with a Repeat Control

All of the same concepts apply. The difference is that you don’t have a built-in styleClass property to use. Assuming your repeat control contains a table, you can compute the style class on the xp:tr tag within the xp:repeat tag.

It can be difficult to try to select the xp:tr tag directly, but you can click on the first cell in the row and then locate the xp:tr tag either via the Outline view or in the page source. Once you have the xp:tr tag selected, you can compute its styleClass property and use the same code as shown above, provided you have defined the same var name for the repeat control.

Dynamic Row Styling Part 1 - Repeat

XPages Tip: Check whether your Checkbox is Checked with SSJS

If you’re not used to the fact that all component values are treated as strings by default, it can seem strange when you check the value of a checkbox component and expect a boolean return value.

I was recently working with a form that used a checkbox for one field. As I wrote SSJS code to check whether the checkbox was selected, I was reminded that comparing the value of that field is not done the way I would naturally expect. I would expect that code like this would work to execute logic based on the state of the checkbox:

if (getComponent('checkBox1').getValue()) {
  // checkbox selected - do something
} else {
  // checkbox not selected- do something else
}

This doesn’t work as expected. The condition in line 1 above will always evaluate to true.

In this next block of code, the condition will always evaluate to false and the ‘else’ block will always execute:

if (getComponent('checkBox1').getValue() == true) {
  // checkbox selected - do something
} else {
  // checkbox not selected- do something else
}

This is because component values are always treated as strings (on the front end, they’re not converted yet). Therefore, if you don’t explicitly define selected and unselected values for the checkbox, then it’s value will either be “true” or “false”. (Even if you compute the values for the checkbox and set them to boolean values, they’re still treated as strings when checking the component.)

So, if you want to check whether the checkbox is selected, you need to use code like this:

if (getComponent('checkBox1').getValue() == "true") {
  // checkbox selected - do something
} else {
  // checkbox not selected- do something else
}

Troubleshot Port 80 Being Blocked on a Local Dev Server

I came across a strange issue when trying to do some development on a Notes 9 server that’s running locally on my development machine. When the server started, it threw an error message in the console, saying “HTTP Server: Error – Unable to Bind port 80. port may be in use or user needs net_privaddr privilege.”

Port80Block_ServerConsole

What really threw me off is that I’ve used this server several times already and haven’t had this problem.

Fortunately, it’s generally easy to figure out what’s blocking the port.

If you open up a command prompt and type netstat -an -o, you’ll get a list of all ports in use. In my case, the process ID was 4904.

Port80Block_Netstat

Note the PID column — it lists the process ID of the task.

Scroll to find what’s listening on port 80 (TCP 0.0.0.0:80) and get the process ID.

Next, open up the Task Manager. If you don’t see the PID column in the list of Processes, click on the View menu and choose Select Columns… and add the PID column.

Port80Block_TaskMgr_ProcessIDNow you can locate the process that is blocking port 80 and shut it down. As you can see in the screen shot above, the task with process ID 4904  was Skype!

Once that was shut down, the server was able to start and use port 80.

Port80Block_ServerConsole_Success

Dojo Data Grid – Part 21: Locking Columns

In a recent post, Mark Roden showed how to horizontally lock columns in an ExtJS grid, which has the same effect as Freeze Panes in Excel. In this post, I’ll show how to achieve the same functionality with the Dojo DataGrid.

Dojo Data Grid Series

LockedCols_A

When the grid has locked columns, a horizontal scroll bar will appear to scroll unlocked columns when there isn’t enough screen real estate to display all of the columns. In the screen shot above, the first two columns are locked and the rest will scroll.

Based on the Dojo DataGrid documentation this seems like it should be a relatively easy feature to implement. With a declaratively-or programmatically-defined grid, it’s pretty simple. Unfortunately, I don’t see a way to make it work with the Dojo Data Grid control in XPages.

Declarative Definition

In a declaratively-defined grid, you add colgroup tags before the tags defining the grid columns. For example, if you have a 6-column grid and you want to freeze the first column, you’d add this before the thead tag in the grid definition:

<colgroup colspan="1" noscroll="true"></colgroup>
<colgroup colspan="5"></colspan> 

Programmatic Definition

You can implement the same effect in a programmatically-defined grid by modifying the structure property to list two groups of cells (one set to freeze and one set to scroll), rather than just defining the grid rows.

An example of this method will be shown in the solution below.

Problem with the Dojo Data Grid Control

Unfortunately, there doesn’t appear to be a way to modify the output of the Dojo Data Grid control in a similar manner. I tried adding colgroup tags within the grid tag but before the column tags, but the colgroup tags were not rendered. I also tried other methods of running client-side javascript after the page loads to insert the tags and re-draw the grid, but none of it was effective.

The Solution – Programmtically Define the Grid

So, for the time being, the way to implement column locking in the grid is to programmatically declare your grid instead of using the Dojo Data Grid control.

It’s not as complicated as it might sound, but it’s definitely more work than using the grid control.

Here is the source of the entire page that generated the grid shown in the screen shot above.

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
  xmlns:xe="http://www.ibm.com/xsp/coreex" dojoParseOnLoad="true"
  dojoTheme="true">

  <xe:restService id="restJsonService" pathInfo="gridData">
    <xe:this.service>
      <xe:viewItemFileService defaultColumns="true"
        var="gridEntry" viewName="ByName-First" count="10000">
      </xe:viewItemFileService>
    </xe:this.service>
  </xe:restService>

  <xp:this.resources>
    <xp:dojoModule name="dojox.grid.DataGrid"></xp:dojoModule>
    <xp:dojoModule name="dojo.data.ItemFileWriteStore"></xp:dojoModule>

    <xp:styleSheet
      href="/.ibmxspres/dojoroot/dojox/grid/resources/tundraGrid.css">
    </xp:styleSheet>
  </xp:this.resources>
	
  <xp:eventHandler event="onClientLoad" submit="false">
    <xp:this.script><![CDATA[		
      var jsonStore = new dojo.data.ItemFileWriteStore({ url: "Grid_21_LockColumns.xsp/gridData"});

      var layout = [
        {cells:[ [ 
          {field:'firstname', name:'First',width:'8em'}, 
          {field:'lastname', name:'Last', width:'10em'}
          ] ], noscroll:true 
        }, 
        {cells:[ [ 
          {field:'address', name:'Address',width:'18em'},
          {field:'city', name:'City',width:'10em'},
          {field:'state', name:'State',width:'5em'},
          {field:'zip', name:'Zip',width:'7em'}
          ] ] 
        }
      ];
	
      grid = new dojox.grid.DataGrid({
        store: jsonStore,
        structure: layout,
        rowsPerPage: 25,
        autoHeight: 15
      }, '#{id:gridNode}');

      grid.startup();
    ]]></xp:this.script>
  </xp:eventHandler>

  <xp:div id="gridNode"></xp:div>

</xp:view>

Lines 3 and 4 show the two dojo properties that need to be set at the page level. Since we’re not using the Dojo Data Grid control, we have to set these properties manually.

Lines 6-12 define the REST service that provides the data for the grid

Lines 14-21 include the resources required. This generally isn’t required when using the grid control, because some resources are automatically included by the control.

Lines 23-51 include the code that defines the grid. The code runs on the page’s onclientload event.

  • Line 25 sets up the data store for the grid. It reads data from the REST service on the same page.
  • Lines 27-40 define the grid structure. The first group of cells has the noscroll attribute set to true. This locks the columns in place.
  • Lines 42-47 create the grid and set the key properties
  • Line 49 generates the grid.

Disclaimer

This is a proof-of-concept to show the column locking feature. I realize that creating the REST service and setting its count to a high number to include all records while also not using a RESTful data store defeats the purpose of lazy loading. However, I had issues getting the REST data store to display data in the grid.