Search
Categories

Entries in Advanced (5)

Wednesday
12Aug2009

Adding content to the HTTP Header of an XPage

Sometimes it is necessary to add some content to the HTTP header of a servers response. Some of you may have already dealt with such before and therefore have wondered how this is accomplished with XPages. For those of you, who doesn't here's a small example why one would ever like to do that:

Suppose you want to tell the client the contents language you are about to send - this is done by the Content-Language header field. A more common approach is specifying how the browsers cache should handle this particular content (that is being allowed to cache on disk or to reload the URL every single time you access it), then you would want to use the Cache-Control header field. Those of you who have dealt with the specialties of Internet Explorer 8 may want to make IE8 behave like IE7 for some reason - which is done by the X-UA-Compatible header field and can make IE8 behave pretty much like an Internet Explorer 7. If you look at your Internet Explorers address bar you have certainly seen the sometimes appearing compatibility icon that can make a Website look better in IE8. The X-UA-Compatible header is about enabling that button rather than requiring the user to click it.

But how can one add such a header field with XPages? The answer is: very easy - you need only one line of code (but you will use more for better readability/maintainability of your code).
Let's have an example. We will take the IE7 compatibility field but the example can be easily customized to be used for other fields like cache directives or the like.

At first you will need to open the Events section of your XPage (or custom control if you want to reuse the code), navigate to the beforeRenderResponse event and switch to the Script Editor code type:



Then you need to add only the following line:
facesContext.getExternalContext().getResponse().setHeader("X-UA-Compatible", "IE=EmulateIE7");
This one single line will add the X-UA-Compatible header field and will place the content IE=EmulateIE7 to it. For the purpose of making IE8 behave like an IE7 this would be already enough, but since we want to have maintainable code, we will make the code probably a bit longer:

try {
  if (context.getUserAgent().isIE(8, 8)) {
    var response = facesContext.getExternalContext().getResponse();
    response.setHeader("X-UA-Compatible", "IE=EmulateIE7");
  }
} catch (e) {
  print("Something bad happened in the IE8 detection: " + e);
}

These few lines will not only handle any possibly occurring error and throw that to the servers console but will also place the header field only to responses generated to an Internet Explorer 8 browser - other browsers would ignore it anyway.

Friday
24Jul2009

Sample NSF of XPage Login Control

As requested by a couple of people, here is an NSF with a working sample from the Advanced In-Page Login Custom Control for XPages blog entry I posted a while back.

Wednesday
22Jul2009

XPages:Behind the Scenes


Over on the "Mindoo Blog", Karsten Lehmann has started an XPages series about stripping back some of the Notes-centric pieces of XPages and working with the underlying structure, java beans, and more.  While this is more than most Domino only developers may need to get into, the exposure to the underside of how XPages work, JSF, and so on is a great tool in understanding how XPages work behind the scenes, and can help you to design better XPages applications.

Hop over to the first article in his XPages series, and give them a read (5 articles in the series so far.) Its promising to be good stuff.

Happy Coding.

Wednesday
15Jul2009

Advanced In-Page Login Custom Control for XPages

Declan has a post on his blog on how to create a login window using a Dojo dialog box. Its a very nice example on how to do a quick login control using Dojo and AJAX to log in a user versus using the standard Domino style login.

I use a slightly different approach using an XPage custom control that allows me to have more control over the look of the login, so that I can integrate it with the site's overall look and feel, as well as use some dojo fadeIn/fadeOut animations to give it a little bit of extra flair. It also allows me to authenticate against a specific domino resource of my choice so that I can not only verify they are an authenticated user for the application, but also that they might have a specific role that allows them access to a particular resource.

To use the custom control, you just place it on the XPage where you want the "Log In" prompt to appear, set the control's properties, and customize the prompt's text through the use of some editable areas (facets).

Here is a description of the different properties:

loginText: This is the text you want to appear on the page as the link for the end user to click. I usually use 'Log In'

loginURL: This is the URL that the control uses to actually process the login. 99% of the time, its /names.nsf?login.

verifyAccessURL: After the control issues a successful login attempt to loginURL, the control validates access against this URL, verifying they user has access to what I want to redirect them to. If they don't have access, I tell them so.

logoutText: If a user is currently logged in, the control displays this text, instead of the loginText. I usually use "Log Out"

logoutURL: This is the URL to use for the logout. usually its /names.nsf?logout which takes them back to the default site for the domain/domino server. If I want to redirect them to a different page, then i might use /names.nsf?logout&RedirectTo=/mynew/url/page

successURL: This is the URL to send them to after a successful login and verification.

There are also 2 other properties which I haven't implemented yet, numberAttempts and failureURL, the idea being after numberAttempts failed logins, I'd redirect to failureURL.

In order for the control to work, there are two additional resources you'll need to add to your database. The first is a stylesheet that controls the look of the login window, and the other is a short script that sets up the controls on page load. There isn't alot to them, the control just needs them. If you want to change the look and feel of the control, then you modify the login.css. Both files are included in the download for the control at the end of the article.

Let's get down into the meat of the control so you can see how it works, and see how to develop a custom control.

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
style="display:inline;">

<xp:this.resources>
    <xp:script src="/startsHidden.js" clientSide="true"></xp:script>
    <xp:styleSheet href="/login.css"></xp:styleSheet>
</xp:this.resources>

Above is the first 8 lines of the custom control, pretty basic for any XPage. Two things to note off the bat, is first the xp:view has a custom style set to "display:inline". This allows the "Log In" link to appear right along with and next to any code or elements surrounding it, without causeing the margins to clear on either side. By default, a custom control renders as an XHTML div tag, which is a block element (clearing both margins). The other item to note is the inclusion of the startsHidden javascript and login.css stylesheet.

This particular version of the login custom control displays as a floating panel above the page. To help distinguish it from the rest of the page when it is displayed, I "lower the lights" on the rest of the page by creating a div that is 100% width and height, and black, then set the opacity to 70%. This keeps the user from clicking on other controls, and focuses their attention on the login. I call this the 'dither panel.' That is done with the next three lines of code:

<xp:panel
     styleClass="pnlDither startsHidden" id="pnlDither">
</xp:panel>

The properties of this panel are all controlled in the login.css, and later, i'll show you the code that makes it fade in and out with the login panel.

After that, the next bit of code creates the link on the page where the custom control is embedded.

There is a hefty amount of code in there, so I'll just explain and break down a little bit, download the code below and follow along if you like. The first block <xp:text>...</xp:text> creates the first part of the link, the <a> XHTML tag. If the user is logged in, the link's href is set to the logoutURL property. If the user is not logged in, the onclick of the anchor is used to execute some javascript that makes the dither panel, and the login panel itself visible, then animates them with a fade in. We can use a straight-forward dojo.fadeIn for the panel because we are going to display it at 100% opacity, however we want to stop the opacity of the dither panel at 70% opacity so we can see through it, so we use the dojo.animateProperty to animate the opacity of the panel from it's current setting (currently 0) to 70% (css: opacity: 0.7).

The next block of code, again an <xp:text></xp:text> block decides whether the user is logged in or not, and displays the appropriate text, either loginText or logoutText.

And the final block simply closes the XHTML anchor tag.

The next bit of code just lays out the actual login panel, called loginWindow. It uses several nested <xp:panel> elements (the XHTML equiv to div tags) to define the window, its title bar, etc. There are facets (editable regions) that allow you to set the text for the title bar, the welcome message, etc. I won't post all the code for it as it's lengthly, but its all in the download at the end of the article.

I will however go through the actual login script so you can see how the exchange actually happens.

The first part of the code simply posts a the username and password to the loginURL using dojo.xhrPost:

dojo.xhrPost({ url : '#{javascript:compositeData.get("loginURL");}',
handleAs : "text",
preventCache : true,
content: {
     "UserName": dojo.byId('#{javascript:getClientId("inpUser")}').value,
     "Password": dojo.byId('#{javascript:getClientId("inpPassword")}').value },

You can see in the second line how I retreive the value of the loginURL parameter using the inline XSP javascript command #{javascript: ... } that block is executed on the server side and the result is put back into the code in replacement. In this case the server executes compositeData.get("loginURL") to get the value entered in the parameter loginURL.

Next we have the load function for the xhrPost. This gets executed if the xhrPost is is successful, and data is returned. Here's the first part of that code:

load : function(response, ioArgs) {
    if (response.indexOf('reasonType')==-1) {
           dojo.xhrGet({

Here all we do is take the response back from the server, held in the variable 'response', and look for a string 'reasonType.' That is the key to telling if the login was successful. If it was not a successful login, then the returned HTML includes a variable reasonType indicating the type of error. So far, I haven't cared enough about what the actual error is with the login, so I display a generic message if it fails, shown below (but actually appears later in the script):

} else {
     dojo.byId('#{javascript:getClientId("loginErrorPanel")}').innerHTML = "You have entered an invalid username or password, or do not have sufficient credentials."; }

If the login was successful, I then proceed to actually check against the specific element that I want to veryfiy they have access to. This is done by issueing a dojo.xhrGet to the verifyAccessURL property, then checking again if the access attempt was successful. This time, i do not check for reasonType, as it has caused me a few problems here and there in the past that are really irrelevent to this code. Instead I check for the string 'action="/names.nsf?Login"' that appears in the <form> tag of a login prompt. If the check there is successful, then I redirect the page to the value of the successURL parameter, if not, I force a logout, display my standard message, and have them try to log in again. Here's most of that code:

dojo.xhrGet({
  url : '#{javascript:compositeData.get("verifyAccessURL");}',
  handleAs : 'text',
  preventCache : true,
  load : function(response, ioArgs) {
    if (response.indexOf('action="/names.nsf?Login"')==-1){
      document.cookie = "BackupSesID=; expires=Thu, 01-Jan-70 00:00:01 GMT";
      window.location.href = '#{javascript:compositeData.get("successURL");}';
    }else{
        dojo.xhrGet({
            url : '#{javascript:compositeData.get("logoutURL");}',
            handleAs : "text",
            preventCache : true,
            load : function(response, ioArgs) {
                   if (document.cookie.indexOf('BackupSesId=')!=-1){
                       document.cookie = "DomAuthSessId=" + document.cookie.split('BackupSesID=')[1].substr(0, 32);
                   }
                   document.cookie = "BackupSesId=; expires=Thu, 01-Jan-70 00:00:01 GMT";
                   dojo.byId('#{javascript:getClientId("loginErrorPanel")}').innerHTML = "You have entered an invalid username or password, or do not have sufficient credentials.";
            },
            error : function(response, ioArgs) {
                 dojo.byId('#{javascript:getClientId("loginErrorPanel")}').innerHTML = "You have entered an invalid username or password, or do not have sufficient credentials.";
            }
         })
      }
   },
   error : function(response, ioArgs) {
              dojo.byId('#{javascript:getClientId("loginErrorPanel")}').innerHTML = "You have entered an invalid username or password, or do not have sufficient credentials.";
   }
})

That's pretty much it in a nutshell. Download the Zip with all three elements, the custom control, the css and the javascript (which needs to be created a a javascript script library in your database) from my blog, and let me know if you have any questions!

Happy Coding!

Update: Here is a link to a flash movie of the control in action (sorry about the poor quality)

Wednesday
15Jul2009

Using Themes In XPages Part Three

In the last part of this series I showed you how to add <control> sections to your theme document so that default properties could be set for different controls that you add to your XPage application. I also mentioned that you can override the default properties by manually adding in your own properties to the control.

If you have written any XPage applications yet you will probably have seen the 'All Properties' section for each control where you can manually set the styleClass and whatever other properties are exposed but there is one property that I have yet to see anybody talk about and that is the 'themeID'.

Lets say you have decided that you are going to use 'Link' controls with the 'lotusBtn lotusBtnSpecial' outerStyleClass, that is part of the oneui theme, for all the action buttons in your application. You would not want to create a special <control> section in the theme file for the link control because it would apply to all link controls on your page. Instead you can define your own control name like this

<control>
<name>Link.Action</name>
<property>
<name>outerStyleClass</name>
<value>lotusBtn lotusBtnSpecial</value>
</property>
</control>

Now in your application when you add a link control that you want to be an action button, instead of having to remember the different style classes that you need to use you can set the themeID property to 'Link.Action' and the XPages renderer will be able to work out what properties to set by looking at the theme file.

Using Themes In XPages Part One
Using Themes In XPages Part Two