Lotusphere 2012 – Day 3 (Tuesday)

A busy day at Lotusphere. It started with a keynote about how Social affects business. The messge was that leadership matters, a dedicated team must take point. Social is not an IT function/project, it needs to involve departments liek HR, legal and of course IT. If you engage the employees, they will be a huge resource. The example given was when TD Bank decided to be open 7 day a week, the reaction from the workforce would probably been negative. But by using social capabilities and transparency, the employees got engaged and even enthusiastic about the new opportunity.

Then the sessions started.

BP102 – User Blast with Mat Newman. Mat Newman, the Australian with the yellow suit, had a great session showing a multitude of functions that will make life easier for end-users. The idea is that anyone attending the session should be able to bring this info back and energize their users. The room was packed (as was the repeat session later), and the organizers had to use overflow rooms. Even experienced Notes users learned new things here.

BP110 -  A Performance Boost for Your IBM Lotus Notes Client, presented by the excellent pair Francie Tanner and Florian Vogler (both from Panagenda). I learned a number of things that I will be able to bring home and hopefully implement in my environment.

I also had some meetings, so I went to the Solutions Showcase and looked around inbetween meetings. GBS was out in force and had occupied a huge section of the floor. I did not have as much time as I wanted on the showcase floor, but it seemed like bigger than last year, with more booths and vendors.

Finally I took some time to visit the Meet the Developers lab, a place where I always spend several hours during the week. This time I had mainly development questions, most of them related to issues with Domino Designer itself. I spent quite some time with Maureen Leland, the head of Domino Designer development, and we identified a number of issues that hopefully can be fixed. One of them, to automatically indicate that a database is a template by using a different background color behind the workspace icon, was so easy that she was convinced she could code that on the flight back to Boston. She said the hardest problem to solve was to decide what indicator to use…

This is the kind of access we get to the IBM staff, nothing beats being able to talk to the guys and girls who actually wrote the code we have questions about. If you haven’t been to the labs, take a few minutes or an hour to go there. Highly recommended!



Lotusphere 2012 – Day 2 (Monday)

OGS. The abbreviation seasoned ‘spherians use for the Opening General Session. The fear in the community was a repeat of last year, with endless customer panels with readings from teleprompter and no demos. But IBM listened. There were plenty of demos, and the customers on stage were on for just a few minutes, were well rehearsed and had interesting stories to tell.

The OGS started, as in previous years, with a musical performance. This year by the band OK Go.


Then this years guest speaker was introduced: Michael J Fox. He talked (among other things) about how 75% of people diagnosed with Parkinson’s Disease (which Michael is suffering from) are members of an online community.

Michael J Fox 

What new products did we see? Well, we got to see what came out of Project Vulcan: the new Activity Stream. It is integrated with the next version of Connections as well as in Lotus Notes/Domino Social Edition and other products.

Alistair Rennie 

Another very interesting product that was demonstrated at the OGS is a plugin that let you run Notes applications totally modified in a browser, without having to rewrite it using Xpages. Finally, LotusLive Symphony has been renamed IBM Docs.

So what is Lotus/Domino Social Edition? Ed Brill explains it well:

What version number is Notes/Domino Social Edition? Answer: It isn’t. It’s a feature version, but it is built atop the simultaneously-forthcoming planned Notes/Domino 8.5.4 release.


It includes the activity stream, but if I understand it correctly, you need Connections in order to fully benefit from this.

Personally I think this was one of the better opening sessions I have been to. It was well paced and had enough news to keep the audience awake. The four customer cases were fairly short, but next year, try just three, and add even more demo, especially on the main product we all use, Lotus Notes.


After the OGS, I had some meetings. When they were handled, I managed to catch two good sessions.

AD111 – The X Path: Practical Guide to Taking Your IBM Lotus Notes Applications to IBM Lotus Domino XPages, presented by Hunter Medney and Stephan Wissel. This was a great session, explaining the recommended steps to move existing Notes applications to Xpages. The speakers explained what to look out for, and how to best perform the conversion.

AD106 -IBM Lotus Domino XPages -Write Them Ones, See Them Everywhere, by Viktor Krantz and Stephan Wissel. Another great Xpages session, showing how Xpages can be consumed by different platforms, including mobile phones and pads.

Then it was on to a reception for an hour, before heading to Joe Litton’s Mai Tai night. Then we all migrated over to Shula’s for the traditional UK Night. The night then continued on the Swan side outside Kimono’s, where a smaller group were hanging out, smoking cigars and swapping war stories from previous ‘spheres.

A great day and night.



Lotusphere 2012 – Day 1 (Sunday)

Sunday is now officially the first day of Lotusphere, with Jumpstart sessions and (of course) the welcome reception at night. I had a full day’s schedule, and went to four great sessions.

JMP102 – Introduction to Java for IBM Lotus Domino Developers, by the excellent Paul Calhoun. I can’t say I know Java, even if I did write some code many years ago. This session made me very excited about taking another look at Java, in order to add that language to my toolbox.

JMP103 – "How Stuff Works" IBM Lotus Domino Style! by Susan Bulloch and Jess Stratton. I am a developer, but I want to (and feel that every designer should) learn the basics of administration and security. Susan and Jess are excellent presenters and are experts at their subject. This session was very beneficial to me.

JMP303 – Master Class: They Really Are Out To Get You by Gabriella Davis and Andrew Pollack. The title is ironic, as it seems like a certain corporate lawyer at IBM is out to get Andrew. An hour before the presentation was about to be given, the lawyer demanded that all pictures were to be removed, because of potential copyright issues. You can read the full story on Andrew’s blog.
Despite this, Gab and Andrew managed to deliver a great session on security, which of course would not surprise anyone who know these competent long-time speakers.

JMP101 – IBM Lotus Domino Xpages JumpStart by Howard Greenberg and Paul Della-Nebbia. I attended some Xpages sessions at Lotusphere 2011, but have not been able to start doing any actual development. I was having problems getting over the initial hump, but this session explained things very well and inspired me to take a look at Xpages when I get back home.

Then there was the traditional poolside welcome reception, followed by a visit to Kimono’s, before heading to bed.



Lotusphere 2012 – Day 0 (Saturday)

After getting up early in the morning, I boarded my flight from DFW to Orlando. As usual I flew American Airlines, but for the first time they had WiFi on the plane. So I was able to stay connected even on the flight, keeping track of how all my friends and contacts in the Lotus community were arriving to Orlando as well, from all over the world. Some had already arrived on Friday, and they shared weather info and other tidbits with the rest of us. During the approach to Orlando, I was even able to get a picture of Dolphin and Swan from the airplane.


After taking the shuttle to Dolphin, I checked in, and #occupyswandolphin (as Andy Donaldson once called it on Twitter) could start. The official twitter hash tag is #ls12, though.

After picking up my badge at registration, I headed over to Big River Brewhouse and the traditional BALD, which stands for Bloggers (and friends) Annual Lotusphere Dinner. There I met many of my friends, and we had some beer, food, and Kitty Elsmore's excellent toffee.


Spankford Blogmonkey also made an appearance, and he even brought some friends.


The plan was to head over to ESPN Zone after BALD, but because a couple of football teams decided to play each other, so the line was ridiculously long. A number of us went to Kimono's and had some drinks and some food.

After Kimono's we moved over to the Dolphin Bar to continue socializing. I had brought some "apple cake" (or perhaps it should be called "apple pie"), a drink that is popular in Sweden. It was a big hit with everyone that tried it. I will post the recipe soon, for anyone that want to make it themselves. 



On My Way to Lotusphere


On Flight AA1434 DFW to MCO. Should be at Swolphin around noon. Can't wait for Lotusphere 2012 to start! I have high hopes for the OGS, hope IBM delivers.

Looking forward to see all my friends again, and make new ones.



Code: Example of how to use Windows Clipboard in a Notes application

As I promised in my previous entry, I will show how to use the Windows clipboard functionality in a Notes application.

We have a knowledgebase, where information for different departments is posted. A very typical and simpel Notes application, in other words. But some users requested a simpler way to create nice doclinks, so I listened and created two buttons on the document form. 

The first button is 'Copy Link', it will simply collect information about the open document (database server, filename and path and UNID of the document. This information is then just stored in the cliboard as a delimited string.

The second button is 'Paste Link'. It will read the delimited string from the clipboard, get a handle to the document and create a nice doclink. The title of the document is also pasted into the document.

To support this functionality, you also have to create a form called 'doclink', where the only content is a RichText field called "Body". Make sure there is not even a linebreak after the field.

Below is the actual code. It requires the script library I posted earlier. Hope this can help someone.


Button 'Copy Link':

 Use "Class.Win32.ClipBoard"
Sub Click(Source As Button)    Dim ws As New NotesUIWorkspace    Dim uidoc As NotesUIDocument    Dim clipboard As WindowsClipboard    Dim clipdata As String     Set clipboard = New WindowsClipboard()    Set uidoc = ws.CurrentDocument    clipdata="NotesData~" & ws.CurrentDatabase.Database.Server    clipdata = clipdata & "~" & ws.CurrentDatabase.Database.FilePath    clipdata = clipdata & "~" & uidoc.Document.UniversalID    clipboard.Contents = clipdata    Msgbox "Document info has been stored. You can now" & Chr$(10) & _    "use the 'Paste Link' button in IT Support db or" & Chr$(10) & _    "your mail file to create a link to this document.",,"Success"End Sub


Button 'Paste Link':

 Use "Class.Win32.ClipBoard"
Sub Click(Source As Button) ' *** Create and insert a doclink at the insertion point    Dim session As New NotesSession    Dim ws As New NotesUIWorkspace     Dim thisdoc As NotesUIDocument     Dim tempuidoc As NotesUIDocument    Dim db As NotesDatabase    Dim doc As NotesDocument    Dim tempdoc As NotesDocument    Dim linkdb As NotesDatabase    Dim temprt As NotesRichTextItem    Dim dbname As String    Dim servername As String    Dim unid As String    Dim clipboard As WindowsClipboard    Dim clipdata As String    Dim clipargs As Variant  ' Array to hold data from clipboard     Set thisdoc = ws.CurrentDocument    Set db = session.currentdatabase    Set clipboard = New WindowsClipboard()     clipdata = clipboard.Contents     If clipdata="" Then        Msgbox "No clipboard info found."        Exit Sub    Elseif Instr(clipdata,"NotesData~")=0 Then        Msgbox "No doclink info in clipboard."        Exit Sub    End If    clipargs = Split(clipdata,"~")     servername = clipargs(1)    dbname = clipargs(2)    unid = clipargs(3)     Set linkdb = session.GetDatabase(servername, dbname, False)    If linkdb Is Nothing Then        Msgbox "Could not find " & dbname & " on " & servername,,"Not Found"        Exit Sub    End If    Set doc = linkdb.GetDocumentByUNID(unid)    If doc Is Nothing Then        Msgbox "Could not located document.",,unid        Exit Sub    End If      ' Now the awkward bit: the only way to insert a doclink into RT that you  ' are editing is to paste it. The only way to get a pastable doclink into ' the clipboard is to copy one from an existing document so first we make ' a doclink in an RT field that links to the document.    Set tempdoc = New NotesDocument(db)     ' Form that will only contain the field with the doclink and NOTHING else.    Call tempdoc.ReplaceItemValue("Form","doclink")    Set temprt = New NotesRichTextItem(tempdoc,"body")    Call temprt.AppendText(doc.KBFull(0) & " - " & doc.Topic(0) & " ")    Call temprt.AppendDocLink(doc, doc.Topic(0))    ' This doc must be saved otherwise the display doesn't work    Call tempdoc.Save(True, False)    ' Now "display" the document so we can grab the doclink    Set tempuidoc = ws.EditDocument(False, tempdoc)     ' Select and copy the content    Call tempuidoc.SelectAll    Call tempuidoc.Copy  ' We should have the doclink inthe clipboard    ' Close and throw away the temporary document     Call tempuidoc.Close      Call tempdoc.Remove(True)      Call thisdoc.Paste   ' Paste the doclinkEnd Sub




Getting ready for Lotusphere

Like so many other in the Yellowverse, I am getting ready to leave for Orlando. A couple of more days, and I will be on the airplane heading to (hopefully) sunny Orlando again.
My schedule is still up in the air a bit, I know that Monday and Tuesday there are probably some interviews and meetings scheduled for me by IBM, and I will not know about them untilI check in.
Except for the obvious sessions (OGS, Gurupaloza, Beat the Developers and CGS), there are a couple of sessions I want to push for. They are high on my priority list as well,a nd I hope I have time to go to all of them.
SHOW112 – How to Build an XPages Application from Start to Finish Tim Clark and Matt White
JMP102 – The Top Things All New IBM Lotus Domino Developers Need To Know Thomas Duff and Kathy Brown
JMP106 – "Kum Bah Yah" Meets "Let’s Kick Butt" John Head and Alex Kassabov
BP108 – Worst Practices 4.0: "Orlando, We Have a Problem" Paul Mooney and Bill Buchan
BP114 – IBM Lotus Domino Server & Application Performance in the Real World Andrew Pollack
BP202 – There´s No Fixing Ugly: How to Make a Great First Impression with Your Applications Scott Good
BP205 – "I’m a Programmer Not a Firefighter!": The Low Maintenance "Notes Shop". Timothy Paque and Bruce Elgort
BP210 – The Great Code Giveaway 9: Never Gonna Let You Down Rob Novak and Viktor Krantz
I am not a new Domino developer, but I still hope to be able to pick up a few new tips and tricks fromDuffbert and Kathy. This is Kathy’s first LS presentation so sheneed some friendly faces in the audience.I want to see if she live-twitter during her presentation orif she can survive without twittering for an hour. 🙂
Last year I submitted a suggestion for a similar session, and I am very happy to see this session at Lotusphere this year. I think that is important to get new developers interested in the platform, and give them tips and pointers. So great job, IBM!
See you in Orlando!



Code: Accessing Windows Clipboard

Several years ago, I found some code to access the Win32 functions for the Windows Clipboard. I don't remember where I found it, who wrote it or if it was VB code that I modified or already written for Lotusscript. I rewrote the the code as a class and put it in a script library called "Class.Win32.ClipBoard". The complete code is listed below. In my next blog entry I will describe how I am using this class for some very convenient functions.



Option Public

Option Declare


Declare Private Function GetClipboardData Lib "User32" (Byval wFormat As Long) As Long

Declare Private Function SetClipboardData Lib "user32" (Byval wFormat As Long, Byval hData As Long) As Long

Declare Private Function OpenClipboard Lib "User32" Alias "OpenClipboard" (Byval hwnd As Long) As Long

Declare Private Function CloseClipboard Lib "User32" Alias "CloseClipboard" () As Long

Declare Private Function GlobalLock Lib "kernel32" Alias "GlobalLock" (Byval hMem As Long) As Long

Declare Private Function GlobalUnlock Lib "kernel32" Alias "GlobalUnlock" (Byval hMem As Long) As Long

Declare Private Function GlobalAlloc Lib "kernel32" (Byval wFlags As Long, Byval dwBytes As Long) As Long

Declare Private Function GlobalFree Lib "kernel32" (Byval hMem As Long) As Long

Declare Private Function EmptyClipboard Lib "user32" () As Long

Declare Private Function lstrcpyLP2Str Lib "kernel32" Alias "lstrcpyA" (Byval lpString1 As String, _

Byval lpString2 As Long) As Long

Declare Private Function lstrlenLP Lib "kernel32" Alias "lstrlenA" (Byval lpString As Long) As Long

Declare Private Sub MoveMemory Lib "kernel32" Alias "RtlMoveMemory" (Byval strDest As Any, _

Byval lpSource As Any, Byval Length As Any)

Declare Private Function GetFocus Lib "User32" Alias "GetFocus" () As Long 


Private Const CF_TEXT = 1

Private Const GMEM_MOVABLE = &H2&

Private Const GMEM_DDESHARE = &H2000&


Class WindowsClipboard


Public Property Get Contents As String

    Dim hClipboard As Long

Dim LpStrl As Long

     Dim Resultl As Long

    Dim Clipboardstr As String


         If (OpenClipboard(0&) <> 0) Then

            hClipboard = GetClipboardData(CF_TEXT)

            If (hClipboard <> 0) Then

                LpStrl = GlobalLock(hClipboard)

                Clipboardstr = Space$(lstrlenLP(LpStrl))

                 Resultl = lstrcpyLP2Str(Clipboardstr, LpStrl)



                 Clipboardstr = "NULL"

              End If

            Call CloseClipboard()


            Clipboardstr = ""

        End If

        Contents = Clipboardstr

    End Property ' Ends the "Get" method for the "Contents" property


    Public Property Set Contents As String

         Dim lSize As Long

         Dim hMem As Long

         Dim pMemory As Long

        Dim temp As Variant


        lSize = Len(Contents)+1

        hMem = GlobalAlloc(GMEM_MOVABLE Or GMEM_DDESHARE, lSize)

        If hMem = 0 Or Isnull(hMem) Then Exit Property

        pMemory = GlobalLock(hMem)

        If pMemory = 0 Or Isnull(pMemory) Then 


            Exit Property

        End If

         Call MoveMemory(pMemory, Contents, lSize)

        Call GlobalUnlock(hMem)

        If (OpenClipboard(0&) <> 0) Then

            If (EmptyClipboard() <> 0) Then

                temp = SetClipboardData(CF_TEXT, hMem)

             End If

            temp = CloseClipboard()

        End If


    End Property ' Ends the "Set" method for the "Contents" property


End Class





14 Years Ago…

This Sunday it was 14 years ago I stepped onto an airplane in Stockholm, Sweden. In the morning hourse of January 1, 1998, my dad and my sister took me to the airport for my move to the US. I had married Angie back in August, and soon after that she told me she wanted to move back to the US. I applied for and got a resident visa (green card) and got a job with IDG in Boston. Initially we had planned to move to Seattle, but when I was offered the job in Boston I took that one instead.

Some 18 hours later I landed in Spokane, WA, where Angie met me. She had been staying with her mom, who lived in northern Idaho, while I got our appartment in Sweden packed up in 13 moving boxes (that is what I could afford to ship) and I got everything finished at my previous job at IDG in Sweden. My last task was to build an editorial system in Lotus Notes 4.6, that I built in 3 weeks. By the way, this system is still in use today, having survived several attempts to have it replaced with different other publishing systems.

After four days of driving cross-country from Idaho to Boston, we got an apartment and I started working as a full-time Lotus Notes developer on January 7.

Much have happened since. In 2000 we had our son Erik, in May 2002 we moved to Texas and I started working at Deep South as a Sr. Lotus Notes developer, in late summer of 2002 we moved into our first house, and then in July 2003 Angie and I separated and finally divorced aftyer 6 years of marriage.

It is interesting that I now have been working with Lotus Notes almost three times the time I was married, despite all the claims I have seen for many years that "Notes is Dead"…

As always, it will be exciting to see what the coming year brings me, both on a personal and professional level. Perhaps I will finally get the time to learn Xpages and/or Java?




Free Tool: Clean your NAB

As a follow-up to my previous tool that let you analyze the ACL of a database, I built another tool for my admin. For different reasons, we need to keep the mailbox of terminated users, sometimes for a shorter time but sometimes for long periods of time. As far as I understand it, if a traditional approach was used to remove a user from the system, the mail file would also be deleted. So the admin put the terminated user in the Deny Access group and change the ACL of the mailfile to include a manager, supervisor or replacement.

But because of this process, AdminP will not remove the terminated user from all the groups he/she is listed in. When you have hundreds of groups, many of them nested, this could be a real headache. So I was asked to build something simple that allows us to remove one or more specified users from all groups in the Domino Directory. Below is the result. Enjoy!

Update: I tweaked the code slightly, to avoid three separate calls to GetItemValue() and to make a line shorter. The modified code is in the end, where I update the deletelog list.


First I created a form with 3 fields:

Form with 3 fields

‘SaveOptions’ has a default value of “0” (to prevent the form from being saved).
‘Users’ is a Names field, getting it’s values using the addresses dialog. The field is multi-value and using New Line as separator.
‘LogResult’ is a multi-value text field, again with New Line as separator.

Finally I added a button to the action bar to remove the user(s). The Lotusscript code is listed below. It is using my class for mail notifications that I blogged about in November, to send a confirmation to the user running the agent. This is useful for example when you need to log all data changes done to a system.

Use "Class.MailNotification"

Sub Click(Source As Button)
 Dim ws As New NotesUIWorkspace
 Dim uidoc As NotesUIDocument
 Dim session As New NotesSession
 Dim nab As NotesDatabase
 Dim view As NotesView
 Dim doc As NotesDocument
 Dim members As Variant
 Dim newmembers List As String
 Dim delmembers List As String
 Dim users As Variant
 Dim userlist List As String
 Dim user As NotesName
 Dim nmcnt As Integer
 Dim newarray() As String
 Dim ret As Integer
 Dim removelog List As String
 Dim userarr As Variant
 Dim mail As NotesMail 
 Dim mailtext As String
 Dim listname as String
 Dim updated As Boolean

 ' *** Make sure the operator is sure
 ret = Msgbox("Are you sure?",4+32+256,"WARNING")
 If ret = 7 Then
  Exit Sub
 End If

 ' *** Get a list of users in field 'Users'
 Set uidoc = ws.CurrentDocument
 users = Split(uidoc.FieldGetText("Users"),Chr$(13))
 Forall u In users
  Set user = New NotesName(u)
  userlist(Fulltrim(user.Common)) = Fulltrim(user.Common)
 End Forall

 ' *** Get all groups in NAB and process them one by one
 Set nab = New NotesDatabase(session.CurrentDatabase.Server,"names.nsf")
 Set view = nab.GetView("Groups")
 Set doc = view.GetFirstDocument
 Do While Not doc Is Nothing
  Print "Processing " & doc.GetItemValue("Listname")(0)
  Erase newmembers
  Erase delmembers
  updated = False
  ' *** Get members in the group and create a list of the ones to keep
  members = doc.GetItemValue("Members")
  nmcnt = 0
  Forall m In members
   Set user = New NotesName(m)  
   If Iselement(userlist(Fulltrim(user.Common))) = False Then
    ' User is not among the ones to delete
    newmembers(Fulltrim(user.Common)) = Fulltrim(m)
    nmcnt = nmcnt + 1
    delmembers(Fulltrim(user.Common)) = Fulltrim(m)
    updated = True
   End If
  End Forall
  ' *** Build array of members to keep
  Redim newarray(nmcnt) As String
  nmcnt = 0
  Forall nm In newmembers
   newarray(nmcnt) = nm
   nmcnt = nmcnt + 1
  End Forall
  ' *** Write array of new members back to document and save it
  If updated = True Then
   Call doc.ReplaceItemValue("Members", Fulltrim(newarray))
   Call doc.Save(True,False)
   listname = doc.GetItemValue("Listname")(0)
   Print "Updating " & listname
  End If
  Forall dm In delmembers
   removelog(listname) = removelog(listname) & dm & ";"
  End Forall
  Set doc = view.GetNextDocument(doc)
 ' *** We are all done
 mailtext = ""
 Forall rl In removelog
  Call uidoc.FieldAppendText("LogResult", "Group '" & Listtag(rl) & "':" & Chr$(10))
  mailtext = mailtext & "Group '" & Listtag(rl) & "':" & Chr$(10)
  userarr = Split(Cstr(rl),";")
  Forall u In userarr
   Set user = New NotesName(u)     
   Call uidoc.FieldAppendText("LogResult", user.Common & Chr$(10))
   mailtext = mailtext & user.Common & Chr$(10)
  End Forall
 ' Call uidoc.FieldAppendText("LogResult", Chr$(10))
 End Forall
 Set mail = New NotesMail()
 mail.MailTo = session.CommonUserName
 mail.Subject = "[Notification] - Users removed from NAB"
 Call mail.AppendText(mailtext)
 mail.Principal = "IT Programs"
 Call mail.Send()
 Msgbox "Done removing specified user(s) from Domino Directory.",64,"Finished"
End Sub

Norwegian TV pwned

The news in the Norwegian TV channel NRK recently talked about testing the vision for elderly, the illustration for the test was picked from teh Internet. Obviously nobody looked closer at it before the broadcast…


I am sure most readers of this blog understand what's so funny… The news anchors did not.



Why my next phone will not be a Blackberry

I like my Blackberry. I currently have a Blackberry Bold 9700, and I am happy with many things. But it is almost 2 years old and it is time to look for a replacement. And after almost 4 years of being a Blackberry user, my next phone will probably be something else.

But why? The Bold has been praised multiple times by people like Vowe. The screen is excellent. It has a real keyboard, and most people (including me) think that is the best physical keyboard on the market. Well, there are several reasons.

I have upgraded the operating system from version 6 5 to version 7 6. However, the new version is not as good as the older one. Some functions are very nice, but at the same time, it uses more memory, and I suspect it got memory leaks left and right. I constantly run out of memory when loading webpages, and I see the clock/hourglass icon way too often. It also locks up frequently, sometimes just for a few seconds, sometimes so hard I have to pull the battery. Talking about pulling the battery, Blackberry users are used to do that in a nearly daily basis, especially if you want to clear the memory. But there is an app for that.

This brings us to another big issue with Blackberry. Apps. Or rather the lack of apps. Yes, there are a number of apps in the Blackberry App World. But if you look at different websites (everything from news to specialized services), chances are that they have an app for iPhone and one for Android. But very few places have an app for Blackberry. Next time you visit a website that offers apps, see what your options are. One app I use all the time is SocialScope, previously only available for Blackberry. But now there is an Android version available as well.

Other issues with Blackberry 7 6 is that the UI looks old. There were no big changes between version 6 5 and 7 6, the icons still looks boring. In BB5 there were a number of themes one could download, and also a tool that let you customize your screen/look (if you were a graphics designer). There are themes for BB6 as well, but my favourite themes did not work after the upgrade and have not been updated.

Then we have posts like this one by Darren Duke and this one by Vowe. The question is if RIM will still be around for the next two years, if I get a new phone from them now…

So it is a combination of all this, together with a wish to get a bigger screen and better performance that makes me consider a different smartphone platform. Personally I do not like being locked in to the Apple eco system. I don't have an iPod, but a MP3 player which can be connected as any other USB device, and my files (in many formats, like mp3, flac, wmv, avi, divx and mpg) can simply be dragged over. I want that for my phone too, to have the freedom to do what I want, use the files I already have.

So my next phone will most probably be an Android. I have been looking at (and reading about) the Google Nexus, but there are a few things that I don't like with it, most of all the lack of expansion (no DS-card support). Samsung Galaxy S II Skyrocket is interesting, as is the new LG Nitro. But I want the latest version of Android, Ice Cream Sandwich, which is still not available on very many phones. So it will be very interestingto see what the lineup looks like after New Years…

Update: I had the wrong OS numbers, it has now been corrected.






Script Library Issue

For a product that just turned 21, one would expect that all childhood issues were gone. Of course, I am (for now) stuck with a 17 year old slightly temperamental teenager… Anyway, I just ran into a strange issue, related to classes and code inherited from other templates.

Template A contains a script library with a class I wrote, called "AgentLog". It is used to log information about agents running in different databases. So obviously the script library need to reside in each database where I want to do the logging. Let’s say there are two, based on Template B and C. In an attempt to be clever, I simply copied the script library from Template A to Template B and C, and answered "yes" on the question if I wanted to inherit changes when the code in Template A is changed.

I then add some calls to the script library in template B and C, and push the changes out. Everything works great. A week or two later, I decide that I want to add some more info to the logging. I add one public string variable to the class, add a reference to it in one method called Terminate() and add a new method called AddMessage(). The new code is marked in red.

Class AgentLog
Private session As NotesSession
Private logdb As NotesDatabase
Private logdoc As NotesDocument
Private running As Integer
Private tstart As Long  ‘ Ticks at start
Private tend As Long  ‘ Ticks at end
Private tps As Long  ‘ Ticks per second (normally 1000)
Private message As String  ‘ Text to store in log entry, e.g. number of docs processed
 Public Sub AddMessage(txt As String)
  Me.message = Me.message + txt
End Sub

Public Sub Terminate()
  Dim seconds As Integer
  If logdoc Is Nothing Then
   Exit Sub
  End If
  tend = Getthreadinfo( THREAD_TICKS )
  seconds = (tend – tstart) / tps
  Call logdoc.ReplaceItemValue("EndTime", Now() )
  Call logdoc.ReplaceItemValue("Seconds", seconds )
  Call logdoc.ReplaceItemValue("Message", message )
  If running = True Then ‘ Check if not terminated gracefully
   Call logdoc.ReplaceItemValue("Terminated", "Yes" )   
  End If
  Call logdoc.Save(True,True)
End Sub

Nothing special, right? I did not even modify New() at all. All this was done in anticipation of adding the additional logging later in Application B and C (based on the templates B and C). The updated Template A was put in production, and the next night the update process refreshed the design of Template B and C with the new code, and they in turn updated Application B and C.

Now the users suddenly got an error message when code using the script library was launched: "Type mismatch on external name: AGENTLOG". I tested it and got the same error. I decided to turn on the debugger to see where exactly it happened. And of course then it worked! Debugger turned off, error again. I recompiled all Lotusscript in the application, and it worked with no debugger on. I then recompiled all code in Template A, refreshed design of Template B and C and recompiled all code in Template B and C. Despite this, the next day we had the same problem…

Finally I ended up not linking the script library from Template A to Template B and C. I will just have to copy it over again every time I make a change to every template where I use it. Anyone got any ideas what the problem might be?.



Re: Using Lotusscript lists to increase performance

In response to: Using Lotusscript lists to increase performance

I now modified the code to use NotesViewEntry.ColumnValues() to get the value I am looking for. I also had to add a column displaying that field in my view.
The code actually took longer, 370 seconds, but all that was in the calls to doc.Save(). The actual lookup went from a total of 15 seconds down to just below 5 seconds.

Using NotesViewEntry.ColumnValues()

So let’s remove the time-consuming doc.Save() command, just to see what the actual execution time is. Here is the result for my four versions of the same import agent:

Agent Time
 Individual lookups for each entry imported  370 sec
 Building list of documents using view lookup/doc.GetItemValue() before importing  55 sec
 Building list of documents at first use, using view lookup/doc.GetItemValue()  54 sec
 Building list of documents at first use, using viewentry lookup/entry.ColumnValues()  36 sec

By removing the print statement that give feedback about the progress (printing every 100 entries), additional 3 seconds would be saved.

So there is a substantial performance benefit when you don’t have to open the actual document and read a value, but can use a view column value instead. If you need multiple values from one document, one easy way is to combine all values into one column, separated with a character that you will never enjounter in the data (I am partial to the ~ character, it also makes it easy to read) and then use Split() to get the individual values.




Trend Micro: Google is most insecure

According to Trend Micro's Third Quarter Threat Report, Google had 82 vulnerabilities during the third quarter 2011. Oracle had 63, followed by Microsoft with 58, Apple with 49, Adobe with 42 and Mozilla and IBM both with 39 during the same period.

In other news: University of Dayton and General Motors are moving from Lotus Notes to Google Mail, while the CIO at the City of Los Angeles demands that Groupwise "be hauled out of its coffin, given a quick slap and defibrillation, and pressed back into service" to replace Google Mail and Apps. Link to letter here, another article here.

According to Consumer Watchdog, the rollout [in Los Angeles] has hit snags, with the police deciding that security on the vaunted platform isn’t adequate for users in the City Attorney Criminal Branch, fire department arson investigations, public safety users, parking police, street services investigations, parks rangers, and “any other City entities that access criminal history data”.





Free Tool: Analyze ACL in Notes Application/Database

Yesterday my network admin asked me if I could write a simple tool that could provide him with with a spreadsheet of what users had access to a certain database, and through what groups and roles. A couple of hours later I had created an agent that analyze the ACL and identify the users who can access it. The result is presented as a CSV file.

I am sharing the code below. It is pretty straight forward. As you can see, I am using lists to hold the data for easy export later to CSV. Run the code with the Lotusscript debugger turned on, and put a breakpoint before the CSV export starts, and you can see how the data is stored in the lists.

The function ExpandGroups() is called recursively to drill down, if the group contains additional groups. This function also use a lookup into a custom view, (LookupPeople), that we have in our corporate NAB, I am sure you can modify this code with something that works for you.

Enjoy! As always, use the code on your own risk, no warranties, etc.

    Agent Export ACL Info to CSV
    Created Nov 14, 2011 by Karl-Henry Martinsson/Deep-South
    Description: Read ACl for specified database and create a
    CSV file with info about each user's access (roles, groups,
    delete access, access level).

Option Public
Option Declare

Dim nab As NotesDatabase

Type RowData
    role As String
    group As String
    username As String
    deletedoc As String
    level As String
    levelno As Integer
End Type

Class GroupData   
    Public roles List As String
    Public Sub New()
    End Sub
End Class

Class PersonData
    Public accesslevel As Integer
    Public roles List As String
    Public deletedoc As boolean
    Public accessthrough List As String
    Public Sub New()
        me.deletedoc = False
    End Sub
    Public Sub SetAccessLevel(level As Integer)
        If me.Accesslevel<level Then
            me.AccessLevel = level
        End If
    End Sub

    Public Function GetAccessLevelText()
        Select Case me.AccessLevel
            Case 0 : GetAccessLevelText = "No Access"
            Case 1 : GetAccessLevelText = "Depositor"               
            Case 2 : GetAccessLevelText = "Reader"
            Case 3 : GetAccessLevelText = "Author"
            Case 4 : GetAccessLevelText = "Editor"
            Case 5 : GetAccessLevelText = "Designer"
            Case 6 : GetAccessLevelText = "Manager"
        End Select
    End Function
End Class

Class RoleData
    Public groups List As String
    Public Sub New()
    End Sub
End Class

Sub Initialize
    Dim ws As New NotesUIWorkspace
    Dim session As New NotesSession
    Dim db As NotesDatabase
    Dim pview As NotesView
    Dim pdoc As NotesDocument
    Dim acl As NotesACL
    Dim entry As NotesACLEntry
    Dim person List As PersonData
    Dim group List As GroupData
    Dim role List As RoleData
    Dim users As Variant
    Dim row List As RowData
    Dim cnt As Long
    Dim groupname As String
    Dim filename As String
    Dim rowstr As String
    Dim dbname As String
    Dim servername As String

    servername = InputBox$("Enter server for database:","Select Server")
    If servername = "" Then
        Exit Sub
    End If
    dbname = InputBox$("Enter full path of database:","Select Database")
    If dbname = "" Then
        Exit Sub
    End If
    set nab = New NotesDatabase(servername,"names.nsf")
    Set db = New NotesDatabase(servername,dbname)
    Set acl = db.ACL
    Set entry = acl.GetFirstEntry()
    Do While Not entry Is Nothing
        If entry.Isgroup Then
            If IsElement(group(entry.Name))=False Then
                Set group(entry.Name) = New GroupData()
                ForAll r In entry.Roles
                    group(entry.Name).roles(r) = r
                End ForAll
            End If
            users =    ExpandGroup(entry.Name)
            If IsList(users) then
                ForAll u In users
                    If IsElement(person(u))=False Then
                        Set person(u) = New PersonData()
                    End If
                    Call person(u).SetAccessLevel(entry.level)
                    If entry.Candeletedocuments Then
                        person(u).deletedoc = True
                    End If
                    person(u).accessthrough(entry.Name) = entry.Name
                    ForAll r In entry.Roles
                        If FullTrim(r)<>"" then
                            person(u).roles(r) = r   
                        End if
                    End ForAll
                End ForAll
            End If
        ElseIf entry.IsPerson Then
            If IsElement(person(entry.Name))=False Then
                Set person(entry.Name) = New PersonData()
            End If
            Call person(entry.Name).SetAccessLevel(entry.level)
            If entry.Candeletedocuments Then
                person(entry.Name).deletedoc = True
            End If
            person(entry.Name).accessthrough("ACL") = "ACL"
            ForAll r In entry.Roles
                If FullTrim(r)<>"" Then
                    person(entry.Name).roles(r) = r
                End if   
            End ForAll
        End If
        Set entry = acl.GetNextEntry(entry)   
    ForAll g In group
        ForAll rr In g.roles
            If IsElement(role(rr)) = False Then
                Set role(rr) = New RoleData
            End If
            role(rr).groups(CStr(ListTag(g))) = ListTag(g)
        End Forall
    End ForAll
    ' *** Time to export the data
    cnt = 0
    Set pview = nab.GetView("(LookupPeople)")
    ForAll p In person
        ForAll gg In p.accessthrough
            groupname = gg
            If IsElement(group(groupname)) And groupname<>"ACL" Then
                ForAll r2 In group(groupname).roles
                    cnt = cnt + 1
                    row(cnt).username = ListTag(p)
                    row(cnt).group = groupname
                    If p.deletedoc then
                        row(cnt).deletedoc = "Y"
                        row(cnt).deletedoc = "N"
                    End If   
                    row(cnt).levelno = p.accesslevel
                    row(cnt).level = p.GetAccessLevelText()
                    row(cnt).role = ListTag(r2)
                End ForAll
            End If
        End ForAll
    End ForAll
    filename = "c:\ACL_"
    filename = filename & Replace(Replace(dbname,"/","-"),"\","-")
    filename = filename & ".csv"
    Open filename For Output As #1
    rowstr = |"UserRole","Group","User Name","Del","Level","Access"|
    Print #1, rowstr
    ForAll x In row
        rowstr = |"| & x.role & |",|
        rowstr = rowstr & |"| & x.group & |",|
        rowstr = rowstr & |"| & x.username & |",|
        rowstr = rowstr & |"| & x.deletedoc & |",|
        rowstr = rowstr & || & x.levelno & |,|
        rowstr = rowstr & |"| & x.level & |"|
        Print #1, rowstr
    End ForAll
    Close #1
    MsgBox "ACL exported to " & filename,,"Finished"
End Sub

    Function ExpandGroup
    Created Nov 14, 2011 by Karl-Henry Martinsson/Deep-South
    Description: Returns a list of users in specified group in NAB
Function ExpandGroup(entryName As string) As Variant
    Dim nabview As NotesView
    Dim nabdoc As NotesDocument
    Dim pview As NotesView
    Dim pdoc As NotesDocument
    Dim uname As NotesName
    Dim tmplist As variant
    Dim userlist List As String
    If FullTrim(entryName) = "" Then
        ExpandGroup = ""
        Exit Function
    End If
    Set nabview = nab.GetView("Groups")
    Set nabdoc = nabview.GetDocumentByKey(entryname)
    If nabdoc Is Nothing Then
        ExpandGroup = ""
        Exit function
    End If
    ForAll n In nabdoc.GetItemValue("Members")
        If Left$(n,3)= "CN=" Then
            Set uname = New NotesName(n)
            userlist(uname.Common) = uname.Common
            Set pview = nab.GetView("(LookupPeople)")
            Set pdoc = pview.GetDocumentByKey(CStr(n))
            If Not pdoc Is Nothing Then
                userlist(CStr(n)) = CStr(n)
                tmplist = ExpandGroup(CStr(n))
                If IsList(tmplist) Then
                    ForAll t In tmplist
                        userlist(t) = t
                    End ForAll
                End If
            End If   
        End If
    End ForAll
    ExpandGroup = userlist
End Function



The DRM graveyard: A brief history of digital rights management in music

Very interesting.

There are more than a few reasons digital rights management (DRM) has been largely unsuccessful. But the easiest way to explain to a consumer why DRM doesn't work is to put it in terms he understands: "What happens to the music you paid for if that company changes its mind?" It was one thing when it was a theoretical question. Now it's a historical one. Rhapsody just had the next in a line of DRM music services to go–this week the company told its users than anyone with RAX files has unil November 7 to back them up in another format or lose them the next time they upgrade their systems.

Full story here.  As Vowe says: DRM is bad for the customer.




LS12 Session Proposal – Error 500 when submitting

I just tried to submit a session for Lotusphere 2012. I filled out the form, and pressed the "next" button as instructed.

This is the result:

Error 500

I know a few good Notes/Domino developers out there that could create a working form quickly… 🙂


Update: I called the support number on the page and was asked to mail a screenshot. I did and they said it worked when they tested, but it still did not work for me. There is documentation about the error here. The documentation indicates that it has to do with data being too long, or wrong data type. All fields on the form are below the posted limits (close, but at or below). 997 characters for the main abstract, 350, 347 and 328 characters for the three bullet points and 92 characters for the title.
Hint to the developer of the form: best practices is to do validation before submission, especially if the server will blow up if anything is invalid…


Update 2: I was finally able to submit it this afternoon. Not sure if it was because I used Firefox (unlikely, since it seemed to be a server issue) or if IBM fixed it.



Re: Lotusscript: Mail Notification Class – followup

In response to: Lotusscript: Mail Notification Class

I also want to bring attention to how I am using lists in the class. The different sets of recipients (To, CC and BCC) are all stored in lists. The name/email is both the list tag and the value, e.g. p_sendto(“texasswede@gmail.com”) = “texasswede@gmail.com”.

There are several benefits of using lists. First of all, it is very easy to add items to a list. When using an array you need to redim it (using ReDim Preserve in order not to lose existing data) before adding items.
Secondly, with a list it is trivial to remove one specific item, and you can easily loop though the list and build an array to pass into the fields on the mail document later. It is not nearly as easy to remove items from an array, even if you can clear out an entry and use FullTrim() to remove any empty items.

This is just one more example of how powerful lists are.



End of content

No more pages to load