Pages

Tuesday, September 15, 2009

CSS stylesheets in Lotus Connections 2.5

From Lotus Connections 2.0 to 2.5 the handling of theme and styles has been changed. While LC20 used static stylesheets, LC25 uses a specialized servlet for that purpose. This servlet called JAWR bundles multiple files and sends them compressed to the client. The HTML that is styled by this JAWR servlet refer to a normal URL, in LC25 this URL points to something like "/bundle/css/gzip_something/coreBundle.css". The context of the URL - /bundle/css - is mapped to the JAWR servlet, and the id of the bundle - /coreBundle.css - may look like file and is resolved by the servlet.. That makes it difficult, to find the proper stylesheet in the filesystem - simply, because it does not exist.

Looking at a sinlge LC25 component (i.e. Communities), the configuration file for the JAWR servlet can be found in /WEB-INF/properties.
In the configuration file, a bundle with the name "core" and the id "/coreBundle.css" is defined. The /coreBundle.css is mapped to the files
  • /stylesheet/update_styles.css,
  • /stylesheet/update_community.css,
  • /nav/common/styles/base/core.css,
  • /nav/common/styles/defaultTheme/defaultTheme.css,
  • /javascript/build/dijit/themes/dijit.css,
  • /nav/common/styles/base/dojo.css,
  • /nav/common/styles/defaultTheme/dojoTheme.css,
  • /javascript/source/lconn/comm/typeahead/themes/lconn.typeahead.css,
  • /javascript/source/lconn/comm/typeahead/themes/lotusBlue/LotusBlue.css,
  • /nav/common/styles/base/connectionsCore.css,
  • /nav/common/styles/base/semanticTagStyles.css,
  • /nav/lconn/styles/sprite-lconn.css
These files are located relative to the root of the web archive comm.web.war.

For faster development, the JAWR servlet offers a debug mode. According to the JAWR documentation the debug mode is enable by setting the jawr.debug.on=true in the jawr.properties file. The setting is applied after restarting the application.
The debug mode has two advantages:
  1. changes to the css stylesheets in the filesystem are effective immediately
  2. stylesheets are not included as a single, one-lined compressed file but as a set of files pointing to every single stylesheet. In the html, each mapped stylesheet is included via a separate link-tag. That way, it is transparent, which css definition is set in which stylesheet. Second, the stylesheets are not reformatted meaning that the line-numbers of tools such as firebug point to the correct line of the included stylesheet.

Friday, September 11, 2009

Creating a singleton using Eclipse Code Templates

In this post I'd like to demonstrate the Eclipse Code Template feature for implementing a singleton pattern.
The Singleton is a creational pattern of the set of Gang-of-Four Java Design patterns. It uses a single static variable (the singleton), a private constructor to prevent public instantiation and a public static getter method to retrieve the instance. For lazy instantiation, the singleton instance is not preinitialized, instead, the static getter method checks if the instance is null and instantiates a new instance on its first call. Lazy instantiation (as used in the following example) is not uncommon, though it may cause race conditions in multithreaded environments.
The Code Template feature is an extension to the code-completion functionality that is accessible by pressing Ctrl+Space. When the user types in code, he may hit Ctrl+Space to auto-complete, what he was typing. Eclipse proposes a set of options for completion, i.e. a Type, method of that type etc.
The Code Template feature might be known from typing in "sysout", pressing Ctrl+Space and Eclipse completes this to "System.out.println("");".
To add a new code template,
  1. open to Window -> Preferences
  2. navigate to Java -> Editor -> Templates.
  3. click on "New..."
  4. set the name of the new template to "singleton" (this is what you type into the editor)
  5. as pattern enter
    /**
     * static Singleton instance
     */
    private static ${enclosing_type} instance;
    /**
     * Private constructor for singleton
     */
    private ${enclosing_type}(){
    }
    /**
     * Static getter method for retrieving the singleton instance
     */
    public static ${enclosing_type} getInstance(){
      if(instance == null) {
        instance = new ${enclosing_type}();
      }
      return instance;
    }

The variable ${enclosing_type} resolves to the Class you are editing. From now on, it is possible to create the entire singleton pattern by just typing "singleton" and hitting Ctrl+Space - saving lots of keystrokes!

Wednesday, August 19, 2009

Converting GPX tracks to Google's kml format

Earlier this year I bought a neat GPS tracker for recording hiking trips and geotag my photos. With the software provided I can export these tracks to the GPX format which is an XML format for describing waypoint, tracks and routes. But for displaying my tracks in Google Earth, I need the kml format, which is also an XML format. For the transformation I use the free tool GPSBabel and a small batch script for convenient conversion.


@echo off
setlocal
if "%2"=="" (set out="%~n1.kml") else (set out=%2)
echo gpsbabel -i gpx -f %1 -o kml -F %out%
gpsbabel -i gpx -f %1 -o kml -F %out%
endlocal


The script could easily modified for the backward conversion (kml to gpx)

Wednesday, July 22, 2009

Batch MP3 encoding with lame

I recently bought the new album from Chimaira and liked listen ot it on my iPod. Since I like the mp3 format, I did not want to import it with iTunes using the AAC format I used the open and free mp3 encoder Lame. I ripped the CD tracks using Winamp and configured it to export the tracks to the format "Artist - Album - TrackNumber - SongTitle".wav. After that, I can encode the wav's to mp3's. But encoding each wav file manually is quite time consuming, especially, when all id3 tag information had to be entered manually. So I wrote a little Windows batch file that encodes all the wav's in a batch and automatically sets the ID3 tags.

The source for the script is:

@echo off

if "%1"=="" (
echo Usage: %0 [files] {genre} {year}
goto :eof
)

rem %2=genre
rem %3=year
if "%2"=="" (set GENRE=) else (set GENRE=--tg "%2")
if "%3"=="" (set YEAR=) else (set YEAR=--ty "%3")

for /R %%f in (%1) do (
echo Converting "%%~nxf"

rem extract from format "Artist - Album - trackNo - Title"
for /F "tokens=1,2,3,4 delims=-" %%A in ("%%~nf") DO (
call :encode "%%~nxf" "%%A" "%%B" "%%C" "%%D" "%GENRE%" "%YEAR%"
)
echo.
)

goto :eof

:encode
SETLOCAL

rem strip quotes
set _artist=%~2
set _album=%~3
set _trackNo=%~4
set _title=%~5

rem trim spaces
set artist=%_artist:~0,-1%
set album=%_album:~1,-1%
set trackNo=%_trackNo:~1,-1%
set title=%_title:~1%

echo Artist: "%artist%"
echo Album: "%album%"
echo TrackNO: "%trackNo%"
echo Title: "%title%"
if NOT "%genre%"=="" echo Genre: "%genre:~6,-1%"
if NOT "%year%"=="" echo Year: "%year:~6,-1%"

lame.exe -h -V 6 "%~1" "%~n1.mp3" --add-id3v2 --tt "%title%" --ta "%artist%" --tl "%album%" --tn "%trackNo%" %genre% %year%
ENDLOCAL

goto :eof

:eof

With the help of the scripts I could easily encode the files of an entire directory using:

myScipt *.wav 137 2009

Note, that 137 refers to the Genre list of the lame encoder. Use "lame --genre-list" to print an alphabetically sorted ID3 genre list.


Friday, May 8, 2009

Single Sign On with Quickr 8.1 and SPNEGO

Yesterday I was at a customer that is using Lotus Quickr 8.1 (J2EE) and als uses an ISSW asset - the SPNEGO TAI for WebSphere Application Server 6.0 - for WebSphere Portal 6.0. This customer wanted to use the TAI for Quickr too, respectively the underlying Application Server version 6.0.2.17.

And after some troubleshooting and a fix for WAS we finally managed to achieve SPNEGO/Windows Single Sign On for Quickr 8.1!

The only real issue with the product (Quickr) itself was a bug in the redirection after the request had not been intercepted by any TAI where the user was redirected to an error page instead of the login page. This was no specific SPNEGO TAI issue but an issue with the Trust Association Interception in general in the Quickr 8.1 and 8.1.1 underlying App Server, version 6.0.2.17.

However there is a technote and a fix for that known issue if you ever stumble upon this error.

After that fix had been installed - which went pretty smooth - Single Sign On with SPNEGO worked properly. At least in the WebUI but unfortunately not with the connectors.

One of the issues was that with SPNEGO enabled it didn't matter which values we specified for the main UI as user/pwd in the first dialog that comes up when we added a place to the connectors. These values were sent to the server and failed. However, the SPNEGO credentials were also sent along as we were authenticated successfully. But there were other user/pwd dialog boxes that appeared in the usage of the connectors as well. These did not work correctly with SPNEGO enabled.
Currently the Java based connectors (ie ST, Notes 8 Standard, etc) do not work at all when SPNEGO is enabled.

As a workaround to disable SPNEGO for the Connectors we considered two options:
The first (that was tested and finally implemented) was simply to create an additional DNS entry for the same Quickr server and specified this entry in the Connector (ie. web.quickr.intranet.com for the browser and connector.quickr.intranet.com for the connector). The TAI is configured to intercept only requests to a particular URL respectively a host (ie. web.quickr.intranet.com). If a request is sent to a different hostname (ie connector.quickr.intranet.com), the request is not intercepted and thus no Single Sign On happens.

The second option we thought of but did not test yet was to configure the TAI that way, that it uses a filter to exclude the URL used by the Connector respectively only intercept requests for the protected URLs of the web ui, which are URLs that contain /lotus/myquickr. Using the HTTPFilter for the configured server and a filter string "request-url==/lotus/myquickr" should do the job, but as mentioned - we did not test this.

Luckily, Quickr 8.2 is coming soon with official SSO support for the Connectors and it will be backward compatible to Quickr 8.1.1.

Wednesday, February 25, 2009

Quickr UI hacking

This week I was in a workshop at a customer where we discussed among others how to customize Quickr for Portal. They had some special requirements regarding the UI about which I'd like to write here.

One of their requirements was, to have a sidebar menu on the left-hand side containing the place navigation in the upper part of the sidebar and the functional elements below the navigation. They took the current Quickr for Domino look&feel as an example. I don't exactly know how the Quickr for Domino works, but I know that the Quickr for Portal works differently. The main problem is, that the PDM portlet (Library) in Quickr J2EE contains the entire toolbox as a right-side menu (with the "Create" button on top of if). So I needed to find a way to rip it off the portlet and place it in the right side navigation. I did this using JavaScript and it was actually only a couple lines of code.

First of all, create a place that uses a theme policy that displays the sidebar. Create a page that contains the library portlet (the one with the right sidebar). Then open the Default.jsp of the theme and search for the expression that includes the sidebar.

<%@ include file="./sideNav.jspf" %>

Place the following html snippet below this sidebar include.

<div id="injection-point"> </div>

Its important, that it is not a standalone html tag.
Now go to the end of the file and add the following script before the closing body tag:

<script type="text/javascript">
document.getElementById("injection-point").appendChild(document.getElementById("sidebar").parentNode);
document.getElementById("sidebar").style.visibility = "visible";
</script>


You could see, that the visibility attribute of the sidebar is set to visible, this comes a long with a change in the styles.jsp where you'll have to add the following snippet
#sidebar {
visibility: hidden;
}

This prevents from showing the sidebar on the right side during page load and then jumping to the left side, when the rendering process is complete. The sidebar is simply not shown from the beginning. Now save the files and see what happens :)

The final result looks like this:




Using this technique of adding injection points to the theme and some javascript code that moves elements around, you could easily modify the entire look and feel. For a customization I did this week for a customer, I moved the Create button and the Action menu to the left bar, and the other sections to the right side (outside of the portlet area). I also modified the search box, which is completely generated by a JSP tag.

Friday, February 13, 2009

Pointing to a specific Activity using the Portal Bookmark portlet

Today we tried to create a landing page for a Portal with integrated Connections. The landing page should contain a link to give feedback. Since it is a PoC we'd like to use an Activity for collecting feedback to demonstrate the various usage possibilites for Lotus Connections. The link to the feedback activity should have been provided in the Bookmark portlet of WebSphere Portal. However, we encountered a minor issue for which we found a solution.

The problem was, that the URL to the Activity contained a comma character respectively the octet encoding of it (%2C). Although this format is conforming with URL RFC 1738 (paragraph 2.2.) the Bookmark portlet refused to accept this URL stating the URL was not valid.

However, we found a workaround for that. We created a Portal URL page pointing to the feedback activity and put this URL page underneath the Activities URL page in the portal page structure so that it remains invisble. We assigned a uniquename to this URL page pointing to the Feedback Activity and created a bookmark in the bookmark portlet that pointed to the Portal URL page for the Feedback Activity. Thats it. The uniquename is required, otherwise this URL page would not be selectable as portal internal page in the Bookmark portlet.

By the way, I like the approach of collecting feedback using an Activity. The users can comment on existing feedback, agree, disagree. The feedback can be given in a structured way, like technical feedback, opinions, enhancement requests, and the users could even post files. The receivers of the feedback can give answers directly to the posts and users could easily subscribe to updates to stay informed about what's going on. And the best of all: it's usable out-of-the-box, we did not need to create a custom feedback form :)