Saturday, April 24, 2010

Pixels - wow! :-)

Thursday, April 15, 2010

Happy Birthday to U

Received this for my birthday today. Made me smile :-) Thanks @Sigi :-))

Friday, March 5, 2010

How to Become an Early Riser

I made several attempts to get up early again in the last weeks, but never really made it. I wanted to continue what worked really well last year, getting up at 4:30 almost everyday. I really love the early hours: no distractions, time for yourself, getting a lot done, and, of course, just the magic of the time before sunrise. But having tried again, I just couldn't make myself stay up when the alarm clock went off anything before 7:00am.

Since yesterday, I seem to be back on my old schedule. Why is that? I read a blog post of Levi Lewandowski, who already motivated me the first time. In his blog post, he writes why he loves being an early riser, reminding me of what early rising means to me, too.

So I commented on his post, that I'd be interested in how he does it, and if he had any tips. His answer: "actually it is quite easy: you simply have to get your *** out of bed early in the morning".

Now, that's pretty straight to the point - and it worked ;) :) sometimes, things can be so simple...

Wednesday, March 3, 2010

LotusScript function to compare the items of two documents

I want to share a little LotusScript code that compares the items of two documents, and which can be used e.g. to compare conflict documents.

The main function is "compareDocuments" which expects the two documents as parameters.
Adjust the constant "LOGFILEPATH" for the filepath of the log output.

The comparison goes step by step, from checking the existence in the other document, to the item types, to the item values.

Why do I use lists of a class CL_ItemCompare? Because it is slightly faster to read the items once and then access the cached information than to have to access the documents twice for the same information (as it would be the case in the second items loop in the function "compareDocuments").

'CompareDocuments:

Option Public
Option Declare

Class CL_ItemCompare
Public itName As String
Public itType As String
Public itValues As Variant
Public isChecked As Boolean
Sub new(it As NotesItem)
Me.itName = it.Name
Me.itType = it.Type
Me.itValues = it.Values
Me.isChecked = False
End Sub
End Class

Function compareDocuments(doc1 As NotesDocument, doc2 As NotesDocument) As Boolean

On Error Goto errorhandler

Const LOGFILEPATH = "C:\comparedocs.txt"

Open LOGFILEPATH For Output As #1

Print #1, "Comparing [" & doc1.UniversalID & "], [" & doc2.UniversalID & "]"


' get all items from doc1
Dim listItCompare1 List As CL_ItemCompare
Dim itx As NotesItem
If Not Isempty(doc1.Items) Then
Forall it In doc1.Items
Set itx = it
Set listItCompare1(Lcase(itx.name)) = New CL_ItemCompare(itx)
End Forall
End If

' get all items from doc2
Dim listItCompare2 List As CL_ItemCompare
If Not Isempty(doc2.Items) Then
Forall it In doc2.Items
Set itx = it
Set listItCompare2(Lcase(itx.name)) = New CL_ItemCompare(itx)
End Forall
End If

' compare all items of doc1 with items in doc2
Forall itCompare1 In listItCompare1
If Not compareItems(itCompare1, listItCompare1, listItCompare2) Then Goto e
End Forall

' all items of doc2 that have not been treated yet are missing in doc1
Forall itCompare2 In listItCompare2
If Not itCompare2.isChecked Then
Print #1, "Item '" & itCompare2.itName & "': [DOES NOT EXIST, exists]"
End If
End Forall

compareDocuments = True
e:
Close
Exit Function
errorhandler:
Msgbox "compareDocuments: Error " & Error & " in line " & Erl
Resume e
End Function

Function compareItems(itCompare1 As CL_ItemCompare, listItCompare1 List As CL_ItemCompare, listItCompare2 List As CL_ItemCompare) As Boolean

On Error Goto errorhandler

itCompare1.isChecked = True

' check if item of doc1 exists in doc2
If Not Iselement(listItCompare2(Lcase(itCompare1.itName))) Then
Print #1, "Item '" & itCompare1.itName & "': [exists, DOES NOT EXIST]"
compareItems = True
Exit Function
End If

' get item of doc2
Dim itCompare2 As CL_ItemCompare
Set itCompare2 = listItCompare2(Lcase(itCompare1.itName))
itCompare2.isChecked = True

' compare types
If itCompare1.itType <> itCompare2.itType Then
Print #1, "Item '" & itCompare1.itName & ": type = [" & itCompare1.itType & ", " & itCompare2.itType & "]"
compareItems = True
Exit Function
End If

' compare typename of values
If Typename(itCompare1.itValues) <> Typename(itCompare2.itValues) Then
Print #1, "Item '" & itCompare1.itName & ": typename of values = [" & Typename(itCompare1.itValues) & ", " & Typename(itCompare2.itValues) & "]"
compareItems = True
Exit Function
End If

' compare item values

Dim isEqual As Boolean
Dim i As Integer
Dim txtValue1 As String
Dim txtValue2 As String
Dim isFirst As Boolean

If Instr(Typename(itCompare1.itValues), "( )") > 0 Then ' compare arrays

' compare ubound of item values array
If Ubound(itCompare1.itValues) <> Ubound(itCompare2.itValues) Then
Print #1, "Item '" & itCompare1.itName & ": ubound(values) = [" & Ubound(itCompare1.itValues) & ", " & Ubound(itCompare2.itValues) & "]"
compareItems = True
Exit Function
End If

' compare single array entries
isEqual = True
For i = 0 To Ubound(itCompare1.itValues)
If itCompare1.itValues(i) <> itCompare2.itValues(i) Then
isEqual = False
Exit For
End If
Next

If Not isEqual Then

' concatenate values (we cannot use join here if values are e.g. array of integers)
txtValue1 = ""
isFirst = True
Forall x In itCompare1.itValues
If Not isFirst Then
txtValue1 = txtValue1 & ";"
Else
isFirst = False
End If
txtValue1 = txtValue1 & Cstr(x)
End Forall

' concatenate values (we cannot use join here if values are e.g. array of integers)
txtValue2 = ""
isFirst = True
Forall x In itCompare2.itValues
If Not isFirst Then
txtValue2 = txtValue2 & ";"
Else
isFirst = False
End If
txtValue2 = txtValue2 & Cstr(x)
End Forall

Print #1, "Item '" & itCompare1.itName & ": values = [" & txtValue1 & ", " & txtValue2 & "]"
compareItems = True
Exit Function

End If

Else ' compare single value (i.e. NotesRichtextItem.Values)

If itCompare1.itValues <> itCompare2.itValues Then
Print #1, "Item '" & itCompare1.itName & ": values = [" & itCompare1.itValues & ", " & itCompare2.itValues & "]"
compareItems = True
Exit Function
End If

End If

' items are equal
Print #1, "Item '" & itCompare1.itName & ": is equal"

compareItems = True
e:
Exit Function
errorhandler:
Msgbox "compareItems: Error " & Error & " in line " & Erl
Resume e
End Function
Sub Initialize

End Sub

PickListStrings - Something Unexpected

If you look for the cause of an error, it's not always the obvious - like in this case:
the following LotusScript function retrieves a Notes database filepath, a view name and title and prompt to show a picklist to the user, and display the user selection in a messagebox.

Function handlePicklistResult(dbFilepath As String, viewName As String, dlgTitle As String, dlgPrompt As String) As Variant

On Error Goto errorhandler

Dim res As Variant

Dim ws As New NotesUIWorkspace
res = ws.PickListStrings( _
PICKLIST_CUSTOM, _
False, _
"", _
dbFilepath, _
viewName, _
dlgTitle, _
dlgPrompt, _
1 )

If Not Isempty(res) Then
Msgbox Join(res, "")
End If

e:
Exit Function
errorhandler:
Msgbox "Error " & Error & " in line " & Erl
Resume e
End Function
Now, the code results in the following error:

Error Notes Error - Incorrect argument: non-null string expected in line 16
Can you guess why (it's in the parameters...)?

It could be, that dbFilepath is not set, but it is...
It could be, that the viewName is not set, but it is...

So what is it?

It's the title and the prompt! PickListStrings does not allow an empty title or an empty prompt. If you do not want to show a title or a prompt, you have to use at least a space character...

Tuesday, March 2, 2010

Formula-Agent and @Prompt

I had to write a small maintenance agent to reset NotesItems, and, to not accidentally trigger it, I used the @Prompt function to ask the typical "Are you really sure?" question - not a good idea ;-)

The result was, that I had to keep clicking message boxes until the agent was finally done - fortunately there where only 42 documents in the databases, which made me click 42 times.

Here is why; I used the following agent, with the options "Action menu selection" and "All selected documents":

@If(!@Prompt([YesNo]; "Reset fields"; "Are you sure?"); @Return(""); "");
@SetField("Body"; "");
@SetField("nbMemos"; 0);
[...]
SELECT @All

If you do this, the result is, that Notes triggers the agent for each document (actually "of course" ;-)) and asks for each document separately; could be kind of useful if this is what you want it to do, but you should never try this on a database storing some thousand documents :-)

A better way to accomplish the task would be to write the agent in LotusScript, even though it is a lot longer:

'RESET MAIL FIELDS:

Option Public
Option Declare
%INCLUDE "lsconst.lss"
Sub Initialize

On Error Goto errorhandler

Dim s As New NotesSession

Dim db As NotesDatabase
Set db = s.CurrentDatabase

Dim doccoll As NotesDocumentCollection
Set doccoll = db.UnprocessedDocuments

If doccoll Is Nothing Then
Exit Sub
End If

If Msgbox("Are you sure?", MB_YESNO, "Reset fields") <> IDYES Then
Exit Sub
End If

Call doccoll.StampAll("Body", "")
Call doccoll.StampAll("nbMemos", 0)
[...]

e:
Exit Sub
errorhandler:
Msgbox "Error " & Error & " in line " & Erl
Resume e
End Sub

Monday, March 1, 2010

LotusScript Quiz: the "+" operator

As you (if you are a LotusScript developer ;-)) know,

- Msgbox "a" + 3 results in a type mismatch error.
- Msgbox "a" & 3 delivers "a3".

Now, do you know what the result of this line of code is?

Msgbox "1" + 1
A. "11".
B. 2.
C. A type mismatch error occurs

Solution: B - LotusScript tries to parse the string, and adds the two values.

This also works with strings like "1.1" or "1,1". In my Notes environment "," is the decimal seperator, so "1,1" + 1 results in 2,1 and "1.1" + 1 results in... 12.

The code does not work, however, with "1a" + 1, even though val("1a") returns 1. LotusScript seems to parse the string differently here - mh...

Still... please do NOT use this - this is evil coding ;-)

Wednesday, February 24, 2010

Build Smarter, Richer Notes/Domino Applications with Adobe Flex

Thomas Baumgartner and I wrote an article for the View about building smarter, richer Notes/Domino Applications with Adobe Flex, which just got published last Friday.

The article can be viewed here: Domino/Flex integration

In this article, we lead the reader through the first steps of building a Flex application, then connecting the Flex app with a Domino database and charting the Domino data with just a few lines of code added. In the last example, we show how Flex applications can be integrated directly into a Notes form, bringing RIA charts and dashboards right into your Notes environment.

Check out our example page for the Domino/Flex article, if you want to see what we help the reader build in the article.

This article is the first of a series of three, so stay tuned! :-)

Sunday, February 7, 2010

Winter Wonderland

Enjoying a wonderful day with my son at the "ski kindergarten" in Lenggries.



Position:Lenggries,Deutschland

Thursday, February 4, 2010

Tech 2 Wear

How cool *g* :) just saw someone carrying this obviously selfmade briefcase - the grip is an old receiver from the times when the post office offered exactly one type of phone in three weird colors, and the round thing in the picture is the corresponding rotary dial... How neat can ugly be? ;)

Wednesday, February 3, 2010

Adobe Flex - helpful development resources

Here's a list of links that are a great starting point if you want to learn Flex, and some like the style and component explorer still help me a lot during the development process. Just shared them via email and thought I might just share them here, too - sharing is good, right?

Originally, these links were put together by Thomas Baumgartner -> Thank you, Thomas :-)


Have more links? Post them as a comment :-)

Tuesday, February 2, 2010

Import Lotusphere presentations into session database

Have you downloaded the Lotusphere 2010 presentation files already and are you now switching between LS agenda and your file folder to find out what session id belongs to what session?
Wouldn't it be nice to be able to browse through the sessions by day, speaker etc. and view the short descriptions together with the presentation files?

Here is how you can do this:

The guys at Genii Software offer their LS2010_SessionsDB for download with all the meta information for the Lotusphere 2010 sessions. Great job!! Download the LS2010_SessionsDB here.

Then use the Lotusscript code below to populate this database with your presentations - and voilĂ , you have all the information together.

Make sure you adjust the constants in the top section of the code to your own environment; if you have any questions, leave me a note. All presentations have to be in the same folder. I did not try it with a different OS then Windows, so I don't know if the code works for UNIX/Linux/Mac. Could though ;-) The agent writes a CSV file with the upload logs and you can choose if you want to upload only the colored, the black/white or all presentations. Have fun!

Sub Initialize

On Error Goto errorhandler

Const FILEPATH_RESOURCES = "C:\ls10_sessions\" ' file path of all presentations (including terminating slash)
Const SERVER_LS_DB = "" ' server of session database
Const FILEPATH_LS_DB = "ls10/LSess10.nsf" ' file path of session database (relative path)
Const FILEPATH_LOG = "C:\ls10.csv" ' file path of log; will be created / overwritten if it exists
Const UPLOAD_MODE = "color" ' options are: "color", "bw", "all" (uploading colored presentations only, black/white presentations or all)

' check constant upload_mode
Select Case UPLOAD_MODE
Case "color", "bw", "all"
Case Else
Msgbox "Invalid option for constant UPLOAD_MODE: " & UPLOAD_MODE & "." & Chr$(13) & " Only 'color', 'bw' or 'all' allowed."
Goto e
End Select

' get session database & view
Dim dbLS As New NotesDatabase(SERVER_LS_DB, FILEPATH_LS_DB)
If Not dbLS.IsOpen Then
Msgbox "Unable to open database " & SERVER_LS_DB & "!" & FILEPATH_LS_DB
Goto e
End If

Dim vBySessionID As NotesView
Set vBySessionID = dbLS.GetView("BySessionID")

If vBySessionID Is Nothing Then
Msgbox "View BySessionID not found in database " & SERVER_LS_DB & "!" & FILEPATH_LS_DB
Goto e
End If

Open FILEPATH_LOG For Output As #1
Print #1, "SESSIONID;FILENAME;UNID;LOGSHORT;LOG"

Dim filename As String
filename = Dir$(FILEPATH_RESOURCES, 0)

Dim docSession As NotesDocument
Dim sessionID As String
Dim embObj As NotesEmbeddedObject
Dim varBody As Variant
Dim isSkip As Boolean

Do While filename <> ""
Print filename

sessionID = Trim(Strtoken(filename, ".", 1))

' check upload mode - process file?
isSkip = False
Select Case UPLOAD_MODE
Case "color"
If Lcase(Right$(sessionID, "2")) = "bw" Then
isSkip = True
End If
Case "bw"
If Lcase(Right$(sessionID, "2")) <> "bw" Then
isSkip = True
End If
End Select

If Not isSkip Then

' get session ID and session
If Lcase(Right$(sessionID, "2")) = "bw" Then
sessionID = Left$(sessionID, Len(sessionID) - 2)
End If
Set docSession = vBySessionID.GetDocumentByKey(sessionID, True)

If docSession Is Nothing Then
Print #1, sessionID & ";" & filename & ";;NO MATCH;No match found for session ID " & sessionID
Else

' check if old attachment exists with same name; do not upload again
Set embObj = docSession.GetAttachment(filename)
If Not embObj Is Nothing Then
Print #1, sessionID & ";" & filename & ";" & docSession.UniversalID & ";FILE EXISTS;Session ID " & sessionID & " already contains a file named " & filename & ". Skipped"
Else

' check if Body RT item exists / if not, create if possible
Set varBody = docSession.GetFirstItem("Body")
If varBody Is Nothing Then
Set varBody = New NotesRichTextItem(docSession, "Body")
Elseif varBody.Type <> 1 Then
If varBody.Text = "" Then
Call varBody.Remove()
Set varBody = New NotesRichTextItem(docSession, "Body")
Else
Print #1, sessionID & ";" & filename & ";" & docSession.UniversalID & ";UNABLE TO UPLOAD;Session ID " & sessionID & " contains a Body item that is not a NotesRichTextItem and that contains text. Skipped."
End If
End If

' upload file
If Not varBody Is Nothing Then
Call varBody.EmbedObject(EMBED_ATTACHMENT, "", FILEPATH_RESOURCES & filename)
Call docSession.Save(True, False, False)
Print #1, sessionID & ";" & filename & ";" & docSession.UniversalID & ";UPLOADED;Session ID " & sessionID & ": attached file " & filename & " (" & FILEPATH_RESOURCES & "/" & filename & ")"
End If

End If
End If
End If

filename = Dir$()
Loop

e:
Close
Exit Sub
errorhandler:
Msgbox "Error " & Error & " in line " & Erl
Resume e
End Sub

Monday, January 18, 2010

Lotusphere 2010 Monday

It's been a pretty long time since my last blog post - I guess Lotusphere is just the right time to start again. So what's happening in Disney World? Weather is great, 70F, it's awesome to be here and see everyone again. Hey, where was everyone at JellyRoles yesterday night? :-)

Today's big event was of course the Opening General Session of Lotusphere 2010. The guest speaker William Shatner was just pure brilliant - and everyone in the room very excited... geeks... ;-)

What Bob Picciano and Alistair Rennie announced afterwards was partly interesting, partly exciting, but not (yet) the *bang* that would be expected for the Opening General Session. The announcements included news about:

- an improved Quickr that got more integrated with Notes
- Notes Traveler Companion now being available in the App Store
- 18.700 and some new customers since Notes 8
- Connections gone mobile
- the improved vertical approach to the market
- LotusLive having more than 18 Mio Users
- Lotus Connections becoming part of LotusLive in Q3
- LotusLive Notes being planned for spring
- Lotus Live Labs for partners being planned for Q2

And then, nearly towards the end, it came - the big bang: Project Vulcan

Ron Sebastian and Suzanne Minassian showed us what we can expect: a whole new "application", built on loosly coupled systems, that seems to have Notes as its center point, with LotusLive'ish applications integrated, reminding of Google Wave, but seeming to be - at least in the vision - already ahead. At this point it looks great, and I must admit my first thought was "I want to work with that". I am pretty excited about this move and most people here at Lotusphere seem to share this feeling.

Ed Brill writes: "... that leap is going to leave everyone else in the dust" - read more about Project Vulcan in his blog.

Wednesday, May 13, 2009

"Test Driving" - How do you test traffic information systems?


As seen yesterday morning on one of the main streets in Albuquerque, NM :)

Saturday, May 9, 2009

Uno, Symphony, and weird things happening

I totally like the idea of Symphony, being integrated in Notes, being free and all... I was excited to try out Uno and write my own Symphony exports in LotusScript - until I had to deal with some really weird effects, that costed me hours of researching (to give it some credit, there's not too much information out yet). At the end, I landed up deinstalling Symphony and installing OpenOffice (don't worry, just for this project ;-)). And, without *any* changes in the code, everything worked. Mh..

Here is an example (see code at the end of this post). If you want to try it yourself, just place a starWriter (odt)-file in c:/tmp/myfile.odt, that has the text <name> in it (with brackets).

What the code does, or is supposed to do, is open the odt, then search for the tag <name> and replace the text.

Works perfectly in OpenOffice; in Symphony, the document is opend and nothing else happens ..unless we put a "sleep" before "objDocument.replaceAll" (see commented code below) making the code wait a little (!); then, Symphony replaces the tag. Seems that Symphony is so fast, that it is too fast for itself ;-)

After a little research, I found out, that it might have something to with the frames. In the code below, I open the odt file in "_default". Opening it in "_blank" does everything it should. Another Mh..

Now, I don't know where the mistake is... maybe I'm just not understanding the differences between OpenOffice and Symphony. But already opening a file that I detached from a richtext item before, led to problems ("is already in use by another application"); something I have done a thousand times in MS Office. I will definitly keep playing with Symphony and Uno, but, let's say, right now I'm not *as* enthusiastic anymore as I was before. Hope that will change again.

If you are looking for a cool step-by-step introduction into Uno, here's everything you need: John Head's blog posts on UNO

And here is the LotusScript code:

%REM
Testing uno: symphony vs. open office
To use this agent, you must have a file c:/tmp/myfile.odt on your hd, that contains the text ""
Agent will try to replace the "" tag
%END REM

' initialize service manager and desktop

Dim objServiceManager As Variant
Set objServiceManager = CreateObject ("com.sun.star.ServiceManager")

Dim objDesktop As Variant
Set objDesktop = objServiceManager.createInstance ("com.sun.star.frame.Desktop")

' open file

' not as template
Dim args1 As Variant
Set args1= objServiceManager.Bridge_GetStruct ("com.sun.star.beans.PropertyValue")
args1.Name = "AsTemplate"
args1.Value = False

' show us what's happening
Dim args2 As Variant
Set args2= objServiceManager.Bridge_GetStruct ("com.sun.star.beans.PropertyValue")
args2.Name = "Hidden"
args2.Value = False

Dim args(1) As Variant
Set args(0)=args1
Set args(1) = args2

Dim sUrl As String
sUrl = "file:///c:/tmp/myfile.odt" ' this is where the file is located

' open document in "_default" (with "_blank" it works in both oo and symphony)
Dim objDocument As Variant
Set objDocument = objDesktop.loadComponentFromURL(sUrl, "_default", 0, args)

' now try to replace the tag

Dim vInterface As Variant
Set vInterface = objDocument.createReplaceDescriptor()
vInterface.searchstring = "<name>" ' search for this
vInterface.replacestring = "Marcus Foerster" ' replace it with this
vInterface.searchwords = True
vInterface.searchcasesensitive = False

' in symphony, we need a sleep here now, otherwise it won't replace anything
' sleep 1

' now replace it
Dim vNumbers As Variant
vNumbers = objDocument.replaceAll(vInterface)

End Sub