Getting the Base URL of the Current Database with SSJS

It’s easy to get the current page URL in SSJS with context.getUrl().toString(). However, it’s a little less straightforward if you just want to determine the base URL of the current application. This can be useful when you need to build a link to some other page within the current application and send it out in an e-mail notification. In this post, I’ll look at the results of some methods in the XSPUrl object and show how to achieve the desired effect.

In the last post, I looked at URL @functions in the Extension Library. There are some useful functions there, but none that retrieve what I’m looking for.

XSPUrl

The global context object has a getUrl() method that makes it easy to get a handle to an XSPUrl object representing the current URL. You can retrieve the full URL (including the querystring) or use any of the methods available to get pieces of the URL ( getAddress(), getHost(),getParameter(), getPath(), getPort(), getQueryString()).

Here are some examples of the output, given this url: http://127.0.0.2/BlogTesting.nsf/URL.xsp?parameter1=a

Method Result
context.getUrl().toString() http://127.0.0.2/BlogTesting.nsf/URL.xsp?parameter1=a
context.getUrl().getPath() /BlogTesting.nsf/URL.xsp
context.getUrl().getAddress() http://127.0.0.2/BlogTesting.nsf/URL.xsp
context.getUrl().getHost() 127.0.0.2
context.getUrl().getSiteRelativeAddress(context) /URL.xsp

There are several handy methods, but none providing exactly what I’m looking for.

Options for Getting the Base URL

The getAddress() method gets the URL up to, but not including, the querystring. However, it still includes the current page name. The global view object has a getPageName() method that returns the name of the currently-displayed XPage.

The snippet that I’ve been using recently uses the getAddress() method and replaces the current page name with an empty string, leaving us with the base URL:

context.getUrl().getAddress().replace(view.getPageName(), '')

You could just as easily use Javascript’s substr or substring methods to look for the ‘.nsf’ and only retrieve characters up to the end of it in most cases, but that may be problematic if you have routing that masks the NSF name.

What’s Your Solution?

This seems like something that probably has a number of workarounds. How have you solved it? Is there something simpler that I’ve overlooked?

Update

Check out David Leedy’s XPages URL Cheatsheet for more URL-related functions http://xpagescheatsheet.com/cheatsheet.nsf/url.xsp

There are two listed on there that sounded like they might provide what I was looking for, but did not.

  • database.getHttpURL() – includes the database replica ID rather than name and appends ?OpenDatabase to the end
  • view.getBaseURL() – sounds perfect, but doesn’t return anything

URL @Functions in the Extension Library

There are a few @Functions for working with URLs available in the Extension library. They can come in handy if you need to work with URLs related to any element in your database.

Availability

These functions are part of the extension library. They’re available if you’ve installed the Extension Library, or if you have Upgrade Pack 1 for 8.5.3. They’re also part of the extension library features built directly into Notes9.

@FunctionsEx

The functions are available on the Reference tab in the SSJS Script Editor. Under Libraries, select @FunctionsEx to see the additional functions.

ExtLib URL Functions - 1

The following URL functions are available:

  • @EncodeUrl() – Encodes spaces and symbols in the URL
  • @FullUrl() – Displays the full path to a resource in the database (relative to the server)
  • @AbsoluteUrl() – Displays the absolute path to a resource in the database*
  • @IsAbsoluteUrl() – Boolean value for whether a given URL is absolute

Examples

Here are some examples of how they can be used in an application:

Code Result
@FullUrl(view.getPageName()) /BlogTesting.nsf/ExtLibURLFormulas.xsp
@AbsoluteUrl(view.getPageName()) http://127.0.0.2/ExtLibURLFormulas.xsp
@EncodeUrl(context.getUrl().toString());

Where the url is: http://127.0.0.2/BlogTesting.nsf/ExtLibURLFormulas.xsp?my_Parameter=spaces and $ymbol$
http://127.0.0.2/BlogTesting.nsf/ExtLibURLFormulas.xsp?my_Parameter=spaces+and+%24ymbol%24
@IsAbsoluteUrl(view.getPageName()) false
@IsAbsoluteUrl(context.getUrl().toString()) true

*@AbsoluteUrl

I generally use context.getUrl() and work with that to parse and build URLs, so I haven’t run into this before, but the output of @AbsoluteUrl() wasn’t what I expected. At first glance, it appeared to give the full url to the specified design element, but it actually just gives the protocol, server, and design element name that was supplied.

It looks like I can get the real absolute url to the design element by combining @FullUrl() with @AbsoluteUrl()

Code Result
@AbsoluteURL(@FullUrl(view.getPageName())) http://127.0.0.2/BlogTesting.nsf/ExtLibURLFormulas.xsp

XPages Tip: Getting the Value of the Current Field with SSJS (@ThisValue equivalent)

Sometimes, I miss the convenience of several @functions. If you’re writing SSJS code that needs to access the value of the current field from an event handler on that field, but you don’t want to hard-code the component name (so you can easily reuse it), it would be handy if there was an @ThisValue equivalent. Alas, there isn’t, but, fortunately, you can still easily access the value.

this

The special this keyword is where we need to start. It provides a handle to the current object.

When running code from an event handler, the object type is: com.ibm.xsp.component.xp.XspEventHandler

I find this by using the typeof operator in JavaScript

print (typeof this);

getParent()

The event handler doesn’t have a value — we need to get to the parent component that contains it. That’s where the getParent() method comes in handy. As you would expect, it goes up the chain and gives us a handle to the parent object, which for my sample input field is: com.ibm.xsp.component.xp.XspInputText

This object has a getValue() method to return the value of the component.

Getting the Current Field Value

Putting this together, I can easily get the value with this line:

var thisValue = this.getParent().getValue();

FYI — if you want the ID of the parent control from an event handler, you can use this line:

var thisID = this.getParent().getId();

XPages Tip: Select a Working Set in Package Explorer (and Navigator)

If you’ve been using Domino Designer for any length of time, you’re likely aware that you can group your applications into Working Sets in the Applications view. But did you know that you can do the same thing in the Package Explorer and Navigator views?

Working Sets in the Applications View

Here’s what it looks like to select a working set in the Applications view in DDE:

Blog - Package Explorer - 1

You can choose one or select the Multiple... option to select multiple working sets.

Working Sets in the Package Explorer View

Until recently, I didn’t realize that I could make a similar selection in the Package Explorer view. I always just saw it as annoying that I had to dig through every NSF I’ve worked on until I looked around for this option.

If you click on the icon with 3 horizontal gray lines, you get this drop-down:

Blog - Package Explorer - 2 - None Selected

You can select a single working set or choose the magic Window Working Set option to have the Package Explorer databases automatically updated based on your working set selection in the Applications view.

Blog - Package Explorer - 3 - Window Working Set Selected

If you want to disable this option and show them all, you can select Deselect Working Set from the menu.

Working Sets in the Navigator View

Interestingly, you can also choose a working set in the Navigator view, but the options are more limited; there’s no option to link it up with the current working set.

Blog - Package Explorer - 4 - Navigator

XPages Tip: Fixing a partial refresh issue with the Pager Add Rows control in a Data View

I came across a strange issue with a Data View using a Pager Add Rows control resetting the number of documents displayed after a partial refresh. In this post, I’ll explain the scenario where I saw the issue and show the simple solution that fixed the problem.

The Problem

I have a Data View that starts with 20 rows and uses a Pager Add Rows control to add 10 more rows at a time.

Within the summary facet, there’s an action that can take place, which runs a minimal script and then performs a partial refresh with partial execution on the summary section.

This *seems* to be fine when performed within the first set of rows, but I noticed that if additional rows are added to the display and then the action is run in any one of the rows, the action runs successfully, but the data view is reset to only display the initial set of 20 rows.

If the action was taken on a row after the initial 20, I can add rows again and see that the action has taken place and the partial refresh has occurred as designed.

The Fix – Changing the ‘State’ Property

I tried making a number of changes to partial refresh and partial execution settings, but none of them fixed this issue.

Ultimately, the issue was fixed by a simple property change. The Pager Add Rows control has a state property (All Properties > basics > state)

When set to true, it stores the view state on the server.

Pager Add Rows - State Property

This did the trick. It allows the row count to be preserved between partial refreshes by storing it on the server.

Monitor Source Control Synchronization with the Console View in DDE

If you’d like to monitor the synchronization of an application with the local source control repository, you can use the Console view in Domino Designer. In this post, I’ll show different kinds of helpful information that the view displays.

Console View

If you’re using source control, the Console view displays information about the synchronization of design elements between the NSF and the (source control) on-disk repository.

This can be helpful to see what’s going on if your DDE client is responding slowly (and may help you decide whether you want to enable or disable automatic synchronization). It may also be helpful in tracking down the creation of the dreaded .orig files.

Opening the Console View

Select Window > Show eclipse views > Other...

Blog - Console Sync - Add 1

Under General, select Console

Blog - Console Sync - Add 2

Console Output

When you open an application in DDE, you’ll see the console start the synchronization process. (There’s even a misspelling to keep the grammar cops on alert.)

Console - Show initial synch

When you update an existing design element, you see that it exports the change to the on-disk repository (for the .xsp and related .xsp-config file).

Console - Show Element Modified

When you add a new design element, it creates the .xsp, .xsp.metadata, and .xsp-config files in the on-disk repository.

Console - Show Element Added

Automatic Synchronization

If you have automatic synchronization enabled, it will happen when the application is built. (If you have Build Automatically enabled, it will happen automatically. Otherwise, it’ll happen when you build the application.)

These two properties in the Domino Designer preferences determine whether synchronization is automatic:
Console - DDE Prefs - Auto Sync

 

Simple Sync Conflicts

If you’ve seen a popup like this, then there is a synchronization conflict:

Blog - Console Sync Conflict

Here’s the corresponding message in the Console view:

Nsf file AppProperties/database.properties and disk file AppProperties/database.properties both have been updated since last sync or are never synced:Wed Aug 20 20:17:36 EDT 2014

It indicates a conflict and it’s making it easy for you to choose how to resolve it. You can also see evidence of this in the Console view. I tend to see this often with the database.properties (because the time stamps are always being modified). This type of popup is displayed when you synchronize with source control before opening the application in DDE. (If you modify the same element as someone else, conflicts will show up differently when you try to commit changes within your source control plugin or application.)

One View – Multiple Uses

The Console view is displayed as Console (DDE Sync Console) when displaying source control sync information. You can also switch it to be used as a Java stack trace console by via the Display Selected Console icon.

Computing Custom Date Format Patterns Throughout an Application

If you want to ensure consistency in date formatting throughout your application, you can define the pattern for each Edit Box or Computed Value control that displays a date. What you may not have considered is that you can compute the date format, so you can define it in one place and use the same format everywhere. In this post, I’ll show how you can set it up and even change it conditionally.

Locale Conversions

The browser will automatically attempt to format dates based on your specified locale. Here’s an example of a computed field set to display a date. The Display type must be set to Date/Time.

Date Format 1a Default Date Format - Settings

Here’s how it displays in my browser when the language is set to English:

Date Format 1b Default Date - English

Here’s how it displays when I change my browser language to French:
Date Format 1c Default Date - French

Custom Date Patterns

If you want to change the default formatting, you can define a custom date format pattern.

To define a specific pattern, change Display format to Custom and then you can edit the Pattern field. (You can select a pre-defined pattern or enter your own.)

Date Format 2 - Custom Date Format

In this case, the pattern will stay the same, regardless of the browser language.

(Note: When defining a date pattern, the month value uses one or more capital M’s, because the lower case m is for minutes when defining a time pattern.)

Computing Date Patterns

Just like most other properties in XPages, there’s a blue diamond next to the Pattern field, allowing you to use SSJS to compute it. This gives you the opportunity to create a library function to return some pattern to use. Then, you can set all of your date controls to call the same function and modify the pattern in one place.

Your library function could be as simple as this:

function getDateFormat() {
  return 'dd-MMM-yyyy';
}

Taking it one step further, you could add logic in the function to conditionally determine the pattern, whether it be based on a user preference or a locale or just changing date formatting patterns on a daily basis because you like to mess with your users.

If you use more complex logic, you may also want to consider storing the pattern in an applicationScope variable, so it’s available throughout the application.

function getDateFormat() {
  if (!applicationScope.dateFormat) {
    // Logic to determine date format
    applicationScope.put('dateFormat', some_date_format);
  }

  return applicationScope.dateFormat;
}

Computation Timing

The default setting for the logic Compute Dynamically (#). If you leave it that way, it will throw this error when you load the page: The value of the property pattern cannot be a run time
binding.

To fix this, select Compute on Page Load when entering/editing the snippet or change the octothorpe/pound sign/hash (#) to a dollar sign ($) in the page source, because date pattern computations only work on page load.

Compute Date Formats - 3

Quick Replace

If you’ve already set up hard-coded patterns on date fields, they would look something like this:

You can do a quick search and replace to modify them all easily.

Search for:

pattern="dd-MMM-yyyy"

Compute Date Formats - 4a

Click the Replace... button rather than the Search button.

Then replace with:

pattern="${javascript:return getDateFormat();}"

Compute Date Formats - 4b

Follow

Get every new post delivered to your Inbox.

Join 53 other followers