meta/LPAR

Thinking inside the Big Blue box.

Tracking down misbehaving Domino web agents

The problem: You have a web agent somewhere which uses LotusScript MessageBox to output an error message. However, the message doesn’t say which agent or database it’s being output by, so you need to locate the agent which is printing the text.

The solution: At the console,

set config AgentThreadDebug=1
tell http restart

The HTTP task will then display a debug for each web-triggered agent that starts or ends.

Obviously, you’ll want to turn this off when you’re done debugging.

Posted by meta on 2013-05-08 | Posted in Domino, LotusScript | Tagged , , , , , , , | Comment

Cookies with XPages

My first XPages app is coming along nicely. Today I needed to add some cookie support, and discovered that the otherwise comprehensive Mastering XPages had no information on the topic. There’s a post on the Lotus Developer Domain, but it’s a bit vague and makes the whole thing look harder than it really needs to be.

I started by writing myself a quick Server Side JavaScript library to encapsulate the details. As always, I’m using the Revealing Module Pattern. Here’s the code:

 var Cookies = (function () {
   /**
    * Get a cookie value as a Cookie object.
    */
   var get:javax.servlet.http.Cookie = function (cookiename:string) {
     return cookie.get(cookiename);
   },

   /**
    * Convenience method to get a cookie value as a string.
    */
   getString:string = function (cookiename:string) {
     return cookie.get(cookiename).getValue();
   },

   /**
    * Set a cookie value. Optional max age is in seconds.
    * Must be called during at least a partial update.
    */
   set = function (cookiename:string, value, maxagesecs:double) {
     var xcon = facesContext.getExternalContext();
     resp = xcon.getResponse(), cmap = xcon.getRequestCookieMap(), ck;
     if (cmap.containsKey(cookiename)) {
       ck = cmap.get(cookiename);
       ck.setValue(value);
     } else {
       ck = new javax.servlet.http.Cookie(cookiename, value);
     }
     if (typeof maxagesecs !== 'undefined') {
       ck.setMaxAge(maxagesecs);
     }
     resp.addCookie(ck);
   };

   return {'get': get, 'set': set, 'getString': getString};
 }());

Note that for fetching cookie values in my get() method, I use the predefined cookie variable we’re handed by the XPages runtime, rather than fetching them raw via getHeader(). JSF has already parsed all the cookies out into individual objects, so it would be crazy not to take advantage of that.

The set() method is a bit more complicated, because if you already have a cookie set, you only want the method to change its value (and optionally its maximum age), rather than creating a whole new cookie and hence losing the other properties.

Finally, I added a getString() convenience method to cover the 99% of the time when you just want to fetch the cookie’s value and don’t care about any of the other properties of the Cookie object. As far as I can tell from the Chrome debugger, XPages sets the domain and path to sensible values by default. Oh, and as far as I can tell the above code works, but it’s a Friday and it has been pretty lightly tested, so no guarantees…

So, with this SSJS library loaded, you can give a control on a web page a default value from a cookie:

<xp:comboBox id="locationComboBox">
  <xp:this.defaultValue><![CDATA[#{javascript:
    var locid:string = Cookies.getString("Location");
    locid.length === 11 ? locid : '';} ]]>
  </xp:this.defaultValue>
  …

The length check I’m doing there is to make sure the value looks like a Notes/Domino short unique ID, as produced by @Unique. To set a value in an onChange event:

<xp:eventHandler event="onchange" submit="true" refreshMode="partial">
  <xp:this.action>
    <xp:executeScript>
      <xp:this.script><![CDATA[#{javascript:
        Cookies.set("Location", 
        getComponent('locationComboBox').getValue(), 86400);}]]>
     </xp:this.script>
    </xp:executeScript>
  </xp:this.action>
</xp:eventHandler>

The 86400 being a day’s worth of seconds. Note that since the library is server-side JavaScript, you need to call it as part of an event that causes an HTTP connection, such as a partial refresh. If you want to set cookies without any HTTP round trip, you’ll need to use a completely different JavaScript API, the one provided by the browser.

Posted by meta on 2013-05-03 | Posted in JavaScript, XPages | Tagged , , , , | Comment

Passing document data objects to XPages custom controls

Perhaps unfortunately, custom controls inherit the scope and variables of the XPages they are placed on. The upside of this is that you can bind data to document1 on an XPage, and then refer to document1 in custom controls on that XPage. The downside is that if you define a variable in an XPage (such as a data source), and then define it in the custom control as well, the two will clash. It appears that the custom control definition of the variable ends up overriding the XPage, but I wouldn’t rely on that behavior if I were you.

This means it’s probably a good idea to define data sources at the lowest possible level of the component tree, to limit their scope. You can define document data sources in a custom component, have no data sources at XPage level, and have the custom component load the document as expected by parsing the URL parameters.

So, what if you want to bind a document at XPage level, and then edit it at custom control level, without any risk of variable names clashing? That’s possible too, but is rather badly documented. There are some web pages discussing the technique, but they seem to be a bit out-of-date for Notes 8.5.3 and up. So, here’s the procedure:

  1. Define your custom control. Don’t give it any data sources; no dominoDocuments.

  2. Define a custom property. In the examples below, I’m calling it docToEdit.

  3. In the property Type selector, use the folder button to bring up the expert options dialog, expand Data Sources, and select Domino Document. In the type field, it will show up as com.ibm.xsp.model.domino.DominoDocumentData.

  4. In the Editor selector, select ‘Data Source Picker’.

  5. In the controls which are part of the custom control, instead of using (say) document1.Size to bind to a field, use a JavaScript expression like #{compositeData.docToEdit.Size}.

  6. Save and close the custom control, and drop it onto your XPage.

  7. XPages will let you pick a document variable for the custom parameter using the GUI. However, if you try to build, you’ll get an error saying that the parameter can’t be primitive. So, you’ll need to edit the parameter to have a JavaScript value:

That’s it. Your control can now edit and save documents passed to it in the docToEdit parameter. Furthermore, the control will automatically know whether the document passed to it is in read mode or edit mode. Create mode also works, if you make sure that no document is loaded at XPage level (so nothing is passed to the custom control).

Posted by meta on 2013-04-22 | Posted in XPages | Tagged , , , | Comment

Applying individual CSS styles to TH elements in XPages

The problem: You need to apply different CSS style values to different table header elements in a table, and you’re producing the table using XPages.

You might initially think that you can use the xp:viewColumnHeader styleClass property. If you try it, with something like this:

<xp:viewColumn columnName="Field1" id="viewColumn1">
  <xp:viewColumnHeader value="Field 1" 
    id="viewColumnHeader1" styleClass="one"/>
</xp:viewColumn>
<xp:viewColumn columnName="Field2" id="viewColumn2">
  <xp:viewColumnHeader value="Field 2" 
    id="viewColumnHeader2" styleClass="two"/>
</xp:viewColumn>

…then you’ll find that you get output like this:

<tr><th scope="col"><div class="one"><span>
  <span id="…" class="one">Field 1</span></span></div></th>
<tr><th scope="col"><div class="one"><span>
  <span id="…" class="one">Field 1</span></span></div></th>
</tr>

Close, but no banana. Your next thought might be to embed the view using an xp:dataTable, as that has more presentation options. However, xp:dataTable only has a single headerClass property which is applied to every TH element. If you try putting a comma-separated list of values in, which works with other styleClass properties, then it just puts the entire list into every th element’s style attribute, complete with commas.

All is not lost, however. While there’s only a single headerClass attribute, if you give it a value that changes every time—like #{javascript:@Random();} for example—you’ll discover that it is evaluated once for every table column.

So, we need a function which will iterate through our desired list of CSS classes, returning the next one in the list each time. Here’s some code:

var statefullyIterate = function (varname, values) {
  var i = requestScope.get(varname);
  if (i === null) {
    i = 0;
  } else {
    i = i + 1;
  }
  requestScope.put(varname, i);
  return values[i];
}

This function checks the requestScope to see if there’s a property variable with the name it expects. If so, it takes that value, increments it, stores it back in the requestScope, and uses the incremented value as the index into our list of CSS classes. If there’s no value, then the value’s 0 because we’ve never been called before, but we’ll store the 0 so it’ll be there next time we’re called.

Remember that the requestScope only lasts for the duration of an HTTP request, complete with page rendering, so we don’t need to bother resetting it. We just need to make sure we pick a suitable name for each place where we use the function.

Put the function in a Server Side JavaScript library, and import it as a resource. Now you can alter the xp:dataTable to start like this:

<xp:dataTable id="dataTable1" rows="100" var="rowData"
 value="#{viewAll}">
  <xp:this.headerClass>
    <![CDATA[#{javascript:statefullyIterate('crudehack', 
               ['one','two']);}]]></xp:this.headerClass>

And behold, we get

 <th class='one'>…</th><th class='two'>…</th>

just as the web standards police ordered.

Of course, the fact that headerClass is evaluated multiple times is an undocumented feature, so you probably shouldn’t rely on this approach. Hopefully IBM will provide a way to individually style th elements in a future release.

Posted by meta on 2013-04-05 | Posted in Domino, JavaScript | Tagged , , , | Comment

Notes 8.5.3: ⊕/⊖

Things that make me happy about Notes 8.5.3:

  1. I can export my database designs to plain files, and put them in Git for version control.
  2. I can grep all my source code with ack.
  3. The built-in editor will let me see all the code for a design object in one window.
  4. Hover-over display of documentation comments for functions.
  5. Eclipse-style error summary.
  6. Real HTML and XML generation capabilities.
  7. HTML, ECMAScript and CSS in the Notes client.
  8. Server and client side JavaScript. I look forward to the day when I never have to write any LotusScript ever again.

Things that make me unhappy about Notes 8.5.3:

  1. Still no Linux design client.
  2. Still no Mac design client.
  3. No way to display bookmarks as icons in a grid. (And no, the old legacy Workspace is not an adequate substitute.)
  4. I can’t believe they rewrote it from the ground up and didn’t make it fully multithreaded.
  5. Errors are often reported on the wrong line number. That is, server-side error messages give a line number that may be 3 or 4 lines off from the actual place where the error occurred.
  6. LotusScript code editor no longer autocompletes the end of a block when you open a block, so now I need to remember which keywords are used to end each different kind of block.
  7. LotusScript code editor doesn’t clean up indentation automatically.

Posted by meta on 2011-10-25 | Posted in Domino | Tagged , , | Comment