October 28, 2010

[XPages] Tip When Using CKEditor On Site Behind IIS

A quick tip if you are building an XPages site that is placed behind IIS.
When you are using the CKEditor (default RichText editor in 8.5.2), remember to edit your plugin-cfg.xml to direct requests to  "/ckeditor/*" to the Domino server.

This is achieved by adding a line to plugin-cfg-xml looking like this:
<Uri Name="/ckeditor/*" />

Hints that this is required include the faact that the rich text editor is not displayed and that your browser displays error messages like this:



For more info on the plugin-cfg.xml file and XPages, have a look here and here.

October 4, 2010

HTTP JVM: com.ibm.xsp.acl.RedirectSignal - What does it imply?

After upgrading the Domino server to 8.5.2, we've occasionally been getting entries like these in log.nsf on the server:

04.10.2010 14:07:39 HTTP JVM: com.ibm.xsp.acl.RedirectSignal
04.10.2010 14:07:50 HTTP JVM: com.ibm.xsp.acl.RedirectSignal
04.10.2010 14:09:08 HTTP JVM: com.ibm.xsp.acl.RedirectSignal


I've been trying to ask Google for help on this, but without any luck. In fact, if you google the phrase "com.ibm.xsp.acl.RedirectSignal", you get a single hit (!): http://www.timtripcony.com/xpages_ext_api_javadoc/serialized-form.html


Does anyone of you Lotus Notes experts out there have some more info on this topic?

Might not be a big problem, but I'm a bit paranoid when it comes to entries in the log.nsf... :-)


Update: I've also created a topic at the Notes 8.5 forum over at lotus.com - LINK

XPages: Edit multi-value fields in existing documents - A frustrating battle!

It's been a while since I last wrote something on here, I'm blaming it on a very busy schedule, but now there's something I would very much like to share with the rest of the XPages Community: During the last couple of months, I've been (amongst other things) working on implementing an "edit news article"/"edit user profile" feature on website I'm creating. I've previously developed a "create news article". This XPage contains the usual "heading", "lead paragraph" and "body" fields, but also some multi-value fields to select category and target audience for the news article.

The creation of these multi-value fields were fairly straight-forward, and creating news articles worked like a dream. However, trying to edit these fields on an XPage was to be a more painful experience. Changing the values and hitting the save button on the site would not have the desired effects on the saved Notes document, the multi-value fields would remain unchanged. The whole thing was getting very frustrating, but fortunately there are some explaining and helpful articles out there on the web:


  • Sean Cull has created a page that demonstrates the the differences between a field's value and submitted value. Take a look here.
  • IBM have written something themselves about the topic, have a look here.
  • YouAtNotes.de writing about getting and setting field values and value bindings here.
I hope these links will prove to be as helpful to some of you as they were to me!

June 25, 2010

XPages: XSP Unit testing framework - anyone tried it?

After being "brainwashed" by Uncle Bob and Roy Osherove for 3 days about test driven development, I came back to work eager to try to use some of these techniques in my daily work with XPages. A quick Google search pointed me in the direction of XSP Unit, a "simple framework for xUnit style unit testing of Javascript in XPages Applications" developed by Lorcan McDonald.


My first impression is positive, but I was wondering if anyone else in the XPages community are currently using this framework?
What are your experiences?
Is it worth the hassle to set it up and start using it?

June 14, 2010

XPages: Default Value for Repeat Limit in Repeater Control Equals 30 ?

Last week, I stumbled upon a mysterious bug in XPages. I've created quite a few pages and custom controllers with repeater controls during the last months, but never seen this before:

When I didn't specify a repeat limit for the control, I would only get the first 30 entries from the data source!

I've developed the habit of not setting the repeat limit when I don't need it, believeing that it would provide me with all the entries from the data source. Checking the provided documentation in the Domino Designer comfirmed my thoughts:


Has anyone else experienced this bug? Or is there another way to limit the repeat control, that I might have done unintentionally?

For most cases, the work around is quite easy, just set the repeat limit field to some high value that you (probably?) will never exceed, but in some cases you might not know in what range the number of view entries will be when your application has been in use for a while...

June 8, 2010

XPages: Modifying Button Text in File Upload Control?

I'm currently working on create a page, using XPages, where the users amongst other things are able to upload files. Simply dragging and dropping a File Upload Control onto the page works fine, but I would also like to modify the text on the browse button that is automatically provided by the Dojo framework.

It seems to me that the text on the button is set to the default "browse text" of the system. On other buttons on the page, e.g. the submit button, I am able to set the label of the button (see picture below, which is then displayed as the button's text. This does not work for the browse button...

Label field of an XPages Button, where is the same field for a File Upload Control?


Does anybody know what I need to do to be able to set the text of the file upload control button to whatever string I want to?

May 25, 2010

Trouble Saving a Notes Document from Web

I'm currently working on re-implementing the customer's intranet site. As part of this work, I'm at the moment developing a page that let the users publish news to the site.

Reading different blogs around the web, the way to do is is fairly straight forward:
  • Add a data source to your xpage, select a form, and set it to create a document when the page is loaded
  • Add some fields and bind them to fields in your form.
  • Add a button at the bottom of your page, and add a Simple Action event ("Save Document") to it.

This work was completed in a few minutes and I successfully tested the page in Firefox.  I was about to run around the office in celebration, when I reminded myself to verify that it works in Internet Explorer 7 (the browser of choice at the customer's office) as well... That's when the trouble started!

Clicking the save button in IE7, the only thing happening is that an error message is displayed:
'document.forms[...].$$xspsubmitid' is null or not an object'
Inspecting the source code of the Xpage, there are a few interesting parts. When creating an Xpage with a submit/save button, the following HTML is automagically created and included in your page:
(The id of my save button is "view:_id1:button1")

As I click the save button in Firefox, I can see through Firebug that the DOM is actually changed as the onClick event of the button is fired. The code snippet now looks like this:

As far as I can see (through Firebug Lite), the same changes are not made in Internet Explorer 7. I have just started experimenting with this part of XPages (submitting/editing documents from Web), but I am assuming that this is what causes the trouble, as the Xpage does not know what part of the page to submit to the server side.

Are there anyone else out there experiencing the same problems?
I have made attempts to dive into the XPages event handling, but I have yet to get to grips with it.

Unfortunately, I have not been able to test this page in other browsers yet. Due to security issues, I am developing the XPages application on a computer provided by the customer, with insufficient rights to install additional software.


UPDATE:
The source code that creates the problem:
XPAGE SOURCE CODE

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core" xmlns:xc="http://www.ibm.com/xsp/custom">
    <xp:this.resources>
        // Removed included js and css files
    </xp:this.resources>
    <xp:this.data>
        <xp:dominoDocument var="newsDoc" formName="NEWS">
        </xp:dominoDocument>
    </xp:this.data>

    <xc:global_linkjs />
    <div class="main xpNyhet">
        <div class="mainInner">
            <div class="content">
                <div class="page">
                    <div class="pageCenter">
                        <div class="pageContent">
                            // Removed code to generate entry fields and bind them to fields in the document (by drag-and-drop from Data palette)
                            <xp:button value="Lagre dokument"
                                id="button1" save="true" execMode="complete">
                                <xp:eventHandler event="onclick"
                                    save="true" submit="true" immediate="false" refreshMode="complete">
                                    <xp:this.action>
                                        <xp:actionGroup>
                                            <xp:saveDocument></xp:saveDocument>
                                            <xp:openPage>
                                                <xp:this.name><![CDATA[#{javascript:var mid = new CGIVariables().getURLParam("mid");

                                                    if (mid) {
                                                        return "/forsideSpin.xsp?mid=" + mid + "&login";
                                                    } else {
                                                        return "/forsideSpin.xsp?login";
                                                }}]]>
                                                </xp:this.name>
                                            </xp:openPage>

                                        </xp:actionGroup>
                                    </xp:this.action>
                                </xp:eventHandler>
                            </xp:button>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</xp:view>

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

HTML SOURCE CODE (from IE7)

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="no">
<head>
    // Removed content here
</head>
    <body class="xspView tundra">
        <form id="view:_id1" method="post" action="/weblink/FELLES/spinlokalnyheter.nsf/lagNyhet.xsp?mid=spin&amp;login" class="xspForm" enctype="multipart/form-data">
            <div class="content">
                <div class="page">
                    <div class="pageCenter">
                        <div class="pageContent">
                            // Removed content here. Entry fields bound to fields in the doucment form.
                            <button id="view:_id1:button1" class="xspButtonSubmit" type="button" name="view:_id1:button1">Save document</button>
                        </div>
                    </div>
                </div>
            </div>
            <input type="hidden" name="$$viewid" id="view:_id1__VUID" value="!cjwesc81oc!">
            <input type="hidden" name="$$xspsubmitid">
            <input type="hidden" name="$$xspexecid">
            <input type="hidden" name="$$xspsubmitvalue">
            <input type="hidden" name="$$xspsubmitscroll">
            <input type="hidden" name="view:_id1" value="view:_id1">
            <script type="text/javascript">
                function clearFormHiddenParams_view__id1(curFormName) {
                    var curForm = document.forms[curFormName];
                }
            </script>
        </form>
        <script type="text/javascript">
            XSP.addOnLoad(function() {
                XSP.attachEvent("view:_id1:_id90", "view:_id1:button1", "onclick", null, true, false);
            }); 
        </script>
    </body>
</html>

April 26, 2010

XPages and Caching

Xpages provides you with the functionality to store key/value-pairs in different scopes: request, session, application and view. However, there is no expiry date on this data, at least by default.

I stumbled upon two great blog posts on how to implement a simple, but in many cases more than good enough, caching technique for your XPages variables.

Take a look at Matt White's blog entry or YouAtNotes' blog entry to get all the details!

Also, the rest of their blogs are strongly recommended for excellent XPages tips!

April 16, 2010

Extending the XPages Type Ahead Functionality

Xpages comes with a built-in type-ahead functionality for text input fields for your forms. This functionality is working OK straight out of the box, as long as you don't want to do anything too advanced...

There are several other blog entries out there that explain the basic and some more extended usage of the Type Ahead feature:keithstric.com, Lotus.com, DontPanic82. These entries (and others) all provide great information to get you started, but they didn't quite suit all my needs...

My task was as follows:
  • Develop a type ahead functionality for a company's employee search
    • Large number of employees (have to do a work-around for Notes' @DbLookup 64Kb limit).
  • You should get suggestions based on both first names and last names
    • Typing "and" in the input field should provide both "Andy Johnson", "John Anderson" and "Randy Thompson" as suggestions. (the built-in feature would only do a search for "and" or "and*", not "*and*")
Time to get started on some hacking on my own!


I started off by putting up the basic structure: add an Edit Box from the "Core Controls" menu in the Domino Designer and enabling the type ahead functionality from its property panel. By now, switching to the source view in the Designer, you should see something like this:


Left click somewhere within the xp:typeAhead tags to see all the type Ahead properties in your properties panel. I've ended up with these settings:

The important attributes are "minChars", "partial", "valueList", "valueMarkup" and "var". As long as you do your own computations (which we will discuss in a minute) to find the type ahead suggestions, the setting for "ignoreCase" doesn't matter.
  • minChars: the minimum number of characters that need to be in the input field to start looking for suggestions. I've set this property to 2, as keeping it at 1 (the default) will get me too many suggestions, while setting it to 3 or more kind of defeats the purpose of the type ahead. Set it to whatever suits your task :-)
  • mode: The AJAX request mode. Set it to "partial" to avoid a full page reload.
  • valueMarkup: setting this field to "true" tells the Designer that you will be taking care of creating the markup for the list of suggestions.
  • var: the name of the variable that contains the string entered in the input field. This variable is then accesible when we will create our suggestion list, which brings us to...
  • valueList: Now, this is where the magic happens! This is where you enter your code, so that you get the type ahead suggestions you want presented in the way you want it.

The code I ended up with in the "valueList" property is shown in its full below:

    //Getting the view containing a document for each of the employees
    var searchView:NotesView = session.getDatabase("","myemployees.nsf").getView("employeesTypeAhead");

    // Creating a Lotus Notes search query. Notice the reference to lupkey!
    var query = "(FIELD LastName CONTAINS *" + lupkey +"* OR FIELD FirstName CONTAINS *" + lupkey +"*)";

    // Creating an array to store hits in
    var searchOutput:Array = ["å","åå"];

    // Doing the actual search
    var hits = searchView.FTSearch(query);

    var entries = searchView.getAllEntries();
    var entry = entries.getFirstEntry();

    //Sort the array manually, since Notes doesn't want to sort them alphabetically
    for (i=0; i<hits; i++) {
    searchOutput.push(entry.getColumnValues()[0]);
    entry = entries.getNextEntry();
    }
    searchOutput.sort();

    // Build the resulting output HTML code
    var result = "<ul><li><span class='informal'>Suggestions:</span></li>";

    var limit = Math.min(hits,20);
    for (j=0; j<limit; j++) {
    var name = searchOutput[j].toString();
    var start = name.indexOfIgnoreCase(lupkey)
    var stop = start + lupkey.length;
    //Make the matching part of the name bold
    name = name.insert("</b>",stop).insert("<b>",start);
    result += "<li>" + name + "</li>";
    }

    result += "</ul>";
    return result;

    OK, so I want you to pay special attention to a few things...
    • For the search to work, you should make sure that the documents in the view have fields named "LastName" and "FirstName", or change the code to your needs.
    • By adding a "*" at the start and end of the lupkey variable in my search query, I'm able to get a partial match on any substring in the field values.The built-in feature will only allow partial match from the start of the field value (i.e. searching for "lupkey*").
    • When you do the FTSearch, the entries in the view will automatically be sorted by their search score (some mysterious score calculated by Notes showing how "relevant" the entry is for your query). Since I wanted my results sorted alphabetically, I had to take care of that myself.
    • The return value of the script must be an unordered list (see here for further information)

    Welcome

    I created this blog to share my highs and lows while developing an XPages application.