A little while ago I wrote about our process for prioritizing bugs for fixing. Mathieu Pape has a related idea recently posted on IdeaJam, that you might consider supporting if you're interested in this issue. It seems like a good notion to me.
Andre Guirard | 19 June 2013 05:40:01 PM ET | | Comments (1) | Permanent Link
The low-down:
XPages lets you write code to calculate the values for selection lists. The value your code returns may either be an array of strings, using the pipe symbol ("|") as a delimiter between display value and stored value, or it may be an array of javax.faces.model.SelectItem objects, which each contain a display and stored value as separate data items. It's your choice. The latter method, however, is more bulletproof since you don't have to worry about pipe symbols in your data.
Experienced Domino developers are used to specifying the choices for keyword lists -- comboboxes, radio buttons and the like -- using the pipe symbol ("|") as a delimiter between the value displayed and the value stored, hence: "Yes|1", "No|0".
For XPages, the Designer UI provides a separate place for entering the display value and stored value if you have a hard-coded list:
If you need to calculate the value of the selection list at runtime, using the pipe delimiter is still an option. Here, for instance, is the XSP code for two hardcoded choices and two calculated choices (or whatever number the code chooses to return). For simplicity, the calculation produces a constant value, but use your imagination.
<xp:selectItem itemLabel="Yes" itemValue="1"></xp:selectItem>
<xp:selectItem itemLabel="No" itemValue="0"></xp:selectItem>
<xp:selectItems>
<xp:this.value>#{javascript:(["Maybe|2" , "None of your business|3"])}]]></xp:this.value>
</xp:selectItems>
</xp:radioGroup>
This works. Or, you can return an array of objects:
<xp:this.value>#{javascript:([new javax.faces.model.SelectItem("2", "Maybe"), new javax.faces.model.SelectItem("3", "None of your business")])}]]></xp:this.value>
</xp:selectItems>
Although it's somewhat longer, the nice thing about the latter example is that it doesn't fail if the "display value" contains a pipe symbol. This can occur in any case where you don't control the incoming data (for instance, if the choices are looked up from data in user-created documents). Or, as in my previous blog entry, the pipe symbol is used if you're using an application with localized values in it. When the .properties files are automatically generated, the to-be-translated strings in the foreign-language .properties files are prefixed with a string which includes a pipe symbol:
radioGroup1/xp\:selectItem[2]/@itemLabel=[sq| No ]
So if your strings are somehow derived from .properties files, the application will fail to work in other than the original language until translation is complete. That is, of course, only one way that "|" can get into your data, so if you want to be safe, use the SelectItem object -- even if you have no intention of translating the application.
Andre Guirard | 24 May 2013 09:46:05 AM ET | | Comments (0) | Permanent Link
I may be stating the obvious, but I wasn't the only one on my team caught out by this, so I thought I'd best mention it.
I recently discovered the hard way that there's a problem with using < script > elements in XPages. Always use < xp:scriptBlock > instead. Why, you ask? Either seems to work fine!
The problem comes up if your application is translated. The code that decides what parts of your page are translatable doesn't know anything about the < script > element, so your JavaScript code is marked as a localizable string. It's very rarely the case that you would actually want that translated. Using < xp:scriptBlock > is a best practice that avoids trouble down the road, even if you're only working in one language now.
BTW if you're thinking the translators will be smart enough to recognize source code when they see it and leave it alone, (1) not a good thing to count on, and (2) the other-language property files will mess up the functionality even before translation starts, because they tag the to-be-translated strings with, e.g. "[zh_TW" at the beginning, which won't still be executable code.
Andre Guirard | 15 May 2013 01:10:57 PM ET | | Comments (1) | Permanent Link
An issue recently came to my attention that (based on my informal poll) has been a thorn in the side for Domino administrators for quite a while. I've been trying to figure out how it is that it's never been fixed.
I'm referring to the fact that end users, when they create or rename a folder, are allowed to use various characters that have special meanings in design element names -- backslash, vertical bar, underscore, forward slash (which causes problems in mobile) and enclosing the name in parens (which makes the folder hidden in the UI). It's a nuisance for administrators, because recovering the "corrupted" folders is hard to do without writing code.
For a long time, we didn't have a bug report for this -- I finally wrote one, SPR # AGUD8USQ68. We didn't have a bug report because nobody ever called it in to Support. We have a system for prioritizing bugs. Recent regressions are at the top of the list. Problems that have been in the product for one release or more, are a distinctly lower priority, unless someone complains about them. By which I don't mean bitching about it in IdeaJam or even asking about it at IBM Connect. The most effective way to boost the priority of an issue, is to call IBM Support and open a PMR. An SPR with one customer report is approximately 100 times more likely to be addressed than one with no reports (especially because the SPR with no reports generally doesn't even exist). An issue with two reports is twice as likely to be addressed as an issue with only one. Reports from development count for approximately nothing. It's all about the customers and partners.
So when a business partner wrote to me recently to complain about this issue, I told him if he really wanted it fixed, he should call it in to support. I personally would love to see a lot of these annoying issues given priority, but people generally only call in to Support when they're blocked -- not when merely annoyed and inconvenienced. So we now have one (1) report about this issue. That's almost certainly not enough to get it fixed in a quarterly release, though it might get into the next major release.
So, if there are issues on your personal list, things about the Notes client, the mail template, or Domino that are an occasional problem, that you feel might be damaging end-user confidence in the products, impairing productivity, or just generally causing annoyance, and you want the annoyance to stop, then call IBM Support and open a PMR. And then call your Domino buddies and encourage them to do the same.
EDIT: You can also weigh in on this specific issue on IdeaJam. Thanks, Mathieu Pape, for posting this.
Andre Guirard | 13 May 2013 10:23:11 AM ET | | Comments (9) | Permanent Link
I while back I wrote a post containing code to clean up empty folders from user mail files. Yesterday, someone pointed out to me that it doesn't take into account the fact that folders that contain subfolders can be technically empty, in the sense of containing no documents, but we don't want to delete them if they have subfolders which are non-empty -- or if they have subfolders we don't also want to delete. So I came up with a new version of the code that deals with those situations.
As background, there actually is no such thing as a folder that contains another folder. Folders can contain documents. The apparent containment of folders in folders comes from the way the mail application's navigation pane organizes its outline. Folders whose names contain a backslash are displayed as subfolders in the outline, so for instance "Customers\Yoyodyne" appears in the UI as follows:
It's not necessary for there to even be a folder named "Customers" for the subfolder to exist and be displayed in this way. From the end user's perspective, though, this is a detail we would like to hide. So the goal for this tool is to act as if folders contained other folders, even though this is not technically the case.
So the behavior reflected by the code attached below is:
- We'll show the user a list of empty folders and let them select which ones to delete.
- A folder is considered empty if it contains no documents and all its subfolders (if any) are also empty.
- The list of empty folders will not include any subfolder if its parent is also listed. So in the above example, if Customers contains no documents and Customers\Yoyodyne is also empty, we would only list Customers as a candidate for deletion.
- Selecting a parent folder for deletion also deletes its subfolders (same as if a folder is deleted from the mail navigator).
Andre Guirard | 10 April 2013 10:36:59 AM ET | | Comments (2) | Permanent Link
In case it makes a difference for your application, here's how to tell whether you're running in the Notes browser plugin. So far, the only difference that's mattered to me is that the [FileExit] command doesn't work in NBP, causing an error dialog if you try it.
Function IsNotesBrowserPlugin
Description: Return TRUE if we're running in the Notes browser plugin.
%END REM
Function IsNotesBrowserPlugin As Boolean
Dim session As New NotesSession
If session.Notesversion >= 400 Then
Dim tmp
tmp = Evaluate({@IsNotesBrowserPlugin})
IsNotesBrowserPlugin = tmp(0) <> 0
End If
End Function
Andre Guirard | 19 March 2013 03:17:15 PM ET | | Comments (0) | Permanent Link
It's been an ordeal. I have an Android smartphone and use Gmail's calendar. My wife has an iPhone. You'd think it would be simple to have a shared calendar -- this is, after all, the future. As it turns out, it is possible, but the arduous process has made me wonder whether Apple considers it a form of apostasy for their customers to associate with someone who doesn't use their products.
We had two alternatives to start with: we could create our shared calendar in Gmail or iCloud. No preference, so long as we could both create entries and edit any existing entry. But Gmail is an open system -- anyone can create an account and make full use of the features, without owning any particular hardware. Apple, we found, limits access to iCloud to owners of Apple hardware. So we created a Google calendar.
Really, if Apple makes it difficult to share a calendar with people not using their hardware, any group who wants a shared calendar on the cloud, will have to create it in Gmail, since not every member will be an Apple user.
It isn't hard to synchronize a shared Google calendar onto the iPhone, but the sync is buggy. Two bugs in particular caused problems:
- If a calendar entry wasn't created on that iPhone, or if it has since been edited elsewhere, iPhone won't let the user edit it. The same user has no problem editing the entry in the Google Calendar web interface, with Android, nor, I understand, with Windows smartphones. I'm not certain whether this is a case of everybody misinterpreting the CalDAV spec except Apple, or whether Apple is the one with the error (but I have my suspicions). I read that this is a new problem with iOS 6.0.1, an apparent regression caused by Apple's attempt to fix a problem with accepting invitations, so I'm inclined to think that this is Apple's problem to fix. Every tech in the local Apple store knew of this, none of them had a workaround, and they said there was no official notice of the issue from Apple nor any planned date to fix it.
- On the iPhone, my wife has a default event notification of one hour before. On one event in the shared calendar, my wife wanted a two-hour notification, so she changed the notification time. When the entry synchronized with Google, a second notification was added at the default one hour before, so now the event had two notifications -- two hours and one hour.
By the way, it's amusing how when you share an Apple calendar with an email not already in Apple's system, it sends them an "invitation" to join iCloud, then if they accept the invitation it's, "Oh by the way, did we forget to mention we expect everyone with an email address to also own an iPhone?"
Fortunately, a tech we talked to at the Apple store told us that iCloud.com doesn't require that the device originally registered to get access to the site, remain registered to the same ID. So we deleted my wife's account from the iPhone and set mine up instead, logged me in to iCloud, then switched it back to hers. Apple, if this is in violation of your TOS or something, sorry, but you left us no choice. I'd be happy to discuss this with any representative of your company who can also answer why no public notice has been taken of no-edit issue, which thousands of people have been complaining about all over the internet for months. I'd gladly switch back to the Google shared calendar if it worked
So great, we have a shared calendar in iCloud and I can access it via a web browser. Now, how do I get it onto my phone, and how can I have an offline copy in my preferred mail reader, Thunderbird (with Lightning calendar add-on)*? For that, you need a CalDAV URL to sync with (or iCalendar if you prefer, but I understand that's read-only). You would think that, in the spirit of interoperability and customer convenience, Apple would make it simple to determine what the URL is.
Well, no. They didn't get that memo. Not only does the iCloud UI not make this information available†, Apple's lawyers come after you with long sharp knives and gleaming teeth if you provide people a tool to tell them what the URLs are. Daniel Mühlbachler has written such a tool, and apparently can get away with making it available if he makes it impossible for anyone but developers to use, by only providing Java and PHP source code. To be fair, this does make it harder for someone to steal people's IDs, since you can read the source to check whether it's sending your login information off somewhere. But it makes the functionality unavailable to the average user. Fortunately, I know these programming environments, so I was able to compile the tool and use it to find the URL I needed. But why does Apple make this so hard?
Now for my Android phone. By this point it came as no surprise to me that there was nothing already on my phone to do this, and I wasn't too shocked that Apple didn't offer a free app for it. But fortunately, an enterprising third party has made this capability available for the low low price of $2.81. Who knows whether it will continue to work... Apple or Google might make some change that breaks it. But anyway, we're set for now -- provisionally!
--- --- --- --- --- ---
* My preferred mail reader is Lotus IBM Notes 9.0, but among free, open-source software such as I can put on my netbook without being obligated to pay anything to anyone, I think Thunderbird is best. Or at least, Lightning is a little buggy, but I don't see enough benefit in looking for something better.
† It's possible to get the URL of a private calendar if you read a lot of Apple forums to find out how, and copy and paste long hex IDs from URL they do provide, into a template CalDav URL. But private calendars are read-only for the people you share them with, and the same technique doesn't work for a shared calendar.
Andre Guirard | 21 January 2013 12:42:22 PM ET | | Comments (0) | Permanent Link
Often, entries in this blog are in reaction to questions I get, or someone else's code I've run across. One of the reasons things have been so quiet here is that my job has changed so that's not happening as often. But every now and them I'm reminded that there are things I know that it hadn't occurred to me weren't obvious, until I run across another developer who didn't know it.
That's the case with this tip. Someone mentioned that it was taking their code a long time to scan mail files to see whether there were any design elements that were protected from design refresh/replace (aside from personal folders, where this is expected). I didn't know what they meant by "a long time," so I wrote a few lines of code to scan my mail file, and wrote back that when accessing the file on a server, I couldn't see how to do the search faster than about one or two seconds, and what was their target?
As it turned out, 2 seconds was a considerable improvement over the 220 seconds average they'd been experiencing. The difference was caused by their opening each design note and checking the $Flags and $Class items in it, whereas my code just handed the server a selection formula and let the server do the heavy lifting. Here's my code (using LotusScript because it's my favorite, but it's simple to translate to Java if you're so inclined):
tt = Timer
Dim db As New NotesDatabase("server", "filepath")
Dim nnc As NotesNoteCollection
Set nnc = db.CreateNoteCollection(False)
nnc.SelectAllDesignElements True
nnc.SelectionFormula = { @If(@Contains($Flags; "P") | $Class != ""; $Flags != "3PFY"; @False) }
nnc.BuildCollection
Print nnc.Count & " found - " & (Timer - tt) & " seconds."
(Of course, if any protected design elements were found, it would take extra time if you wanted to generate a list of their names and types, but just confirming a mail file is "clean" -- that nnc.Count = 0 -- is that fast).
The reason for the difference is the same as the reason it's faster to search for documents using the built-in search functions than to iterate through all the documents in a database; when the server searches, it can use indexes and summary fields, plus it has access to all the documents locally rather than through network transactions. An NSF file also contains an internal index of design elements, very similar to a view, which helps in conducting design element searches (I'm not certain whether that was a factor in this case). If you open a design element using a NotesDocument object, the entire design element has to be fetched from the server. If you use the selection formula, the only information sent back over the network is the list of matching note IDs.
Andre Guirard | 8 January 2013 08:53:07 AM ET | | Comments (1) | Permanent Link
If you're thinking of submitting an abstract to the Pulse 2013 conference in Las Vegas, you might like to register for this webcast that explains the process. If you've been doing something interesting with IBM technologies, here's your chance to shine!
Andre Guirard | 17 October 2012 01:55:51 PM ET | | Comments (0) | Permanent Link
I keep getting asked where to get the sample code for the article "Working with file attachments in the LCLSX", which used to be in the Lotus Sandbox before that went away.
The code is here: LcLsxTracy.nsf
Andre Guirard | 5 September 2012 01:50:27 PM ET | | Comments (7) | Permanent Link
