Nostalgia

I am sure most of you who started programming around the same time that I did (in the first few years of the 1980's) at one point carried something like this in your wallets:

ASCII Table

 

1 Comment

Things to think about when programming in Notes

Inspired by some of the posts in the DeveloperWorks forums and on StackOverflow, I thought I would post some more basic concepts and how I handle them. I am not saying my way is the best way, this is just what works for me. I am sure there will be more posts in the future”…”

I will also mention a few other things I noticed while reading the code posted in the forums.

 

Retrieve something that doesn´t exist

The question is how to identify what dates there are no documents created for. This is where lists are very useful. Richard Schwartz answered this question and posted some good code.
Rich suggests to create a list of dates, with each list item having an initial values of false, and then loop through the documents. As each document is processed, the value of the corresponding list item is changed from false to true. You can then go through the list and see which dates still have a value of false, those dates are missing documents.

My version of the same code is to actually delete the list item you have a match for, instead if setting it to true. In the end you have a list of just the items of dates without a corresponding document.

 

Write readable code

This could be a blog entry all by itself. But I notice that much of the code in the DeveloperWorkds forums is hard to read”´”. Partially because any tabs or multiple spaces used for indenting the code is stripped out, but also because the posters don´t write easy-to-read code.

Variable names are often not descriptive:

Dim db1 As NotesDatabase
Dim db2 As NotesDatabase

vs

Dim thisdb As NotesDatabase
Dim nabdb As NotesDatabase

Which one is easier to understand? In my opinion (and I am sure you agree) the second variant. Also function names and other variables should be named so you understand what they do and what kind of data they contain.

Comments are mostly non-existing. It is not that hard to add some comments to the code that explain what the code is doing. But don´t explain every line of actual code (it should be self-explanatory, if variables are named correctly), explain what a particular section of code is intended to do.

Here is a section of code from an agent I wrote earlier this week:

'*** Read PhotoUNID field in LossControl document'*** and build a list of the UNID values in the fieldphotoUNID = lcdoc.GetItemValue("PhotoUNID")(0)If photoUNID<>"" Then '*** Create array of values and put into photolist tmparray = FullTrim(Split(photoUNID,";")) ForAll t in tmparray If t <> "" Then photolist(t) = t End If End ForAll End If

The comments above will help the next person to look at the code to quickly understand what it is intended to do.

 

More on variables

Use Option Declare/Option Explicit. This will find many errors, especially for more inexperienced programmers, where variables are misspelled or missing, something that is a very common reason for posts in the forums.

Another thing that a surprisingly large number of posters seem to struggle with is how to correctly declare variables. I see many cases where several variables are declared on one row, but only the last one has the data type. The author of the code was thinking it would apply to all the variables:

Dim FirstName, LastName, Street, City, PostalCode, State as String

This will declare State as String, but all other variables as Variant. This is not unique for Lotusscript, Visual Basic (on which Lotusscript is based) works the same way.

I always declare each variable on a separate line. This makes it easier to find a particular variable if I am looking for it. I also declare all variables in the beginning of the code/function, again to make it easier to find it in the future. Finally I order the declarations in the same order:

1. Notes UI classes (so they are easy to locate, in case I need to rewrite the code to be used in a server-based agent.

2. Notes backend classes. I always declare them in the order they are being used, as this also is how the classes are structured.

3. Variables and custom classes, in the order they are used.

Here is an example, from the same agent as above:

 Dim session As New NotesSession Dim photodb As NotesDatabase Dim lcdb As NotesDatabase ' LossControl DB Dim lcview As NotesView Dim lccol As NotesViewEntryCollection Dim lcentry As NotesViewEntry Dim lcdoc As NotesDocument Dim photodoc As NotesDocument Dim rtitem As Variant Dim rtnav As NotesRichTextNavigator Dim rtlink As NotesRichTextDocLink  Dim cnt List As Long Dim photoUNID As String Dim unid As String Dim photolist List As String Dim verifiedlist List As String Dim tmparray As Variant Dim photos As String

As you can see, I also put a comment there, to explain what lc stands for.
I also try to use a list for counters, instead of having a number of separate variables. Doing that makes the code easier to read and understand, despite it actually being longer:

 cnt("Total") = lccol.Count cnt("Processed") = 0 cnt("Updated") = 0 cnt("UpdatedPhoto") = 0
 cnt("Processed") = cnt("Processed") + 1 If cnt("Processed") Mod 10 = 0 Then Print cnt("Processed") & " of " & cnt("Total") End If

See how easy that code is to read?

 

Use the Debugger

I see many messages where the poster is getting an error message, or an unexpected result (or no result at all). Sometimes a large chunk of code is posted, but no indicator where the error happens.

It seems like very few (at least of the obviously less experienced programmers) use the debugger at all. In most cases they would quickly find the problem that way, instead of asking why they get “object variable not set” or “type mismatch” errors somewhere in 100 lines of code”…”

Yes, the debugger has limitations, and it could use some new features (like breaking when a particular variable has a specified value or match an expression), but it is a huge help even in the current form.

 

Understand Data Types

Many problems are because the programmer did not understand what data type different functions returns, or even (in some cases) what the different data types means. One poster (I can´t find the post right now) had code like this:

 Dim x As Integer x = 0 x = x + 3.5 MsgBox x

He was the surprised that the message box displayed the value 4… I think understanding data types is a requirement of being a programmer, even if the language you work with is forgiving or don´t require variables to be declared.

 

Analyze the problem

Another common issue I see is that it seems like the programmer just got an assignment and started to write code, without thinking through what the actual process is going to be. He/she often write him/herself into a corner, or is so focused on solving it with existing knowledge (e.g. “has to be @Formula language”), that the difficulty level of the task approaches impossible. Or the code will be extremely convoluted.

Think through the problem, break it down into small problems/steps. Break each of those down into even smaller steps, etc. Finally you have a good specification, and often even pseudo code. It m
ay be that the user requesting the program/functionality (a.k.a. stakeholder) is saying how he want it to be done, but that is really not the stakeholders responsibility. He/she should just explain what the end result should be, and the developer will design the best solution.

I have examples where a manager comes to me and asks for a report “in Excel” of data in a Notes database. That is because the manager in this case was used to working in Excel, and thought of how Excel displays data as the way he wanted it.
I could very easily create a report directly in Notes, displaying exactly the same information. Since I asked what the end result was supposed to be, and how the data was supposed to be used (and by whom), I could avoid Excel altogether and built a pure Notes solution.

This is where experience comes in, things like that is not something you can just pick up at college/university. If you don´t have the analytical/problem solving skills, you will struggle as a programmer. You might be able to write code under strict guidance, or you might even be able to eventually complete the assignment, but it will most probably not be the best/fastest solution, even if the code will work.

Two good blog entries are Separating Programming Sheep from No-Programming Goats (CodingHorror, July 2006) and Why Can´t Programmers.. Program? (CodingHorror, February 2007). Programming consists of problem solving and analytical skills, fundamental skills (like data types, how functions works, recursion different kind of branching/looping), as well as understanding the language and platform you use. If you are missing any of those things, you will probably not be a very good programmer.

 

0 Comments

Movie Review: Looper

Last night I went to see Looper. I had actually not heard much about it, but I looked it up online real quick and at least it sounded like a good premise for a movie. I have always enjoyed sci-fi, and especially time-travel.

The premise is that Joe is a "looper", a contract killer in a near future (2044). About 30 years after that, time-travel has been invented, but declared illegal. So only the biggest crime syndicates have access to time-travel, and they use it to get rid of people. The send the victim back 30 years in time, bound and gagged with a hood over their heads. The looper promptly kills them and disposes of the body. The looper is paid with silver attached to the victim. Occasionally the future version of the looper is sent back, who kills his older himself. The is called "closing the loop".

Joe (played by an excellent Joseph Gordon-Levitt) is a looper. One day his future self (Bruce Willis) shows up, but promptly escapes. In the future, a new gangster boss, "The Rainmaker" has taken over, and he is closing the loop on all loopers. In doing so, the future Joe lost his wife, and he is now looking to prevent this by finding the young Rainmaker in the past and kill him.

I truly enjoyed this movie, it was absolutely much better than I had expected, and it makes you think more than the average movie. The story is clever and it works. You see influences of both Back to The Future and The Terminator in the story, as well as to Carrie and X-Men (with the concept of telekinesis), but it all fits well into the story.

I would highly recommend this movie. However, it´s not a movie for kids, due to violence (and for sensitive Americans, some nudity).

 

0 Comments

Are inexperienced developers the death of Notes?

Lately I have been more active in the IBM DeveloperWorks forums, as well as on StackOverflow, trying to help people with development problems. As I am just myself starting with Xpages, I been staying in the forums for "classic" Notes development.
I have noticed a trend, based on the postings. It seems like there is a substantial number of new developers who are not very familiar with Notes/Domino development. They sometimes think Domino works like a relational database.
There are then several who are posting about very simple things, that can easily be found in the online help, or by looking at the properties for an element. Like how to extend the last column in a view to use all available space.
There was even one user asking about how to duplicate a specific @Formula in Lotusscript, when the help file got a cross reference to the class and method to use…

There are others who does not seem to even understand the basics, either when it comes to programming in general or specifically of Notes/Domino.
Some of them don't understand data types. They declare a variable as integer, then make a calculation that results in a value of say 3.5, and is then wondering why the result is 4.
Others don't understand the difference between strings and variables, they are surprised when @SetField("myField"; "myField + 1") does not give them the expected result (the value in the field ‘myField’ increased by one).

On StackOverflow it is possible to see what other areas the user posted in. Some of the users seems to have a background in Java, SQL, .NET or other platforms. My guess is that they been thrown into a Notes projekt after their company took on a new development project, with the hope that they could learn it quickly. I think this could be dangerous, from some of the code I have seen, the lack of experience and understanding of the Notes/Domino platform will cause sub-standard or slow code, which of course will make executives think that Notes is a bad development platform. After all, if the expensive consulting company (or the off-shore based development house with all developers being at least Ph.D.) can't write fast and good code, the platform must be at fault, right?

Another thing I noticed over the last year or so is that in the Notes-related groups on LinkedIn, there has been a number of requests for the answers to the IBM certification tests. They have originated from both some big consulting companies and from within IBM. None of them were from the US (or Europe, if I remember correctly), but from countries more traditionally associated with outsourced or "off-shore" development. My guess is that the companies want their developers to be certified on paper, as they can either charge higher rates, or pass themselves off as being “experts” on the platform.
A number of the questions in the DeveloperWorks forums were posted under names that often are associated with the same countries/regions.

What I think we are seeing is the result of American and (in some part) European companies using cheaper off-shore development companies in order to save money. What they don't think of is that, unless the developers has a good knowledge of the product, that a local developer with many years of experience will create the same or better result in a much shorter time. So even at a higher hourly rate, the end result will be less expensive as well as better.

I want to make it clear that I don't think all developers in the countries typically associated with off-shoring (India, China, Russia, the Baltic states, Brazil, etc) are bad programmers. I know very competent developers from several of those countries, and I know some not-so-good developers in Europe and North America.

What I am afraid of is that off-shore development companies takes on Notes-projects, expecting (or hoping) their staff will quickly learn the product/platform and quickly develop the requested solution. In the process they are making Notes look bad as they don't understand the platform.
At the same time, the companies that is purchasing the solution are just looking at the hourly rate, and perhaps an initial estimate of how quick and inexpensive (due to low hourly rates) the project is promised to be completed. In the long run, I fear that Notes/Domino as a platform will suffer because of this.

The project management triangle  is still true:

You are given the options of Fast, Good and Cheap, and told to pick any two. Here Fast refers to the time required to deliver the product, Good is the quality of the final product, and Cheap refers to the total cost of designing and building the product. This triangle reflects the fact that the three properties of a project are interrelated, and it is not possible to optimize all three – one will always suffer. In other words you have three options:

Design something quickly and to a high standard, but then it will not be cheap.
Design something quickly and cheaply, but it will not be of high quality.
Design something with high quality and cheaply, but it will take a long time.

ProjectTriangle

Of course, an experienced Notes/Domino developer can make the rule somewhat invalid, but it requires extensive experience. 🙂

I don’t have a good solution. Perhaps companies thinking about outsourcing development need to be more diligent at selecting developers, requesting details about their previous experience, etc. Perhaps they need to ask more questions, including how many years of Notes/Domino experience the developers have. Personally, I would not suggest hiring a consulting company who haven’t had a presence at Lotusphere or at least had some of their developers speak there or at any of the LUG-conferences around the world. Many of the best Notes developers also got blogs where they post code and/or information, I would require a link to some blogs as well, so I could judge the quality of their code.

 

0 Comments

Neil Armstrong dies at age 82

NeilArmstrong_Lotusphere2007

Neil Armstrong. The first man on the moon. Speaker at Lotusphere 2007 (where I took the picture above). Self-proclaimed geek.

I always loved reading about space growing up, and I read everything from sci-fi to real stories. One of my favorites was Carrying the Fire: An Astronauts Journey by Michael Collins (the third Apollo 11 astronaut), which I read in Swedish translation (as I was just 7 or 8 years old at the time).

So when Neil Armstrong stepped up on stage at the 2007 Lotusphere Opening General Session, that was the coolest speaker ever. And he was not just another celebrity, he was interesting and funny. My favorite quote was this (as far as I rememeber):

The scientists came up with an experiment to measure the distance from Earth to the moon, using a laser. But in order to do that, they needed a mirror placed on the lunar surface. I was the service technician tasked to put the mirror there.

 

Best speaker ever, and with Lotusphere being renamed to Connect, he will keep that title forever.

 

0 Comments

Review: Samsung Galaxy S3

Last Thursday I got the Samsung Galaxy S3 I pre-ordered back in the beginning of June, and I have now been playing with it for a few days. There are of course other reviews (mainly of the international version) and overviews of the phone, so I will not list all the features and functions here.
As I am in the US, I received the North American version. It differs from the international version in that it has a dual-core Snapdragon S4 processor and 2GB system memory, instead of the Samsung’s own processor Exynos 4 Quad and 1 GB memory. This is due to the latter processor not supporting the North American LTE networks.

Samsung Galaxy S3 vs. Blackberry Bold 9700With this phone I am also moving from the Blackberry platform to Android. I have been looking forward to getting a nice big screen and a more powerful phone, but at the same time my biggest fear was the on-screen keyboard. The times when I have been using an iPhone or played with older Android phones in the store, I did not feel like I would be able to type as fast as with the Blackberry’s excellent physical keyboard.
Samsung Galaxy S3 LockScreen

I am coming from the Blackberry Bold 9700, with OS 5. The phone is about 2 years old, and originally came with OS 4. After I upgraded, the phone became more and more sluggish, and I constantly ran out of memory, in certain applications as well as when browsing the web. The GPS started taking longer and longer to get a fix, it could take me 2-3 minutes (if it even got the position) if I was indoors. The screen on the blackberry is also tiny compared with today’s phones, even if it was a very good screen when it came out.
So it was about time for me to get something more modern.

I was very pleasantly surprised with the keyboard on the Samsung Galaxy S3. As soon as I started typing, the correct text came out. The predictive text works very well, as long as I use English. I know there are other keyboards (like Swiftkey 3 that Mitch Cohen blogged about last week) where I can set different languages, so that is not a big deal right now.
The 4.8 inch Super AMOLED screen is just gorgeous, and features a resolution of 720×1280 pixels. The internal memory in my phone is 16GB (32GB and 64GB models are also available or coming soon). The memory can be expanded using microSD cards up to 64GB, and in some markets customers get a free 50GB DropBox account. My carrier, AT&T, opted out of this promotion. I already use DropBox, SugarSync, SkyDrive and Google Drive, so after downloading clients for those services, I can now easily transfer my pictures to my cloud storage of choice.

S3_FoldersJust like on the Blackberry, and most other smart phones, the Galaxy S3 has several screens where I can place widgets, icons for applications, and folders containing additional icons. This way it is easy to organize all my apps. On the Blackberry I also used folders, but the lack of available memory caused me to eventually remove most apps.

The default setup came with a number of widgets, but I removed most of them from the screens and opted to just use icons. The lock screen shows the date and time, the current weather, and four icons for applications you want instant access too. To unlock, you swipe your finger over the screen, or swipe any of the four icons to launch that particular application.

I have already modified my phone by adding a custom wall paper, and as I mentioned above, I have organized the icons and widgets the way I want them. To the left you can see a folder open, showing the four applications located in it, in this case IBM Lotus Traveler.

S3_P1

I currently have four screens of icons, of the seven I can have. The first one, the "home screen, is where you end up after unlocking the phone on the lock screen. At the bottom you have five icons of the most frequently used functions, they stay on ever page. The the top of the screen is a notification area, with small icons indication new mail, text messages, twitter messages, etc. It also shows the status for network/wireless connection, battery status, as well as time.

S3_Keyboard

As I mentioned, the keyboard is very impressive, and it exceeded my expectations. I had assumed that I would make a lot of typos, but the predictive text works very well. Or perhaps it is me being too predictable… But the result is that I have very few errors when I type. There are a few small issues, mainly how question marks and similar characters works and that there is no support for Swedish. But as I plan to evaluate a couple of other keyboards, that is not anything that bothers me.

As you can see to the left, when I start typing, suggestions show up above the keyboard. In most cases the suggestion is correct, but in case you want exactly what you typed, the option furthest to the left is what you entered.

You can also see the speech recognition icon to the left of the space bar. I have not used it very much. Speech recognition is of course available everywhere you would use a keyboard, but also on other places, like the S Note application. I have not had time to test the S-Voice yet, nor the face recognition unlocking of the phone or a few of the other advanced functions that is available in this phone.

But I did use the phone to call with. The sound quality is excellent, much better than on my Blackberry. From what I read online, it has active noise cancelling.

I also tested the web browser. As opposed to the Blackberry, it actually load every page I tested.

S3_WebBrowser

The browser is fast (especially on wifi or 4G LTE) and seem to render all pages I tested perfectly. However, I created some bookmarks, and a few hours later they were gone. I am not sure what I did, but now the bookmarks seem to stay. The browser support Flash, of course.

2012-06-24 09.36.53

Talking about speed, I live and work in the Dallas-Ft Worth area, where AT&T have their 4G LTE network available. And it is fast, as you can see to the left.

The one issue I see with the phone is the battery. Despite having a 2100 mAh capacity, it usually lasts only to about 3pm. However, I been using the phon
e extensively, and I may need to tweak some setting. I have no power saving settings turned on, and usually run either wifi or bluetooth. Since I have 4G coverage, that also uses more battery. So one of my first purchases was a portable charger…

So the summary is that this is an amazing phone, and that my worry that the keyboard would annoy me was not an issue. I am very happy with the phone, just wishing the battery lasted a little bit longer.

 

0 Comments

Irony: FunnyJunk lawyer criticizing the same actions as his client engage in

 In a blog entry from 06/23/2010, Charles Carreon (the lawyer who is suing The Oatmeal), posts about the outcome of the case Viacom vs. Google. Youtube, owned by Google, allowed their users to upload copyrighted material, and they then benefited financially from this through the sale of ads on the site. Exactly the same activity that his client FunnyJunk engages in.

As that blog entry in retrospect is somewhat embarrassing for Mr Carreon, he took that down. But since the internet never forgets, Google still got the page cached. Some interesting quotes:

If Google can generate ad revenue by taking in every kind of content without distinction, and make money on the infringing attractions, then Google can “work the float,” and always have enough infringing content to keep its blood pressure up at the expense of copyright holders. The only way that content owners can act proactively is by implementing digital “fingerinting technology” through the “Claim Your Content” system that Google uses as its only screening mechanism. Fingerprinting your content is not, however, cheap.

Please don’t take me for a copyright hawk, but this seems like a ruling that benefits a company that has made a habit of turning other people’s work into their payday, and is being encouraged to keep on doing it.

Here is an image of the page as well, as retrieved from Google this morning:

Blog entry by Charles Carreon - click for higher resolution

Click image for larger/high resolution version. Source: Google cache

Disclaimer: The blog entry is reproduced under “fair use”.

 

 

 

0 Comments

How to make enemies (and a fool of yourself) on the internet

This last week we have seen two high profile examples of how you can screw up and make a total idiot of yourself on the internet. With social networks like twitter and Facebook, news spread quickly, and if it is perceived that there is something unfair going on, expect furious people to share it. When you or I, with perhaps a couple of hundred Facebook friends and twitter followers, post about it, it will still spread, but slowly. But when people like Jamie Oliver (2.5 million followers) or Neil Gaiman (1.7 million followers) tweet about it, things start spreading like wild fire.

 

The first example is The Case of The Thief Suing His Victim. Most of you are probably familiar with the online cartoon The Oatmeal. Matthew Inman, the guy behind all the funny cartoons, complained a year ago that a website called FunnyJunk was full of his drawings. FunnyJunk allow their users to post material (from a quick glance it looks like a large part of the contents is copyrighted material), and then when complaints are sent to them just blame the users, while cashing the checks for all the advertising on the site. Matthew blogged about FunnyJunk doing this about a year ago, and described their business model:

Here’s how FunnyJunk.com’s business operates:
1.Gather funny pictures from around the internet
2.Host them on FunnyJunk.com
3.Slather them in advertising
4.If someone claims copyright infringement, throw your hands up in the air and exclaim "It was our users who uploaded your photos! We had nothing to do with it! We’re innocent!"
5.Cash six figure advertising checks from other artist’s stolen material

 

Last week, Matthew was served with papers, demanding him to pay FunnyJunk $20,000 or be sued. FunnyJunk had hired Charles Carreon as their lawyer, who wrote that letter.
Matthew responded publicly here: http://theoatmeal.com/blog/funnyjunk_letter
I highly suggest reading the whole thing. It is extremely amusing.

BearLove So Matthew sets up a fundraiser. Not to raise money to pay off FunnyJunk, but to split even between the National Wildlife Federation and the American Cancer Society. He raised the $20,000. In 64 minutes! The amount collected by "Operation BearLove Good. Cancer Bad." is currently at $169,000. However, the lawyer, Charles Carreon, is trying to shut down the fund raiser, according to MSNBC. He is also complaining that he was not expecting an outpour of hate and people being upset at him, or having his mom accused trying to seduce a Kodiak bear (the drawing is supposed to be of the mom of the FunnyJunk admin/owner, not the lawyer, by the way). Very strange that someone who market himself as a cyber attorney is so clueless to how the internet works. He should lookup the Streisand effect, as well.

Even other lawyers chime in. The law-blog PopeHat.com uses some strong words:

So, The Oatmeal tried to turn this into something good ?something that would benefit wildlife protection and cancer research ?and Charles Carreon had a snit and tried to shut it down because it was embarrassing to him and his client?

Fuck him. He’s vermin. He’s not forgivable. Let any good he has ever done be wiped out. Let the name "Charles Carreon" be synonymous with petulant, amoral censorious douchebaggery.

 

Another lawyer, who supposedly knows Charles Carreon, is also quoted on the same page:

Despite my earlier charitable comments, I can not find any words to defend trying to shut the fundraiser down. I can’t even gin up a minor benefit of the doubt on that one. I can see an ill-considered demand as a mistake in judgment while hoping to gain an advantage for your client. But taking a shot at the fundraiser would not do that ?it would just be lashing out to hurt bears and cancer patients? Holy fucking shitballs inside a burning biplane careening toward the Statue of Liberty, Captain! I hope that the reporter merely got the story wrong, because if not, that’s more fucked up than a rhino raping a chinchilla while dressed up in unicorns’ undergarments.

 

It will be interesting to see the outcome of this. The twitterverse seems to have the consensus that Charles Carreon just committed career suicide.

 

 

The second example is The Case of NeverSeconds. A nine year old girl, Martha Payne (who blog under the name VEG), started a blog called NeverSeconds, where she posted pictures of her school lunches, as well as described them (contents, taste, etc). This is a great blog!

SchoolLunchPicture by Martha Payne, from this blog entry. 

The blog went viral recently, and children in other countries are sending her pictures of their lunches. Martha is also raising money for Mary´s Meals, a charity trying to feed poor children. UK newspapers started writing stories about the blog and how the schools should serve healthier (and bigger) meals for the kids.

However, on Thursday Martha posted a message titled "Goodbye.":

This morning in maths I got taken out of class by my head teacher and taken to her office. I was told that I could not take any more photos of my school dinners because of a headline in a newspaper today.

I only write my blog not newspapers and I am sad I am no longer allowed to take photos. I will miss sharing and rating my school dinners and I´ll miss seeing the dinners you send me too. I don´t think I will be able to finish raising enough money for a kitchen for Mary´s Meals either.

Goodbye,
VEG

 

The school board decided to stop Martha from taking pictures. Supposedly one of the newspapers who picked up the story about her blog had called for the lunch ladies to be fired. So it was not even something Martha did.

This story was picked up by UK and international news outlets, and celebrities like Jamie Oliver and Neil Gaiman posted on twitter in support of Martha. Her charity went from ,000 to (currently) 7,000strike> 8.000 (and it keeps going up) in a few days! Donate you too.

MarysMeals 

After a media- and twitter frenzy, and after the members of the
school board of Argyll and Bute were contacted by the education secretary of Scotland, the ban was lifted today. The school board first posted this statement, still trying to shift blame to Martha. They then retracted that and posted another statement. I suspect that the first statement is how the council really felt, that government officials should not be allowed to be criticized. They were probably forced by higher-ups and more outrage to withdraw the first statement and replace it with another one.

This is a great example on how NOT to act when criticized. Basically a hug PR fiasco.

 

So there you have it, two great examples of a website/lawyer and a group of politicians who dug themselves a hole, and then kept digging. I have a feeling that that school board will be gone at the next election. And that the FunnyJunk lawsuit will be thrown out of court quickly, if it even get there. By the way, Matthew Inman have retained Venkat Balasubramani to handle the case:

I have discovered that The Oatmeal is represented by none other than Venkat Balasubramani, who will lay a motherfucking smackdown if you make him. While The Oatmeal´s response is funnier, Venkat brings his A-Game here.

 

Let´s bring out the popcorn! But first, go and send some money to the bears/to stop cancer and to feed the children.

Update: Martha is back! Monday she will take a new picture and post. Also, her fund raiser is now up to 2,300″…”

 

0 Comments

Regular Expressions in Notes (Lotusscript)

Today I needed to use regular expressions (a.k.a. regexp) in a Lotus Notes application. I just wanted to check if the user entered a claim number (in the format “nnXXXXXnnnnn”, e.g. 12RICTX12345) in a field. A quick online search found a blog entry with some code using the VBScript object available in Windows, and I adapted it for my application.
Just in case someone need this, I am posting the code below. I am not taking credit for the code, I found it on Giles Hinton´s blog and just adapted it a little bit.

I also found information about using LS2J and Java to handle regular expression in Notes, which should be platform independent, not restricted to just Windows. Since all our users are on Windows (either directly or through Citrix), I could use the quick method below. But I would probably use the script library posted on OpenNTF for more serious code.

 

Dim ws As New NotesUIWorkspace
Dim uidoc As NotesUIDocument
Dim regex As Variant
Dim pattern As String
Dim result As String
Dim match As Boolean

'*** Define pattern and get text value to check for match
pattern = |b([0-9]{2}[a-zA-Z]{5}[0-9]{5})b|
Set uidoc = ws.CurrentDocument
subject = uidoc.FieldGetText("ShortDescription")
'*** Create RegExp object
Set regex = CreateObject("VBScript.Regexp")
regex.Global = True
regex.IgnoreCase = True
regex.Pattern = pattern
'*** Test for match of pattern in text
match = regex.Test(subject)
If match = True Then
    Msgbox "Claim number was found in the field."
End If
0 Comments

Review: LEGO Lord of The Rings

This weekend I spent with my son building some of the new LEGO kits from the new Lord of The Rings series. Here is a quick review of the kits we have built this far. You can click on the images for high-res versions of them.

 

9469 Gandalf Arrives – 83 pieces

GandalgArrives

A small but nice set. Contains Gandalf in his cart loaded with fireworks, as well as Frodo welcoming him.
Plenty of nice details, like the fireworks, a carrot for the pony and an envelope for Frodo to put the ring in.

 

9472 Attack on Weathertop – 430 pieces

Weathertop_Closed

This is a very nice set. It contains five minifigs: Aragorn, Frodo (with the ring), Merry and two Nazgûl (ringwraiths), as well as two horses. The three first minifigs have a feature I have not seen before, they have two sets of faces. By turning the head and exposing the part hidden by the hair, you get two different facial expression, like stern and aggressive or scared. The Frodo minifig in 9460 got the same feature, but not Gandalf as the back of his head is visible. All the minifigs are extremely detailed, it is obvious that the designers of the kits realized that collectors and adults will buy these kits.

Weathertop1_Details

 

The kit itself is of the ruins on top of Weathertop (Amon Sûl), and it features a trap door and a cooking fire. The ruins can be opened and in the inside you find weapons, toches and much more. Even a rat! There is also a stand-alone pieved of ruin with a bush and some plants.

WeatherTop_Open

The plants are the only thing I did not like with the kit. For some reason, perhaps the kind of softer plastic used, they don’t stick well to the bricks they are placed on. But that is a minor detail, otherwise this is a great kit.

 

9473 The Mines of Moria – 776 pieces

Moria2_Finished

This is a big set, the second largest in the series, and it depicts the events in the Chamber of Mazarbul. It contains six minifigs (Gimli, Legolas, Boromir, Pippin and two Moria orcs), as well as the cave troll. There are four separate sections, a large wall section, the doors to the chamber, the well with the skeleton and the chain and bucket, as well as Balins tomb, containing the skeleton of Balin. By pulling a lever, the skeleton, bucket and chain will fall down in the well, just like in the book and movie.

There are plenty of details, from old weapons to gems and even the Book of Mazarbul.

BalinsTomb_Details

 

9476 The Orc Forge – 363 pieces

OrcForge

This is currently my son’s favorite kit. It features a light brick, so when a rod is pushed, it looks like fire under the melting pot. In addition, there are four minifigs: Lurtz, two Mordor orcs and one Uruk-hai. To be really picky, Lurtz was created by Sauron, just like the Uruk-hai, so there should not have been any Morder orcs, but Isengard orcs. There is two sets of Uruk-hai armor (complete with the white hand of Sauroman), a crane to lift material to melt for the forge, etc.

OrcForge_Details1

OrcForge_Details2

 

 

So what is the verdict? As a Lord of the Rings fan (both the books and the movies by Peter Jackson), I am very happy with the LEGO kits this far. The quality is good, the instructions are very clear (recently I have seen some instructions where it was easy to miss a piece of pick the wrong shade of gray) and the detailing is amazing.
I still have two more kits to build that I already purchased, and I have to get the last kit (Battle of Helms Deep). I will report on them later.

 

0 Comments

35 years ago in a galaxy far, far away…

May 25, 1977. Imagine it has been 35 years”…”

When the first movie (then called just "Star Wars", later renamed "Episode IV ?A New Hope") was released in Sweden, the age restriction was set to 11 years. With a parent you were allowed to see it even if you were younger. I was 8 years old, but my parents did not want to go see it.
It was not until "Episode VI ?The Return of The Jedi" was released in 1983 that I actually got to see the two first movies. They were shown back-to-back with a short break in-between, and a few days later the last movie premiered.

I had of course read the book that was released around the time the original Star Wars came out, so I was familiar with the story even before watching the movie. As a young boy, I really enjoyed the movies, and I still do. I recently watched "Episode I ?The Phantom Menace" in 3D.
I am however slightly irritated at George Lucas and how he keep changing the movies”…”

StarWarsMoviePoster1977

 

From Wikipedia:

Star Wars debuted on Wednesday, May 25, 1977, in 32 theaters, and eight more on Thursday and Friday. It immediately broke box-office records, effectively becoming one of the first blockbuster films, and Fox accelerated plans to broaden its release.
 
Star Wars remains one of the most financially successful films of all time. The film earned $1,554,475 through its opening weekend, eventually earning over $220 million during its initial theatrical run. Star Wars entered international release towards the end of the year, earning $410 million in total. Reissues in 1978, 1979, 1981 and 1982 brought its cumulative gross in Canada and the U.S. to $323 million, and extended its global earnings to $530 million.
 
Following the release of the Special Edition in 1997, Star Wars briefly reclaimed the North American record before losing it again the following year to Titanic. In total, the film has earned $775,398,007 worldwide (including $460,998,007 in North America alone). Adjusted for inflation, it has earned $2.5 billion worldwide at 2011 prices, making it the most successful franchise film of all-time; at the North American box-office it ranks second behind Gone with the Wind on the inflation-adjusted list.

 

0 Comments

Lotusscript Code – HTML retrieval class

I saw a question in the DeveloperWorks forum about retrieving a web page (in this particular case in order to get some data out of it), and realized that I never posted my HTML retrieval class…

So without further ado, here it is. It should be fairly self-documenting… Create a new object, then call the GetHTTP method with a URL to get a string representing the HTML code of that URL.  This is Windows only, by the way.

Class RemoteHTML
  Private httpObject As Variant
  Public httpStatus As Integer
 
  Public Sub New()
    Set httpObject = CreateObject("MSXML2.ServerXMLHTTP")
  End Sub

  Public Function GetHTTP(httpURL As String) As String
    Dim retries As Integer
    retries = 0 
    Do
      If retries>1 Then
        Sleep 1   ' After the two first calls, introduce a 1 second delay betwen each call
      End If
      retries = retries + 1
      Call httpObject.open("GET", httpURL, False)
      Call httpObject.send()
      httpStatus = httpObject.Status
      If retries >= 10 Then
        httpStatus = 0     ' Timeout
      End If
    Loop Until httpStatus = 200 Or httpStatus > 500 Or httpStatus = 404 Or httpStatus = 0
    If httpStatus = 200 Then
      GetHTTP = Left$(httpObject.responseText,16000)
    Else
      GetHTTP = ""
    End If
  End Function

End Class
0 Comments

How IBM could make my life a little easier

Dear IBM,

I like that I now can use database icons with more than 16 fixed colors. I have updated pretty much all our applications (or is it called databases again?) with new and modern looking icons. The users likes it too.

However, with the old icon editor,it was easy to use the flood-fill tool to add a (in my case red) background to all templates. This made it very easy to differentiate between applications and templates. There is no option like that when I use the new database icons, I have to make a second icon in Photoshop and manually re-color it.

Why not add some kind of indicator to the application icon, to show that it is a template?  Below is my attempt to design something.  You see the red indicator that it is a template, you see the version number of the template and the server at the bottom is alwasy visible, as it is in a section of it's own.

Template Indicator in Lotus Notes Domino

As you see above, I use version numbers on my templates. Would it be too much to ask for a version number property and have the version number displayed on at least the template icon, like I have in my image above?
The version number should be transferred over to the application at design refresh time. The build number could be automatically updated when a "Recompile All Lotusscript" is performed, or automatically when any design element is modified and the build number has not been updated the same day already. A fully manual option shold also be available. How the build number gets updated should be an option in the settings for Domino Designer.

Of course, you need to create a way to access the version number programatically. Something like this:

 
 Dim version as NotesVersionNumberSet version = db.VersionNumberPrint version.Major ' Returns 1 Print version.Minor ' Returns 3 Print version.Revision ' Returns 0 Print version.Build ' Returns 1234 Print version.FullVersion ' Returns "1.3.0.1234" 

A formula function like @AppVersionNumber would be nice as well.

 

Thank you,

Your friend Karl-Henry

 

 

0 Comments

YUI3: Powerful Javascript Framework

Last week I came up with a small side project. It was basically a simple web chat, working pretty much like the comments on a blog. I decided to build this as a classic Domino application, not using Xpages. I started development Wednesday mid-afternoon, and the application needed be done Thursday evening, and I of course had my regular job to do. So I could not justify spending the time I needed to learn doing it in Xpages, and then try to write CSS to get it to look like I wanted it. So there you have the reason for why I did not use Xpages.

I decided to take a look at YUI, the framework developed by Yahoo. The latest version is version 3, and it is really nice and powerful. I looked into in, and realized that I could do things very quickly, getting the same functionality as using Xpages (partial refresh  or page, etc) in just a few lines of code.

So what did I need for my application? I started with a blank Notes database. I created a page, which is where all the action would take place. On the page I created a header section (logo), a content section (where the messages/chat would be displayed) and a form section where I put a few fields for the users to fill out. The fields were name/handle, email, (optional) website and lastly the message to send. I also put two images there, one to use as a submit button, and one to refresh the chat content without having to submit a text.

Bloggfika Webchat using YUI3

I added some javascript at the top of the page, loading YUI3 (hosted by Yahoo, I did not even have to download anything):

 
<script src="http://yui.yahooapis.com/3.5.1/build/yui/yui-min.js"></script> 

The next step was to add some code for the actual logic on the page. YUI works by binding functions to events on elements on the page. I wanted to bind function functions to the onClick event of the two buttons, "submit" and "refresh". To avoid this blog entry to be too long, I will just show the code behind the refresh button:

 Y.one('#refresh').on('click', function(e) {        e.preventDefault();        var contentcell = Y.one("#contentCell");        if (contentcell) {            var currentTime = new Date();            Y.one('#refresh').set('src','webchat.nsf/ajax-loader-150.gif'); var args = &refresh=true&datetime=" + currentTime.getTime();            contentcell.load("webchat.nsf/SendText?OpenAgent" + args,"", function()  {                Y.one('#refresh').set('src','webchat.nsf/refresh.png');            } );        }    });

This code gets a reference to the first element with the id "refresh" using Y.one(), then bind a function to the "click" event. The function is defined right there, and it will do a couple of things. First it get a reference to the element (in this case a DIV) with the id  "contentCall". I check if it was found, and if so I get the current time (get a unique number). I then change the image of the refresh element to a spinning "loading" icon.

The next line is the coolest one. In one line of code I perform an Ajax call to an agent on the Domino server, and put the returned data into the contentcell element. Finally, after the server returns teh data, another function is called, which restored the refresh button to the original image.

The agent is written in Lotusscript, and it just read the existing chat entries from a database and output it using the print statement as HTML. I add the times in seconds as a second argument, just to get each call to be unique. Otherwise the returned data might be cached on the server and the user would not get the latest data.

The submit action is similar, the difference is that it read the values from the different fields on the form and submit them to the same agent as name-value pairs. The agent will detect that data is being passed to it, and will store the values in a new Notes document, then return HTML for all entries just like the refresh.

As you can see, a complete Ajax call (calling the server, waiting for data, gettingthe data back, replacing the content of an element on a page to perform a partial refresh and triggering additional code when the data has been returned) is just one line of code. Could not be much easier!

The actual application got more functionality, I check for certain required fields already in the web client, using javascript, I load up the content when the page is first opened (so the user does not have to press refresh to see anything), etc. The webchat is also just open specific times, when it is closed a message is displayed about when it will be open next, etc. The open/close is set by a Notes configuration document. Note: Do not use profile documents to store items for a web application, they get cached on the server and you will probably not get the latest data…

I am impressed with how easy and quick it was to build this application, using a compbiantion of classic Notes and a powerful javascript framework.

 

0 Comments

Notes/Domino future roadmap survey results

Yesterday I created a simple survey to see what people wanted in a future version of Notes/Domino. It was based onEd Brill's post, where he summarized the result of the comments to his request for suggestions on what areas to focus future development of Notes/Domino.

I think the results speak for themselves. But with 74 survey takers, more than two in three want the client performance to be improved, and almost as many want the limitations (32K/64K) removed and teh Notes cleint UI modernized. Rewriting all templates using Xpages and match the templates delivered with Sharepoint was another popular feature, as well as better documenattion and a more stable Domino Designer.
I think the 52% who voted on improving Domino NSF performance mainly were thinking about faster indexing, and moving view index to separate drives, outside the NSF file itself.

The "other" answers were:
* Make Connections free
* Create an AppStore for Domino applications
* Make Deployment easier, as easy as outlook
* Solve the known bugs
* Enhance Linux support

 

Survey Results

Update: There are still responses coming in, I will update the result as more results come in.

 

0 Comments

[Code] – Mail Merge/Form Letters in Lotuscript

Back in 2003 or so, I wrote some code to take a form letter (stored in a Notes document) and merge that with data stored in another Notes document in order to create a personalized letter that could be printed or emailed. Back then we were still on Notes 5, so very limited rich text functionality and no budget to purchase Ben’s excellent Midas LSX. The end result worked, but any formatting in the form letter template was lost.

Eventually we upgraded to Notes 7 and later to Notes 8.5. Now I had much more rich text functionality to play with, so I rewrote the code as a class. I added some additional functionality, like formatting values using a mask, and some lookup functionality. The class support all kind of formatting in the form letter template, including fonts, colors, tables, graphics, etc.

This is what a typical form letter look like:

Form Letter Template

As you can see, the placeholders are using curly brackets to hold either a field name or a command. The commands are indicated by the percent sign (%). There can also be different arguments, for formatting, lookup into the NAB/Domino Directory, etc. I even have functionality to present a nice dialog box where the user can pick recipient from a list of everyone associated with the claim (as this is from a claim system used by an insurance company).

Here is a description of the syntax for the placeholders:

{fieldname}
Displays the content of the specified field from the selected source document. Additional (optional) formatting arguments can be used, for example to format values to desired format.
By using the argument LOSSNOTICE or SOURCE=”LOSSNOTICE” the value of the field is retrieved from the Loss Notice instead of the current source document, e.g. {adjuster LOSSNOTICE}.
Use the argument SELECTED to get the fields name, address, city, state, zip, email, SSN and DoB for the recipient selected in the separate dialog box.
{email SELECTED} will return the email address for the recipient selected, either producer, insured or one of the claimants.
The optional argument NABFIELD will retrieve the value of the specified field from the NAB for the user specified in the field (field must be spelled exactly as in the NAB design):
{Adjuster NABFIELD=”JobTitle”} will return the title of the person in the ‘Adjuster’ field.

{%DATE}
Displayes the current date. Default format is mm/dd/yyyy but the FORMAT argument can be used to change the value into desired format. The old {%DUS}, {%DUT}, etc have been removed and must be replaced with the new format, as they don’t work.
Examples of date formats:
FORMAT=”mmmm d, yyyy” -> March 3, 2010
FORMAT=”yyyy-mm-dd” -> 2010-03-01
FORMAT=”mmmm yyyy” -> March 2010

{%TIME}
Displayes the current time. Default format is hh:nn:ss (24h universal format) but like with the date, the FORMAT argument can be used to change the value into desired format. Note that minutes use the letter N, not M (which is used for month)! The old {%TUS}, {%TUT}, etc have been removed and must be replaced with the new format, as they don’t work.
Examples of time formats:
FORMAT=”h.nn ampm” -> 2.34 pm
FORMAT=”hh.nn ampm” -> 02.34 pm
FORMAT=”hh:nn” -> 14:34

{%INPUT PROMPT=”Please enter the amount” FORMAT=”$#,##0.00″ REQUIRED SETVAR=”variablename”}
Asks the user to enter a value. The optional arguemnt REQUIRED is used to force the user to enter a value (blank values will not be accepted). FORMAT can be used to format the value entered, for example into correct currency format (as shown above) or desired date format.
Additionally, SETVAR=”variablename” can be used to store the value entered for re-use later in the document, where {%GETVAR NAME=”variablename”} is used to retrieve/display it.

{%GETVAR NAME=”variablename”}
Get the value previously stored in an {%INPUT} command using the SETVAR argument.

{%USER} or {%USR}
Displayes the current user’s name. It will be the name of the user creating the form letter. {%USR} have been deprecated, it is just available for backwards compatibility. The optional argument NABFIELD will retrieve the value of the specified field from the NAB for the current user (field must be spelled exactly as in the NAB design):
{%USER NABFIELD=”JobTitle”}

{%PICKLIST SERVER=”servername” DB=”database” VIEW=”viewname” FIELD=”fieldname” PROMPT=”text}
This command let the user select a document from a list, and returns the value in the
specified field. SERVER and DB are optional, and defaults to current server and current database. VIEW, FIELD and PROMPT are required. Optionally CLEARCACHE can be used for multiple lookups to the same view where different documents are to be selected.

By using the FILTER argument, the document collection will be filtered to only those where the category (in a categorized view) matches the value in the specified field in the source document. E.g: VIEW=”(ClaimantList)” FILTER=”ParentUNID” will only show documents in the (categorized) lookup view with the same ParentUNID as the document being processed (merged).
By specifying the optional keyword/argument CLAIMANT, the VIEW, PROMPT and FILTER argumenst are set automatically as follows: VIEW=”(SysLookupClaimantsCatByParentUNID)” PROMPT=”Select Claimant:” FILTER=”ParentUNID”.

{%PICKLIST VIEW=”viewname” FIELD=”fieldname” CACHED}
This command is used after the initial picklist command. It will retrieve additional fields from the same (cached) document, so the user only have to select the document once but can retrieve multiple field values from it. If another {%PICKLIST} command is encountered with the CLEARCACHE argument, the cached document will not be available anymore.

And now the code, from a script library called Class.MailMerge.
Right now I don’t have the time to move the code out of my application and build a working sample database (the form letters are actually stored in a separate database), but I hope that this code can still help someone.

Option Public
Option Declare

'*** Script Library for MailMerge, preserving formatting in Rich Text field
'*** Requires Notes 6.5 or higher (tested in 7.0, 8.0 and 8.5)
'*** Copyright (c) Karl-Henry Martinsson 2003-2012
'*** Email: texasswede@gmail.com
'*** Websites: www.texasswede.com & blog.texasswede.com
'*** This code can be user in any application, as long as this notice
'*** is left intact. Also, the copyright information must be published in
'*** any documentation and on the About page, or similar location visible
'*** to the users, if they can not easily view/access the source code.
'*** If this code saves you time and helps you, consider a donation.

Const TYPE_FIELD = 1
Const TYPE_CMD = 2
Dim picklist List As NotesDocument 
Dim variable List As String

Class PlaceHolderData
    Public placeholderstring As String
    Public placeholdertype As Integer
    Public fieldname As String
    Public fieldtype As Long
    Public command As String
    Public argument List As String
    Public text As String 

    Public Sub New(Byval placeholder As String)
        ' Store the original placeholder
        placeholderstring = placeholder        
        ' Strip out curly brackets before and after
        placeholder = Mid$(placeholder,2,Len(placeholder)-2)
        If Left$(placeholder,1) = "%" Then    ' Check if it is a command
            Me.placeholdertype = TYPE_CMD
            ' Remove the % in front of the command
            placeholder = Right$(placeholder,Len(placeholder)-1)    
        Else
            Me.placeholdertype = TYPE_FIELD
        End If
        Call ParsePlaceHolder(placeholder)
    End Sub

    Private Sub ParsePlaceHolder(Byval placeholder As String)
        Dim startpos As Integer 
        Dim midpos As Integer 
        Dim endpos As Integer 
        Dim args As Integer                            ' Boolean to indicate argements present or not
        Dim argstring As String
        Dim qt As Integer 
        Dim eq As Integer
        Dim char As String
        Dim argname As String
        Dim argvalue As String
        Dim i As Integer

        ' First we need to find the end of the command or field.
        ' It is either at the end of the placeholder or when we encounter a space
        endpos = Instr(placeholder," ")            ' Search for space
        If endpos = 0 Then                                ' No space, e.g. no arguments
            endpos = Len(placeholder)                
            args = False
        Else                                                    ' We have some arguments
            endpos = endpos -1                        ' Reduce by one to get rid of trailing space
            args = True
        End If
        If Me.placeholdertype = TYPE_CMD Then
            Me.command = Ucase(Left$(placeholder,endpos))
        Else
            Me.fieldname = Left$(placeholder,endpos)            
        End If
        If args = True Then 
        ' Add code here to get arguments
            argstring = Fulltrim(Right$(placeholder,Len(placeholder)-endpos)    )    ' Get arguments only
            qt = False
            eq = False
            For i = 1 To Len(argstring)
                char = Mid$(argstring,i,1)    ' Get character
                If eq = True Then
                    argvalue=argvalue & char
                Else
                    argname=argname & char
                End If

                If char = |"| Then                    ' We found a quote
                    If qt = False Then
                        qt = True                        
                    Else
                        qt = False
                    End If
                Elseif char="=" Then             ' Found a equal, e.g. now we are getting to a value
                    If eq = False Then
                        eq = True                        
                    Else
                        eq = False
                    End If
                End If
                If i = Len(argstring) Then        ' We are at the end
                    char = " "                        ' Fake a space
                End If
                If char = " " Then                    ' Found a space
                    If qt = False Then            ' Make sure it is not within quotes
                        eq = False                    ' Now we are back at argument name again
                        If Right$(argname,1) = "=" Then                    
                            argname = Left$(argname,Len(argname)-1)    ' Remove trainling equal sign
                        End If
                        Me.argument(Ucase(argname)) = Fulltrim(Replace(argvalue,|"|,""))    ' Create list item, remove quotes
                        argname = ""
                        argvalue = ""
                    End If
                End If
            Next
        End If
    End Sub

    Public Sub ProcessPlaceHolder(sourcedoc As NotesDocument, lossnotice As NotesDocument)
        Dim session As New NotesSession                
        Dim thisdb As NotesDatabase
        Dim ws As New NotesUIWorkspace
        Dim pickcollection As NotesDocumentCollection
        Dim pickdoc As NotesDocument
        Dim servername As String
        Dim dbname As String
        Dim viewname As String        
        Dim fieldvalue As String
        Dim formatstring As String
        Dim inputstr As String
        Dim prompt As String
        Dim title As String
        Dim default As String
        Dim filterfield As String
        Dim filtervalue As String
        Dim nabdoc As NotesDocument

        ' Read any formatting specified in arguments
        If Iselement(Me.argument("FORMAT")) Then
            formatstring = Me.argument("FORMAT")
        Elseif Iselement(Me.argument("FMT")) Then
            formatstring = Me.argument("FMT")
        Else 
            formatstring = ""
        End If

        If placeholdertype = TYPE_FIELD Then
            If sourcedoc Is Nothing Then
                Msgbox "Error: sourcedoc not defined, unable to retrieve data from field '" & Me.fieldname & "'.",,"MailMerge::PlaceHolder.ProcessPlaceHolder()"
                fieldvalue = "*** ERROR ***"                
                Exit Sub
            Else
                If Iselement(Me.Argument("LOSSNOTICE")) Then
                    fieldvalue = lossnotice.GetItemValue(Me.fieldname)(0)
                    Me.FieldType = lossnotice.GetFirstItem(Me.fieldname).Type
                Elseif Iselement(Me.Argument("SOURCE")) Then
                    If Ucase(Me.Argument("SOURCE")) = "LOSSNOTICE" Then
                        fieldvalue = lossnotice.GetItemValue(Me.fieldname)(0)
                        Me.FieldType = lossnotice.GetFirstItem(Me.fieldname).Type
                    End If
                Else
                    fieldvalue = sourcedoc.GetItemValue(Me.fieldname)(0)
                    Me.FieldType = sourcedoc.GetFirstItem(Me.fieldname).Type
                End If
                If formatstring <> "" Then
                    If Isdate(fieldvalue) Then                    ' Check if it might be a date/time value
                        fieldvalue = Format$(Cdat(fieldvalue),formatstring)
                    Elseif Isnumeric(fieldvalue) Then    ' Check if it might be a numeric value
                        fieldvalue = Format$(Cdbl(fieldvalue),formatstring)
                    End If
                End If
            End If
            Me.text = fieldvalue
        Else
            ' *** Fix legacy commands
            If Ucase(Me.Command) = "USR" Then    
                Me.Command = "USER"
            End If
            ' *** Process placeholder commands
            Select Case Ucase(Me.Command)
            Case "USER" :
                If Iselement(Me.Argument("NABFIELD")) Then
                    Me.Text = GetNABField(session.CommonUserName, Me.Argument("NABFIELD")) 
                Else
                    Me.Text = session.CommonUserName 
                End If
            Case "INPUT"        :        ' *** Ask the user to enter information
                prompt = Me.Argument("PROMPT")
                If Iselement(Me.Argument("TITLE")) Then
                    title = Me.Argument("TITLE")
                Else
                    title = "FormLetter Mail Merge"
                End If
                If Iselement(Me.Argument("DEFAULT")) Then
                    default = Me.Argument("DEFAULT")
                Else
                    default = ""
                End If
                If Iselement(Me.Argument("REQUIRED")) Then
                    ' Repeat until user enter a value
                    Do                
                        inputstr = Inputbox$(prompt, title, default)                        
                    Loop While Fulltrim(inputstr)=""
                Else
                    inputstr = Inputbox$(prompt, title, default)                        
                End If
                If formatstring <> "" Then
                    If Isdate(inputstr) Then                    ' Check if it might be a date/time value
                        inputstr = Format$(Cdat(inputstr),formatstring)
                    Elseif Isnumeric(inputstr) Then    ' Check if it might be a numeric value
                        inputstr = Format$(Cdbl(inputstr),formatstring)
                    End If
                End If
                Me.Text = inputstr
                ' *** Check for SETVAR argument
                If Iselement(Me.argument("SETVAR")) Then
                    variable(Ucase(Me.argument("SETVAR")))=Me.Text
                End If

            Case "PICKLIST"    :        ' Present the user with a list of documents to choose from
                Set thisdb = session.CurrentDatabase
                Set pickdoc = Nothing        ' Clear pickdoc
                ' *** We need to get the view argument to perform a lookup into the list...
                If Iselement(Me.Argument("VIEW")) Then
                    viewname = Ucase(Me.Argument("VIEW"))
                End If
                ' *** If CLAIMANT argument is specified, set arguments to predefined values
                If Iselement(Me.Argument("CLAIMANT")) Then
                    Me.Argument("VIEW") = "(SysLookupClaimantsCatByParentUNID)"
                    Me.Argument("FILTER") = "ParentUNID"
                    Me.Argument("PROMPT") = "Select Claimant:"
                End If
                ' *** Check if user requested to clear cached data
                If Iselement(Me.Argument("CLEARCACHE")) Then
                    If Iselement(picklist(viewname)) Then
                        Erase picklist(viewname)    ' Delete this cached item (document)
                    End If
                End If
                ' *** If user want to use cached data, load pickdoc with cached data
                If Iselement(Me.Argument("CACHED")) Then
                    If Iselement(picklist(viewname)) Then
                        Set pickdoc = picklist(viewname)
                    End If
                End If
                If pickdoc Is Nothing Then    ' No cached document for this view 
                    If Iselement(Me.Argument("SERVER")) Then
                        servername = Me.Argument("SERVER")
                    Else
                        servername = thisdb.Server
                    End If
                    If Iselement(Me.Argument("DB")) Then
                        dbname = Me.Argument("DB")
                    Else
                        dbname = thisdb.FilePath 
                    End If
                    If Iselement(Me.Argument("VIEW")) Then
                        viewname = Me.Argument("VIEW")    
                    Else
                        Msgbox "Missing Required Argument - VIEW" & Chr$(13) & Me.PlaceHolderString,,"Missing Argument"
                        Exit Sub
                    End If
                    If Iselement(Me.Argument("PROMPT")) Then
                        prompt = Me.Argument("PROMPT")
                    Else
                        Msgbox "Missing Required Argument - PROMPT" & Chr$(13) & Me.PlaceHolderString,,"Missing Argument"
                        Exit Sub
                    End If
                    If Iselement(Me.Argument("TITLE")) Then
                        title = Me.Argument("TITLE")
                    Else
                        title = "FormLetter Mail Merge"
                    End If
onemoretime:                
                    If Iselement(Me.Argument("FILTER")) Then
                        filterfield = Me.Argument("FILTER")                            ' Get field to filter on
                        filtervalue = sourcedoc.GetItemValue(filterfield)(0)        ' Get value of field on source document
                        Set pickcollection = ws.PicklistCollection(3, False, servername, dbname, viewname, title, prompt, filtervalue)
                    Else
                        Set pickcollection = ws.PicklistCollection(3, False, servername, dbname, viewname, title, prompt)
                    End If
                    If Isempty(pickcollection) Then
                        If Iselement(Me.Argument("REQUIRED")) Then
                            If Ucase(Me.Argument("REQUIRED")) <> "NO" Then
                                Msgbox "You need to select one item/document in the list.", , title
                                Goto onemoretime
                            End If
                        End If
                    Else
                        Set pickdoc = pickcollection.GetFirstDocument        
                    End If
                    If pickdoc Is Nothing Then
                        Msgbox "Error: No document returned.",,"MailMerge::PlaceHolder.ProcessPlaceHolder()"
                        Exit Sub
                    End If
                    Set picklist(Ucase(viewname)) = pickdoc
                End If
                If Iselement(Me.Argument("FIELD")) Then
                    fieldname = Me.Argument("FIELD")
                Else
                    Msgbox "Missing Required Argument - FIELD" & Chr$(13) & Me.PlaceHolderString,,"Missing Argument"                        
                    Exit Sub
                End If
                inputstr = pickdoc.GetItemValue(fieldname)(0)
                If formatstring <> "" Then
                    If Isdate(inputstr) Then                    ' Check if it might be a date/time value
                        inputstr = Format$(Cdat(inputstr),formatstring)
                    Elseif Isnumeric(inputstr) Then    ' Check if it might be a numeric value
                        inputstr = Format$(Cdbl(inputstr),formatstring)
                    End If
                End If
                Me.Text = inputstr
                ' *** Check for SETVAR argument
                If Iselement(Me.argument("SETVAR")) Then
                    variable(Ucase(Me.argument("SETVAR")))=Me.Text
                End If

            Case "DATE"            :         ' *** Current Date
                If formatstring = "" Then 
                    Me.Text = Format$(Now(),"mm/dd/yyyy")
                Else
                    Me.Text = Format$(Now(),formatstring)
                End If

            Case "TIME"            :         ' *** Current Time
                If formatstring = "" Then 
                    Me.Text = Format$(Now(),"hh:nn:ss")
                Else
                    Me.Text = Format$(Now(),formatstring)
                End If

            Case "GETVAR"    :    ' *** Get variable previously stored
                If Iselement(variable(Ucase(Me.argument("NAME")))) Then
                    Me.Text = variable(Ucase(Me.argument("NAME")))
                End If

            Case Else                :
                Me.Text = "**** undefined command ***"

            End Select
        End If

    End Sub

    ' *** Private supporting functions/subs

    Private Function GetNABField(user As String, fieldname As String) As String
        Dim session As New NotesSession
        Dim curdb As NotesDatabase    
        Dim nabdb As NotesDatabase
        Dim view As NotesView
        Dim col As NotesDocumentCollection
        Dim userdoc As NotesDocument

        Set curdb = session.CurrentDatabase
        Set nabdb = New Notesdatabase(curdb.Server, "names.nsf")
        Set view = nabdb.GetView("PeopleByFirstname")
        Set col = view.GetAllDocumentsByKey(user)
        If col Is Nothing Then
            GetNABField = ""
            Exit Function
        End If
        Set userdoc = col.GetFirstDocument
        If userdoc Is Nothing Then
            GetNABField = ""
            Exit Function
        End If
        GetNABField = userdoc.GetItemValue(fieldname)(0)
    End Function

End Class

Class MailMergeObject
    Public templatedoc As NotesDocument    ' Where to get layout from
    Public sourcefield As NotesRichTextItem    
    Public targetfield As NotesRichTextItem    ' Where to put the merged text
    Public placeholder List As PlaceHolderData
    Private sourcedoc As NotesDocument     ' The document containing data to be merged
    Private maindoc As NotesDocument        ' The main document for the processed document
    Private tempbody As NotesRichTextItem        ' Temporary copy of body field for this class/instance

    Public Sub New()

    End Sub

    Public Sub SetSourceDoc(doc As NotesDocument)
        Set sourcedoc = doc                
    End Sub

    Public Sub SetMainDoc(doc As NotesDocument)
        Set maindoc = doc
    End Sub

    Public Function LoadTemplate() As Integer
        Dim body As NotesRichTextItem
        Dim temp As String            
        Dim bodytext As String
        Dim startpos As Long
        Dim endpos As Long
        Set sourcefield = templatedoc.GetFirstItem("Body")    ' Put template body field (rich text) into global object
        Set body = sourcefield                                ' Put rich text into temporary body object
        bodytext = body.GetUnformattedText()
        startpos = Instr(bodytext,"{")
        Do While startpos > 0
            endpos = Instr(startpos,bodytext,"}")
            If endpos>0 Then
                temp = Mid$(bodytext,startpos,endpos-startpos+1)
                Set placeholder(temp & "~" & startpos) = New PlaceHolderData(temp)    ' Add to list of placeholder objects        
            End If
            startpos = Instr(endpos,bodytext,"{")
        Loop
    End Function

    Public Function MergedRichText() As NotesRichTextItem
        Dim range As NotesRichTextRange         
        Dim cnt As Integer
        Set tempbody = sourcefield
        Set range = tempbody.CreateRange 
        Forall p In placeholder
            Call p.ProcessPlaceHolder(sourcedoc, maindoc)
            If p.text = "" Then
                p.text = " -- "
            End If
            cnt =  range.FindAndReplace(p.placeholderstring, p.text, 1+4+8+16)            
        End Forall
        Call tempbody.Compact 
        Call tempbody.Update 
        Set targetfield = tempbody
        Set MergedRichText = tempbody
    End Function    

    Public Function Content() As NotesRichTextItem
        Set Content = targetfield
    End Function

End Class

I will try to post some sample code later, using this script library.

0 Comments

Samsung Galaxy S3 released – Will it live up to the hype?

Yesterday Samsung presented the new Samsung Galaxy S3 smartphone in London. Samsung had been very secretive about the new phone, and speculations on the internet were wild. Some of the rumors were true, while some were not. So will this be an iPhone contestor? I think so, for anyone who look at functionality, technology, etc. For anyone content with the iOS experience and being locked into the Apple eco-system, I am sure the next iPhone will be the given choice.

The predecessor, Galaxy S2, have been a big success for the company who recently took the title of biggest phone manufacturer in the world. Early reviews of the S3 have been mainly positive. The main complain is the plastic case, which makes the phone seem cheaper. There were rumors about aluminum or ceramic cases, but that did not end up being the case (pun intended).

The phone got impressive specifications, with a huge 4.8" screen (720×1280 pixel), quad-code Exynos 4 processor (at least for the European/international 3G model, the US 4G LTE model is rumored to get a dual code Snapdragon S4 instead), 1GB RAM, up to 64GB internal storage and expandable up to 64GB more though SD card. The camera is alos a little bit of a disappointment, instead of the rumored 12MP it ended up being 8MP, but with some nice functionality like burst mode and automatic selection of the best picture. The battery has a 2100mAh capacity, better than the S2 but with the faster and more power hungry processor, it will be interesting to see real numbers on how long the phone actually lasts.

NFC (near field communication) and proximity sensors are other interesting features. The proximity sensor allows the phone to detect that it is lifted to the user's ear, and if a text message is curently being viewed, the phone understand that the user want to dial the person and dial automatically. There is also voice control, similar to Apple's Siri, and the front camera use eye tracking to detect when a user is looking at the screen. That means that if you are reading a long text, you don't have to touch teh screen every 20 seconds to keep it from dimming/turning off, the phone know you are looking at it and keep it on until you look away.

The phone supports a number of formats for audio and video, including MP3, AAC, Flac, OGG, Divx, WMV, etc. It also supports full 1080p recording and playback thorugh an external HDMI-adapter. Another interesting option is a wireless charging station.

Availability: In Europe on May 27 and in the US "this summer", with a end-of-June date hinted at. Price has not been released, but it is expected to be $299 on a 2-year contract with US carriers, and $700 without contract.

It will be interesting to see how it compare to an upcoming iPhone 5, rumored to be released after the summer. Currently the Galaxy S3 looks like the phone to get, especially if you want an Android based phone.

Samsung Galaxy S3 - Press Picture

 

0 Comments

Ubuntu 12.04 to be released tomorrow

The next version of the Linux distribution Ubuntu is scheduled to be available for download tomorrow, April 26. Ubuntu 12.04 will be a so called LTS (Long Term Support) version, which means that it will be supported longer than the regular versions. For 12.04 the support will last for five years, both for the server and the desktop version. Previous LTS-releases were supported for five years for the server version, but only three years for desktop. Non-LTS-releases are supported for 18 months after release.

Ubuntu 12.04 is using version 3.2 of the Linux kernel. Below is a list of other changes, taken from the Ubuntu website:

New Applications and Defaults

  • The default music player has been switched to Rhythmbox, which again includes the UbuntuOne music store.

  • LibreOffice has been updated to 3.5.1.

 

Interface updates

  • The System Setting's "Appearance" panel now allows users to easily configure some properties of Unity.

  • Nautilus quicklist support has been added to Unity launcher.

  • There is a new way to quickly search and access any desktop application's and indicator's menu, called the "HUD". Tap the Alt key and enter some letters and words, and it will show the corresponding entries, including some fuzzy matching. Press enter to execute the action. This one is executed on the current focused application as well as all indicator facilities (like managing emails, sound list, quit…). Also it learns from your previous choices to make the search more and more accurate for you.

 

ClickPad support

ClickPad devices are trackpads where the physical button is integrated into the trackpad surface. Ubuntu Precise now has enhanced support for these devices. When the button is pressed on a ClickPad device, a second finger may be used to drag the cursor.

  • ClickPad support requires extra handling that conflicts with "Click Action" support. Click Actions allow for separate actions when multiple fingers are active on a trackpad. The default Ubuntu settings enable right button behavior when two fingers are in contact with the trackpad surface and the physical trackpad button is pressed. Because of conflicting behavior, ClickPad devices do not support Click Actions in this release.

  • Most Synaptics brand ClickPads are recognized out of the box. Apple MacBook trackpads are recognized as well. Support for Apple Magic Trackpads and more Synaptics brand ClickPads will follow in the next release.

 

Ubuntu One

  • All new Control Panel provides an installer, setup wizard, ability to add/remove folders to sync, and more

  • Proxy support is now fully functional

  • Ubuntu One music store in Rhythmbox

  • Ubuntu One contacts sync has been removed

  • Nautilus ribbon with enable sync check box has been removed

 

Other

  • Until Ubuntu 11.10, the Unix group for administrators with root privileges through sudo had been admin. Starting with Ubuntu 12.04 LTS, it is now sudo, for compatibility with Debian and sudo itself. However, for backwards compatibility, admin group members are still recognized as administrators.

  • Automatic Apport crash reporting now checks for duplicates on the client side, which will avoid uploading debug data and creating Launchpad bug reports unnecessarily in many cases now.

  • When installing new software through software-center, corresponding language support packages (translations, spell check modules particular to that software, help files, etc.) are now installed along automatically. This removes the need to open "Language Support" after installing new software.

  • pm-utils now has two new scripts to power down USB and various PCI devices in battery mode. A number of desktop packages were fixed to wake up less often. Both of these reduces power consumption and thus battery lifetime.

  • resolvconf is now used to manage /etc/resolv.conf on all Ubuntu systems. For systems using Network Manager DNS resolution is now done through dnsmasq, which should help split-DNS VPNs and faster DNS resolution. You can learn more here

  •  Output from Upstart jobs is now logged to per-job files in /var/log/upstart/, unless the job specifies otherwise by setting console to something other than the default of log (see init(5)).

 

You can download it (starting some time tomorrow) here. But be prepared thatthe servers might be busy for the first few days.

 

 

0 Comments

End of content

No more pages to load