Tim.Tripcony@lotus911.com
Blog Tags
|
Tim TripconyTim.Tripcony@lotus911.com
For as long as I can remember (possibly 2001) I've always maintained some variation on a SmartIcon toolbar button that a former coworker called the "magic button": it allows new fields to be added to a document and existing fields to be overridden or deleted from the document, either while editing the document or while it's selected in a view. I've seen various approaches to this basic concept over the years, and each time I install Notes on another computer, I either consult the Google to find one I've used before or just reconstruct it from memory. It finally occurred to me that if I just posted the one I'm currently using here, not only would it be easier for me to find later, but some of you might find it useful as well. So here's the current incarnation of said magic button:
targetField := @Prompt([OkCancelEditCombo]; "Select Field"; "Select a field to override:"; ""; @DocFields );
updateTypes := "Text":"Number":"Time":"Delete Field";
updateType := @Prompt([OkCancelList]; "Select Type"; "Choose a type or action:"; "Text"; updateTypes );
@If(updateType = "Delete Field"; @Return(@SetField(targetField;@DeleteField)); "" );
newValue := @Prompt([OkCancelEdit];"New Value";"Enter the new value:"; @Text(@GetField(targetField )));
newTypeValue := @Select(@TextToNumber(@Replace(updateType; @Subset(updateTypes;3);"1":"2":"3" )); newValue; @TextToNumber(newValue); @TextToTime(newValue));
@SetField(targetField; newTypeValue)
I'm sure you can decipher the above, but here's what it does:
- Asks you which field you want to override. Type in a new one if you're adding a field. I've seen other versions where something like "---NEW---" is included in the field list prompt, and if you select that, it asks for the new field name... figured I might as well skip that extra step by just using [OkCancelEditCombo].
- Asks what data type the new value will be (text, number or time), with an option to delete the field instead - in which case the formula just deletes the field and exits.
- Asks what the new value will be, defaulting to the current value (if any).
- If the new value's data type is not text, converts the value to the selected type.
- Writes the new field value to the document.
This little button has saved me oodles of time over the years...
Tim TripconyTim.Tripcony@lotus911.com
I just bogarted registered PercentRem.com. At the moment it's just a default instance of DominoWiki (although I did update the logo), but my current plan is to dedicate that wiki entirely to LotusScript. The language definitely has its limitations, but it's amazing what it can do when properly coaxed. My hope is to make this a living, breathing, centralized repository of tips, tricks, pitfalls, righteous hacks, and so forth. Let me know if you have any thoughts, ideas, suggestions, etc. Due to Ben's excellent versioning engine, I'ma just leave it wide open for the time being, so feel free to drop in any content that you feel would be a good fit. Over time I hope it will coalesce into a semi-structured reference on what the language truly can and can't do.
Tim TripconyTim.Tripcony@lotus911.com
I just had an epiphany... or maybe just an apostrophe. I'm in the middle of some code that does some date matching (determines which document to process based on date information posted from a web application). I don't have control over the date format posted to the agent, and currently the format is [Month abbreviation][unpadded day number][day number suffix] - i.e., Apr15th. I already knew that passing "April 15th" to the constructor of a NotesDateTime object would create an instance representing [04/15/2008 12:00:00]. But did you know that "Apr15th" will too? As it turns out, it actually ignores the suffix: "Apr15bogus", for example, creates a date with the same properties. Always good to find these things out before I resort to string parsing...
Tim TripconyTim.Tripcony@lotus911.com
As you may have noticed, I've recently become quite the Linux fanboy. Well, today I took it a step further. Yesterday my Dell Inspiron (which I'm using to post this) was running Windows XP with Ubuntu 7.10 running as a VMware virtual machine... today it's the opposite. Of the five computers in our apartment, the ratio is now 3 Linux (1 Ubuntu, 1 OpenSUSE, 1 Xandros) to 2 Windows... and one of those two isn't even mine; the other is Laura's Inspiron, and she hinted today that she might switch to Ubuntu soon too, since she uses exactly two programs: Firefox and OpenOffice - and she knows that both just come pre-installed with Ubuntu. Scratch that; I just remembered that she recently installed a third program: Aptana (which on Linux you don't install, you just unzip to a folder of your choosing, much like Eclipse on Windows). She's dabbling in PHP and had asked which IDE I would recommend. In short, we may soon be a Linux-only household; to the owner of the Latitude, don't worry... I'm leaving Windows alone on that machine.
Unlike on OpenSUSE, installing VMware on Ubuntu was a breeze:
apt-get install vmware-server
Only caveat: although it's free, you still need a valid serial number, and apt won't configure the package installation until you've entered one. Once installed, I spun up a new virtual machine (pointing it to an ISO of my XP CD), and in less than an hour had Notes reinstalled, complete with a data folder overwritten from the backup I'd saved to our NAS before converting. So Notes looks and behaves precisely as it did before. I decided to install Office 2003 because I loathe 2007. Nothing else... nice clean registry, so Windows is actually far snappier running as a VM than it used to be with all of the extra clutter.
On top of the base Ubuntu distro, I've added Skype, Eclipse, Aptana, and of course, Notes 8.0.1. When I tried to install it, the wizard window popped up, but it was just an empty panel. Mayhap I just didn't wait long enough, but after about five minutes, I killed it and just opted for the silent approach:
./setup.sh -silent -V licenseAccepted="true"
This installed Notes with Sametime, but without Symphony or the composite app editor. I'm actually not using Sametime at the moment; although it doesn't have all the fancies of Sametime 8, I actually prefer Pidgin (which is also pre-installed in Ubuntu) because it bundles all of my IM accounts (AIM, Yahoo, MSN, GTalk, MySpaceIM, and Sametime) into a single interface.
By the way, Jens Bruntt posted a great article last week on how he installed Notes on Ubuntu. The fix he outlined for the embedded browser isn't working for me yet (for instance, xulrunner didn't install in /opt, and including a reference to /usr/lib/xulrunner doesn't seem to fix the problem), but to be honest, I don't use the embedded browser much anyway. So far, the only thing I miss is iTunes, but between gtkpod and the Amazon MP3 Downloader, I'm pretty much covered on that front as well.
UPDATE: Designer works flawlessly in wine. My Windows VM is now sleeping peacefully, doubtless dreaming of a day when I'll come crawling back, telling it I can't live without it. Don't feel bad, Windows... it's not you, it's me.
Tim TripconyTim.Tripcony@lotus911.com
Julian's post about JavaScript stack traces reminded me of something I wrote late last year and had been meaning to mention but never did.
Web applications have steadily become more interactive over the last couple of years, and have been evolving from a collection of pages to a sequence of interactions and events. On the one hand, this can be very positive for the user, as we're no longer forced to trigger an entire page refresh for every mouse click. On the other hand, one challenge this presents is that many users are still in the habit of conceptualizing site navigation and interaction in a context of page history; in other words, they expect to be able to click something (i.e. a button or a link), examine the result, then click the Back button and see what they saw before... and click Forward to return. This was already a problem before Ajax when POST requests were involved, but adding Ajax to the equation can compound this even more: by default, not only will clicking Back not show the user a previous state of the current page, in a "one-page application", it'll actually exit the application, possibly even your entire site.
So how can we get around this? Ironically, most workarounds I've seen (including the one that I implemented and am about to describe) make use of the HTML element that was often used to provide Ajaxy behavior before what we now consider to be "true" Ajax gained widespread use: namely, the iframe. When an iframe's location changes, an entry is added to the browser's history (in some browsers... more on that later), even though the location of the page containing it has not changed. As a result, clicking the Back button will simply return the iframe to its previous location without modifying the container's location. Similarly, once that's occurred, the history contains an entry in the forward direction, so clicking Forward at that point will again navigate only the iframe, still leaving the container location intact.
How does that help us? Chances are, those familiar with JavaScript's Function.apply() have already guessed where I'm headed with this. As I commented on Julian's post, a handle on a function can be passed as a parameter to another function. This is used all the time in Ajax requests to specify a callback function: once the request has been processed, the specified callback function is executed to respond to the result of the request. In this case, however, we're not associating a function with a request for remote data (or submission of data); instead, we're actually associating a function (and the parameters that should be passed to it) with navigation. Here's how it works:
- Instead of calling the function directly, we pass a handle on the function (along with an array of parameters to call it with and an optional scope) to another function - AjaxHistoryManager.register(). For example:
function sayHello (sender, recipient) { this.innerHTML = recipient + ', ' + sender + ' says hello.'; }
AjaxHistoryManager.register(sayHello, ['Me', 'World'], document.getElementById('helloDiv')); NOTE: the lack of parentheses after the reference to sayHello is intentional; we're not calling the function at this point, just passing it as an argument.
- The register() function creates an object to store the function handle, parameter array and scope object (which defaults to the passed function if no scope is specified), pushes that object to an array that stores all of the function calls that have been registered since the container page was loaded, and increments a counter.
- Finally, register() sets the source of a hidden iframe to the URL of a Page design element (assuming you're using Domino; a very minor tweak to this would allow it to behave identically in PHP, ASP, JSP... pretty much anything), including a query string parameter indicating the current counter value.
- The Page loaded in the iframe has only one job: it includes a script tag that (using computed text) passes the counter value to window.parent.AjaxHistoryManager.execute(); this function retrieves the object stored earlier and calls the associated function with the stored parameters, binding "this" to the correct scope object. The reason I almost never use "this" in JavaScript is that any number of factors can cause a function to lose a handle on the scope you originally intended it to have, which will cause any references to "this" to apply to the new scope. That's not a problem in this case, because we're calling .apply() on the function to be run, which allows us to explicitly specify a scope object. So, in the example above, "this" refers to the HTML element that was passed as the scope object. If the function you're registering doesn't have any references to "this", you can leave off the scope parameter entirely and just pass the function handle and a parameter array.
The result is that the registered function is still called almost immediately (depending on how long it takes your server to send the 94 byte overhead for the iframe Page), but the browser history now contains an entry associated solely with that event. Ergo, executing a series of events in this manner allows the user to execute them again (with the same parameters and scope each time) simply by clicking Back and Forward. You can see an example of this behavior here (the sample database is also available for download). On that page, click the "Increment Counter" button a few times, then click Back and Forward (Alt+Left / Alt+Right also work)... the displayed counter will increment and decrement based entirely on those navigation events. This works correctly in Firefox and I.E., but not Safari or Opera. In Opera, navigation does appear to update the iframe, but the stored function is not called; in Safari, clicking Back actually navigates the entire container page. In both cases, though, the function is still executed initially upon registration; in other words, your application will still work, but will not have the benefit of triggering previously executed functions again via navigation events. I still have never had to support either of these browsers within an enterprise context, but if anyone has ideas on how to extend this approach to fully support either or both, let me know. One last disclaimer: this approach is best suited for "read-only" operations. For example, user interaction causes some change to a portion of a page (i.e. resorting data), and we want reverse navigation to "undo" that change. This approach gets a whole lot messier if a POST is involved: if the methods being executed are actually updating data in the database, then executing them in reverse order needs to revert the database content to its previous state - whether by resetting field values on one or more documents, or actually removing documents that were added by the executed method(s).
Tim TripconyTim.Tripcony@lotus911.com
Saw a few postings about how various bloggers got into Notes, so I figured I'd wax nostalgic as well.
During the holidays in late '97, shortly after my 20th birthday, I came to the vivid realization that I had no idea what I wanted to be when I grew up. Which was a bit awkward, since I was already three semesters into my pursuit of a degree in music. Having worked at Chick-Fil-A on and off for the previous five years, I decided I'd just go back and man a grill until I'd gotten over my career angst. But my father ( a.k.a. "Pops" ) had another suggestion; at the time, he was a server admin working at a large telecom... large enough that they had a whole team of people whose job was creating and deleting email accounts... all day long. In fact, they were a bit short-staffed, and he suggested I apply. He warned me it might be boring but would pay slightly better than what I'd been planning to do, and I wouldn't smell like chicken when I got home. He booted up his Thinkpad, and I got my first look at Lotus Notes ( 4.5, in case you were curious ). That's right: I'm a second-generation Yellowbleeder.
A few weeks later, I'd received a crash course in basic Notes administration from the contract agency and was happily creating and deleting Notes accounts all day. Part of our process was to leave the Notes ID in the address book so that we could call the user, walk them through installation of the client and initial workstation setup, at which point the ID file would be automatically downloaded to their machine and removed from the account record. But to ensure that we had a safe copy of the ID, we'd also save the file locally and then attach it to a document in a custom database that tracked when the account was created, the initial password (to allow for password resets... this was 4.5, after all), and various other bits of info about the user. So, as you might expect, I spent a lot of time in this particular database. I thought the icon was rather boring, and decided to replace it with something a bit more interesting, and easier to spot at a glance on the Workspace... Unfortunately, during my crash course, nobody mentioned any development-related concepts... say, for instance, that the database icon was a design note stored within the database itself and, therefore, determined how the chiclet would display for everyone, not just me. Imagine the lead administrator's surprise when he glanced at the icon for the ID vault database one morning and noticed it had been changed to the Grateful Dead "dancing bear":

Though embarrassed to admit that I'd made the change without realizing the impact, in retrospect I'm quite glad that happened, because I was finally told about the nature of design notes and their role in the NSF structure. I'd soon automated the bulk of my job duties, and over the next few months became "the administrator's developer", creating various in-house tools for user and server administration. Ten years have passed. I still don't know what I want to be when I grow up (bah, like that'll happen), but I do know that at some point when I wasn't looking, something originally meant to pass the time and pay the bills evolved into a career, and then into a passion. We few, we happy few...
And yes, as far as I know, the icon for that database is still the dancing bear. And now you know... the rest of the story.
Tim TripconyTim.Tripcony@lotus911.com
Yesterday (April 1... I know it's technically April 3 now, but for me it's still Wednesday) was mildly momentous. Although the following graph hasn't quite caught up yet (UPDATE: now it has), the server that hosts TimTripcony.com broke its uptime record:

As the graph indicates, the red X's (which hover, on average, closer to 0) are from the server's stint as a Windows box; the blue represent its shiny new Linux identity. Since the conversion in early December, it's only had one reboot... and that's only because we had a brief power outage (bad Timmy, no UPS). Other than replacing the hard drive (with one that was actually older than the Windows drive, just hadn't ever been used), the hardware is the same - same motherboard, processor, NIC, RAM, etc.... it just stays up now. No babysitting: no weekly defrag, no chkdsk to try to keep it from tanking, no weird behavior if I ignore a security update on a server that's only externally accessible on ports that I'm securing via Domino anyway... it just stays up. Is Linux administration "harder" than Windows administration? Sure... if, like me, you don't originally come from a UNIX background, there's a lot that may seem counter-intuitive about it. You may spend a bit more time on "the Google" looking for the exact syntax of various commands. But I'd personally rather support something I rarely have to touch - even if that occasional interaction isn't quite as intuitive as its Windows equivalent - than have to constantly babysit a system just to keep it operational.
Tim TripconyTim.Tripcony@lotus911.com
We are pleased to announce the availability of Sametime Advanced on BleedYellow.com!!
Check it out for yourself!
|
|