Do you have a Bootstrap navigation menu in XPages that collapses properly in a full browser but not on a mobile device? You may need to set a meta tag to force it to display properly. In this post, I’ll show the effects with and without the tag on a mobile device.
One of the great features of Bootstrap is its built-in responsive UI that scales well to work on screens of varying sizes. The navigation bars collapse nicely into the hamburger menus to make better use of available space on smaller devices.
However, in recent testing of an XPages application with a Bootstrap UI, it was noted that the navigation menus did not collapse properly when viewed on a phone. It was a little confusing because it collapsed as expected on a full browser.
When the navigation bar is set up properly, it should collapse properly on its own when the screen width is below 768 px. I verified that there are no CSS rules overruling that cutoff and that 768px is, in fact, the point where the menu collapses on the full browser.
Bootstrap Navbar Test Page
To test it out, I tried out the Bootstrap Navbar test page
On my laptop with a full browser, it showed the navigation normally.
When I shrunk the screen to be less than 768px wide, it collapsed the navigation properly.
When I checked it on my phone, it was collapsed properly.
Test Page in XPages
I copied the div that contains the navigation from that test page and pasted into an XPage in my application to see how it worked.
It collapsed as expected in a full browser but not on the phone.
It is also apparent that the page has been scaled so that it fits fully on the phone.
To verify this, I put a button on the page to tell me the screen width. As expected, it showed a width > 768 px, which is why it did not collapse the menus. It scaled the entire page to fit on the screen, so it did not fall below the threshold of the responsive design media queries.
(This is on a Samsung Galaxy. An iPhone showed it to be roughly 900px.)
And this is just a simple page. Imagine how that looks with a much bigger XPage!
Ultimately, the main difference is that the Bootstrap test page contains a meta tag to make sure the device doesn’t shrink to fit the entire page on the screen.
<meta name="viewport" content="width=device-width, initial-scale=1">
In XPages, this can be set at the page level, but it’s easiest to do it application-wide via a theme, within a resources tag.
<resources> <metaData> <name>viewport</name> <content>width=device-width, initial-scale=1.0</content> </metaData> </resources>
Now, when I reload the Bootstrap test page, it is displayed properly on the phone.
The button showing the screen with on the phone now shows it to be 360px wide.
I recently spent some time investigating a client’s reports of unexpected behavior with the duration of browser sessions while testing an application on a test server. From time to time, they were required to login even while actively using an application. In this post, I’ll highlight the difference between an idle session timeout and an LTPA token expiration, which serve different purposes and, in the latter case, may cause frustration if not understood.
Most users are familiar with the concept of a browser session timing out if left idle for too long. In this case, websites will generally inform the user that a session has expired and require the user to login again in order to continue.
But users will generally not expect to be required to login again while actively using an application, so it’s important to understand why it might happen and what you can do about it.
Idle Session Timeout – Server
The Domino server document has a setting to define how long it will take for the session to be automatically logged out due to inactivity. This is configured on the server document:
Internet Protocols... > Domino Web Engine > Idle session time-out
The default is 30 minutes.
Idle Session Timeout – Application
There is also an application-level setting for the session timeout, which can be found on the
General tab of
LTPA Token Timeout
If single sign-on is configured to share the session between multiple servers, a Web Configuration document will define the SSO parameters.
The key setting in this case is the
Expiration (minutes) field on the
Basics tab of the document. This defines the lifespan of the LTPA token that is issued when the user logs in.
The important thing to understand is that this has nothing to do with how active or idle the session is.
This is a fixed length of time for which the key will be valid. Once it expires, the user will be prompted to login again. This can be very confusing to a user who is actively using the application!
Improving the Experience
There are a number of ways to implement controls to keep a session from timing out due to inactivity, but they will have no effect on the expiration of the LTPA token.
In order to prevent users from being frustrated with frequent logouts, some very smart people including Per Lausten and Sean Cull, have written about this in years past and have recommended setting the token expiration to a much larger number in order to prevent unexpected behavior. The idle session timeout can still do it’s job dealing with inactive sessions (and you as a developer can programmatically work to keep them alive if desired).
Custom properties are extremely useful for modular design and reuse by sending custom values into each instance. There are a number of data types that can be selected, but no obvious way to pass an array value. In this post, I’ll describe the issue and how to work around it.
Custom Property Data Types
The default data type for a custom property is
There are a number of primitive data types available in the dropdown:
String Data Type
In this case, I want a property that can accept an array of values.
To test this, I created a simple custom control with two properties,
objectProperty, to test how they handle arrays. They both default to
string as the data type.
You can leave the data type as
string, but compute an array to return. It won’t throw an error, but it will treat it like a single string.
No Data Type
You can remove the data type from the property definition and it won’t throw an error on the custom control. However, that is not a valid property, so its treated as though that property doesn’t exist.
If you’ve already set up an instance of the custom control and passed a value to it, it will throw an error on the custom control instance: Unknown property this.. It is not defined on tag .
Custom Data Type
Fortunately, there’s a really simple solution — you can manually type in a property type.
If you type in
object as the type, it does the trick. (It effectively works as desired, although it actually accepts the data as
TL;DR – Server-side code in onClientLoad causes a page refresh and prevents additional onClientLoad events with server-side code from running. In this post, I’ll show an example and describe how it behaves.
Page Ready Events
$(document).ready() and Dojo has
dojo.addOnLoad() to make this checking easy.
The Problem with onClientLoad in XPages
XPages provides the
onClientLoad event on pages, custom controls, and panels. XPages allows you to run client-side and/or server-side code in the event. However, running server-side code can cause a tough-to-troubleshoot side effect.
Take, for example, this XPage, which has two panels. Both panels and the page itself have client-side and server-side
onClientLoad code to write a message to the browser console and server console, respectively.
Here’s what happens when the page loads:
- The page-level client-side code runs to write the message to the browser console
- The page-level server-side code runs to write the message to the server console
onClientLoad handlers stops at this point because the page has posted to the server because of the server-side code; the
onClientLoad event handlers for the panels do not execute at all.
You can look in the browser’s developer tools and see the POST that is sent. It doesn’t try to update anything on the page, so there’s no response, but it’s still enough to interrupt everything else.
More details on the behavior:
- You can run multiple client-side
onClientLoadevent scripts without issue — as long as they’re not triggering partial refreshes
- If the event handlers are rearranged on the page, the one that comes first in the source is the one that will run
- If there is only one
onClientLoadevent with server-side code, it will run (based on it’s order in the source), then POST, then let the rest of the
onClientLoadevents on the page that only have client-side code run. (Any with server-side code will not execute — even the client-side event code on handlers that also have server-side code will not run.)
Mitigating the Problem
This may sound like an easy thing to keep in check, but if you have
onClientLoad code on multiple custom controls, it may be hard to make sure that there won’t be a conflict, especially because there won’t be any error thrown.
The strong recommendation is to just not put server-side code in the
onClientLoad event, period.
If you absolutely need it, then you may need a standard of putting any necessary
onClientLoad code in the common layout control that’s loaded on every page, but it still has the potential to interrupt anything else going on the page or annoy the user by causing a delay immediately after the page is loaded waiting for the post and response. I would try to put code in the
afterPageLoad event or use client-side code to trigger an RPC method or custom REST service if server-side code needs to run in that event.
I recently needed to move control of a source control repository on Bitbucket from a personal account to a company account. Fortunately, there’s a relatively easy built-in process to transfer it between accounts. In this post, I’ll show how it’s done.
- Open the repository in Bitbucket
- Click on Settings at the bottom of the left navigation
- Click on Transfer repository under General
- Choose the new owner and click the Transfer Ownership button
- Login to Bitbucket as with the account of the new owner
- Click on the user icon in the upper right and select “Inbox”
- You’ll see a message with the transfer request. (It will also be e-mailed to the account owner.)
- Open the message and click the link that it contains
- Click the Accept button to transfer the repository
- Now that the repository has been transferred, it takes you to the Access management screen so you can add users to the repository as needed.
- If anyone already had Source Tree set up to use the repository, they will need to update the repository settings to point to the new location.
- If you try to commit to the original repo url, you get a 404 error when trying to push:
- Open the repository in Source Tree
- Click on the Settings button in the upper right hand corner (or select Repository > Repository Settings)
- Click on the default repository to select it
- Click the Edit button
- Update the URL to replace the previous Bitbucket account name to the new one in two places in the URL
At Connect last week, I presented a session on using grids in XPages with Paul Calhoun. Here’s a link to the slide deck
AD-1207: The Grid, the Brad, and The Ugly: Using Grids to Improve Your Applications
At Connect next week, I’ll be co-presenting two Best Practices sessions, one on grids in XPages with Paul Calhoun and the other on web apps and IBM Digital Experience with John Head.
AD-1207: The Grid, the Brad, and The Ugly: Using Grids to Improve Your Applications
Tuesday @ 5:15pm
AD-1539: Bringing Your Web Apps to IBM Digital Experience
Tuesay @ 10:45am
For too long, WebSphere portal has been seen as the realm of the back end developer with specialized Java skills. This has been a barrier to entry to the IBM Domino community. IBM has transformed the product to the IBM Digital Experience platform – and it’s not just a name change! With the inclusion of the Script Portlet & IBM Portal on Cloud option, it’s time to look again. We will show you how to integrate your XPages applications, Bluemix and even Microsoft SharePoint. We will show content re-purpose without migration. If you are looking for a single point of integration for all your apps, this session is for you!
Hope to see you there!