Tim.Tripcony@lotus911.com
Blog Tags
|
All entries tagged with
domino
Tim TripconyTim.Tripcony@lotus911.com
A couple of disturbing observations:
First, in a 2006 post from Julian about animated favicons, the little animated banana dances at the exact same tempo as the song "Your Star" by the All-American Rejects. Try it... it's mesmerizing.
But secondly, to explain the title of this post, I noticed an odd behavior related to image resources in the Notes client: GIF support is incomplete. ICO files are actually stored in the same format as GIF, just with a different extension. So another approach to creating animated favicons is to simply rename an animated GIF to .ICO. That's how Dean Edwards created a favicon that periodically blinks. Theoretically, then, we should be able to download any site's favicon, rename it to .GIF, import it as a file resource, and then display it in the Notes client. No such luck.
Since real browsers (read: Firefox, Safari, and Opera... of course, as always, IE fails) don't distinguish between the two formats and simply trust the server-supplied MIME type, navigate to the same image resource via a URL and the image displays as expected. But try to reference the same image resource anywhere in the Notes client (or even just preview it in the image resource list), and you get nothin' but gray.
Sorry, Bruce, I tried...
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 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
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
I haven't seen much discussion about how easy it really is to consume web services in Notes/Domino 8 (perhaps precisely because it is so easy), so I thought I'd outline exactly how one goes about providing and consuming services now.
Let's use a very basic example. Create a script library and toss this in the Declarations:
Public Class House
Private currentColor As String
Private currentValue As Long
Public Sub buy (price As Long)
Let Me.currentValue = price
End Sub
Public Function color() As String
Let Me.color = Me.currentColor
End Function
Public Function value() As Long
Let Me.value = Me.currentValue
End Function
Public Sub sell (price As Long)
Let Me.currentValue = price
End Sub
Public Sub paint (newColor As String)
Let Me.currentColor = newColor
End Sub
End Class Save that library, and create a web service. Include the library you just created via a Use statement in the Options, then in the properties of the service, specify House as the PortType class. The service doesn't need any code of its own, because it's aware of the House class via the Use statement and knows that's the class that defines the service methods. Now it's time to consume the service. From within the service, click "Export WSDL", and save the file anywhere you can get to it later. In any Domino database (including, but not limited to, the database containing the web service), create a new script library (can be Java if you prefer, but in this example, we'll use LotusScript). At the bottom of the window you'll see a WSDL drop-down button. Click that and select "Import WSDL". It'll warn you that this will overwrite your script library (which is fine, since that's precisely what we want); click OK and select the file you just exported. Here's what you'll see: %INCLUDE "lsxsd.lss"
Class House As PortTypeBase
Sub NEW
Call Service.Initialize ("UrnDefaultNamespaceHouseService", _
"HouseService.Domino", "http://localhost", _
"House")
End Sub
Sub BUY(PRICE As Long)
Call Service.Invoke("BUY", PRICE)
End Sub
Function COLOR() As String
Let COLOR = Service.Invoke("COLOR")
End Function
Function VALUE() As Long
Let VALUE = Service.Invoke("VALUE")
End Function
Sub SELL(PRICE As Long)
Call Service.Invoke("SELL", PRICE)
End Sub
Sub PAINT(NEWCOLOR As String)
Call Service.Invoke("PAINT", NEWCOLOR)
End Sub
End Class
Any of this seem familiar? Only one change to the library is needed: replace the reference to "http://localhost" (in the Service.Initialize call) with "http://server/path/db.nsf/servicename?OpenWebService" (where server is the IP or DNS address of your Domino server, path/db.nsf is the full filepath of the database containing the service, and servicename is the name of the web service design element). At this point, you can save the new library and include it anywhere you need to consume the service. And actually consuming it is this easy: Dim myHouse As New House()
Call myHouse.buy(200000)
Call myHouse.paint("Purple")
Call myHouse.sell(5000000) But the real beauty of web services is that it allows you to execute functions against an application you know nothing about, whether it's inside your network or not. For example, if you download this file and import it into a script library in any of your applications, you can pull a random quote from my site as easy as this: Dim Quotes As New Quotes()
Dim Quote As Quote
Set Quote = Quotes.getRandomQuote()
Msgbox Quote.Content & Chr(13) & "- " & Quote.Source
Tim TripconyTim.Tripcony@lotus911.com
import lotus.domino.*;
public class JavaAgent extends AgentBase {
public void NotesMain() {
try {
Session session = getSession();
AgentContext agentContext = session.getAgentContext();
DxlFactory.createAgent("James Bond", DxlAgent.LOTUSSCRIPT).addCodeEvent("Initialize", "Sub Initialize\n'Shaken, not stirred.\nEnd Sub").toDesignElement(agentContext.getCurrentDatabase());
} catch(Exception e) {
e.printStackTrace();
}
}
}
Tim TripconyTim.Tripcony@lotus911.com
Last month, Greyhawk expressed frustration with a pattern he'd detected in the whitepaper emails they'd been sending, and unsubscribed from their distribution lists (as did many of us). Ed later took them to task on the same issue. I'm keeping them in my RSS strictly because I've also noticed a decline in the quality of tips submitted, and I would like to occasionally serve as some small bulwark against the dissemination of bad advice to noobs. Messiah complex? Yeah, maybe a little. Earlier this week, I spotted a real winner.
In a remarkable display of over-engineering (sorry, Christian), the tip linked to above demonstrates one approach to downloading the contents of a server's INI file. It uses approximately 220 lines of code (including comments and whitespace) to issue a remote console command and store the response in a text file... using Notes API calls to issue the command and Windows API calls to write the response to the file using Notepad. Assuming you're using one of the last three major releases of Notes/Domino, the same exact operation can be accomplished with 8 lines of code. And (gasp) it'll work on all supported platforms.
Sub downloadServerINI (Byval server As String, destination As String)
Dim session As New NotesSession
Dim stream As NotesStream
Set stream = session.CreateStream()
Call stream.Open(destination)
Call stream.WriteText(session.SendConsoleCommand(server, "Show Config *"))
Call stream.Close()
End Sub
I used their comment link (a mailto:... they haven't quite gotten around to implementing the comment feature you'll now find on almost any blog) to send Gervais a comment summarizing the above. He responded less than an hour later, thanking me for the feedback and indicating that he'd update the tip as soon as he got a chance. I just checked, and although it's been over two days since then, there's been no change... except that it now shows that, thus far, it's been rated a 5.00 out of 5.00. So anyone reading this tip that doesn't know any better might think this is the most elegant approach available. I rated it a 1, but apparently the average isn't real-time.
Tim TripconyTim.Tripcony@lotus911.com
By now, we all know (hopefully) not to use GetNthDocument to loop through document collections, but here's something that often gets overlooked: don't use NULL in formula to determine if a field is blank. NULL doesn't exist.
In LotusScript, the keyword Null basically means "unknown" or
"invalid". For example, ArrayGetIndex returns Null if the search value
does not exist in the source array. You can manually assign Null to a
variable - but only if it's a Variant - and then use IsNull to see if
it's still Null, but I'd advise against that, since the only way a
value can be Null is if you specifically tell it to be or assign it to
an expression that returns Null. IsNull( "" ), for example, returns False.
In formula, although @IsNull does exist, NULL simply has no meaning. It's not a reserved keyword, so much like extended syntax in LotusScript,
it doesn't know what it means, so it assumes you're referring to a
field. Since there is no field (again, hopefully) named NULL, NULL
returns "". So technically you get the same result, just more slowly.
This reminds me of trying to navigate one of those voice-activated
menus when I try to call the customer service department of an ISP,
utility company, insurance agent, etc. After trying unsuccessfully to
determine how to contact an actual human, I'll just say "human". More
often than not, the response is: "I'm sorry, I didn't understand you.
I'll transfer you to a representative." The result was precisely what I
wanted, it just took longer than it would have if I'd known to just
push 0 at the start of the call.

In other words, the following formulae all return the same Boolean value:
Subject = NULL Subject = "" @IsNull(Subject) @Length(Subject) = 0
The last is the most efficient, but arguably the least readable. I'd still recommend it, though, for use in column formulae for potentially large views and view selection for any view in a potentially large database. For performance purposes, hide-whens on forms/subforms should be kept to a minimum anyway, but the more you have the more noticeable it becomes if you're asking if some field value = NULL in each: Notes has to check each time whether a field named NULL exists, find out that it doesn't, evaluate NULL to "", and then do a string comparison between "" and the field you've specified. With @Length(FieldName) = 0, it's just checking the length of the field value, then comparing one number to another. Again, slightly less readable, but faster every time.
Tim TripconyTim.Tripcony@lotus911.com
A friend of mine recently set up a Domino server at home running on Fedora, and is currently replicating a few databases with my personal server. He's locked out of the rest of the databases on my server, of course, so the connection document is set to only synch the ones he has access to. But in order to trigger a manual replication (outside of the schedule defined in the connection), he had to issue a separate Replicate command for each database to avoid getting errors about all the databases with shared replica IDs that his server can't access. He wanted a single command that would allow him to just type "go go gadget replication" and have it do everything he wanted, but only what he wanted. As it turns out, IBM posted instructions for this just last Friday.
If you issue a console command with the following syntax: < "filename" ...Domino looks for the specified filename relative to the data directory, and treats each line in the specified file as a separate console command. So, for example, a file named "go go gadget replication" (no extension necessary) in the data directory containing:
Replicate Coyote/ACME names.nsf Replicate Coyote/ACME mail/coyote.nsf Replicate Roadrunner/ACME stuff.nsf Broadcast "(!)Selective replication manually initiated." "Wile E Coyote/ACME"
Wile E can now issue the following console command (the < indicates the rest of the command is a filename):
< "go go gadget replication"
The server will kick off three replications, then pop up a message confirming the comands.
NOTE: on Linux, the file must be owned by notes:notes (or whatever user:group Domino is running under) in order for this to work.
Tim TripconyTim.Tripcony@lotus911.com
An announcement during Lotusphere that grabbed some media attention but didn't seem to even get mentioned at Lotusphere is that Red Hat is now offering a version of RHEL client and server that comes bundled with Notes and Domino, respectively. Our general manager is quoted in the BusinessWire press release, but it doesn't make it clear what (for us, at least) is so exciting about this: if you want to try this out but aren't comfortable flying solo on the initial setup, you call them and they send us. That's right: we'll come install the whole package for (and with) you to get you up and running. I'm a bit fuzzy on the exact details, to be honest, but if this sounds tempting to you, send an email to services@lotus911.com and we'll get you the whole scoop.
(cross-posted from TimTripcony.com)
Tim TripconyTim.Tripcony@lotus911.com
If necessity is the mother of invention, boredom must be the weird aunt that has 87 cats and always smells like cheese.TM
I was going stir crazy here in my hotel room, so I started playing with the XML/SWF Charts library I stumbled upon the other night. Here's a sample of what I came up with. That's live data, generated by 11 lines of code (not counting variable declarations). The sample database
contains the chart library (comprised of various SWF files stored as
file resources), an example page to demonstrate the markup syntax for
embedding the main SWF, and an agent to demonstrate generating the
source XML. In this case it's navigating the referrers by date view in
my blog. Nothing too fancy in this example, but this library allows all
sorts of wacky output formats. Check out the gallery to see some of what it's capable of.
(cross-posted from TimTripcony.com)
Tim TripconyTim.Tripcony@lotus911.com
I'm really becoming a broken record, eh? Or, for you kids in the audience, a degraded MP3 file. By request,
I put together a full demonstration of how documents can be created,
edited, and deleted using AJAX, all without the use of any agents. You
can try it out here. And you can download the demo database here.
Previously, I described how documents can be created and edited
in a Domino database without calling agents. This demo combines the two
- as well as deletion of documents - using a simple contact manager as
an example. The UI is very primitive, but hopefully it illustrates the
premise.
In this example, contact information can be entered and then posted to
the server without a page refresh. The function that posts the document
(thereby creating a new record in the database) in turn calls the
function that loads the view data (thereby displaying the newly created
record). The new data could of course be inserted into the table
immediately instead of reloading the view, but this approach was
intended to illustrate how much faster document creation is when agents
aren't involved. Reloading the view also ensures that the new record
will already be sorted correctly when it's added to the table. In
addition to document creation, each view row includes a link to allow
editing and deletion of existing documents.
Please feel free to post any questions or suggestions you may have
about this... I was planning to write up a far more detailed
explanation of how all of this is tied together, but I'm suddenly very
sleepy.
Oh, one thing I should mention: the reference to Event.onDOMReady at
the end of ajaxeditor.js isn't part of the core prototype.js. It's an
extension I found on Vivabit.
This allows functions to be defined in external js files that would
otherwise need to be called inline or via window.onload... for example,
functions that you want to run immediately, but not until some DOM
element that the function is going to muck with is actually ready to
receive said mucking. The primary advantage of this extension is that
it triggers the functions as soon as it is safe to do so, instead of
waiting for the entire page to load (images, for example).
(cross-posted from TimTripcony.com)
|
|