Sunday, September 30, 2007

PHP-like Templating in Domino

Templating may sound confusing, since "we" have Design Templates, but I believe that is what Ruby On Rails/PHP-guys call this.

Vision:
Imagine you get a XML-spec your customer want data exported in. With my method, you may just be able to copy/paste the spec into a template-document. Add field references, and you are good to go.

The same with HTML-pages. Make a static mock-up in Aptana/DreamWeaver. Paste it into your template (maybe split it into modules), add field-references, and you have a nice-loking, valid (if that's what you're aiming for) dynamic (X)HTML-page.

To make/edit the documents, you can use the notes-generated form.

The demo
I've been wanting to do this for a while, but other experiments have overshadowed this. Thanks to this post by Michel Van Der Meiren, and a couple of beers this weekend, I finally got around to making a prototype.

Since I'm lazy, I don't take the time to make data. I use the FakeNames database, kindly provided by Jake.

I think making the templates took me about an hour, including finding out the names of the fields I was going to use. Rapid deployment..

Document in it's original form:




Simple table with header-module:


>> HTML Template source

XML:


>> XML Template source

Text:

>> Text Template source

Implementation:

Getting the correct content-type
To show the documents in a template, I have a view that opens the Person-documents in a form that has the correct content-type (Form Formula). In the demo, I look for Text or XML in the @UrlQueryString( "template" ). If XML, XML-form, text, Text form, else HTML-form. On WebQueryOpen, I run the agent that applies the template to the document. It prints its output to a NotesRichTextItem.

The template-query string decides which template to show the document in.

Processing the template
I first split UnformattedText of the body-field in the template on #%, which are used to process modules, if there are any. Example of module-reference, #%header#%.

I test each item in the resulting array, item Like "*[A-Z,a-z]". This simple testing-method should work for HTML/XML, because they containt a lot of has brackets, which are not Like A-Z. With text-templates, there may occur errors using this. For more complex content, you could make a LS2J-class that enables you to do Regular Expression tests of the content.

The reason I check for modules first, is that they can contain formula-snippets and field-references, which also should be processed.

In it's current state, it doesn't support modules within modules.

Then I look for field references, which are contained within #$, e.g. #$FirstName#$.

Finally, I look for formula-snippets, e.g. #@WebDbName#@, in the home-link of the simple table template, and pointing to a stylesheet in the application.

>> Demo application

If you're downloading the demoapp


All design-elements made for the demo have DontPanic as a comment. I cleaned out a lot of the original design-elements in the app, to make it easier to find what I made for the demo.

The agent that "renders" documents is called "RenderPage". It's not well commented, but I hope most of the code speaks for itself.

Open the db on the Web, click on one of the people to open the document in its original form.

At the top of the page, there is a button for each template I made for the demo.


All the templates are in the templates-view, modules (only one in the demo) in the modules-view.

Why should I care/You can already do this with a form+content-type HTML


If you add more advanced back-end processing, you could have inline LS/Function calls in the context of the document. e.g.
#LS
Use "MenuBuilder"
makeMenu( #@UserRoles#@ )
#LS
^^This using Execute.

I don't think you need to pass the document-object to the inline functions, since NotesSession.DocumentContext is a global object. From a little test, it seems like it's best to Update the RT-field before running each inline LS-modification to it.

Inline formula-example

#F
@For( i:=0 ; i<10 ; i := i++;
    num := @Text(i);
    html := html + "<tr><td>" + @GetField( "line" + num) + "</td></tr>"
)
"<table>" + html + "</table>"
#F

You can of course do this with a a regular form using a WQO agent + computed elements, but what I hope my proposal of "Notes Templating" can do, is to add a layer of abstraction, maybe to make it easier for notes newbies to make Domino Webapps.

I also think this way is more readable than a lot of computed text/Computed For Display fields. The downside is that it adds processing overhead.

Comments/suggestions are greatly appreciated!

Friday, September 28, 2007

Entry not found in index

If you're trying to open a document from a view, using a stored @Text(@DocumentUniqueID), that don't match the documents UNID, you may get "Entry not found in index" when trying to open the document from the web.

It seems the Domino Server "mistakes" a 32byte hexadecimal string to be the UNID of a document, and ignores the values in the first sorted column of the view.

The quick and dirty workaround is to add a character or two before the stored ID (e.g. "u_" + UNID), in the sorted column of the view, and in the links that point to documents in the view.

The best-practice solutions is for fix the application, and remove a stored UNID as a lookup-value. The simplest alternative is @Unique.

Tuesday, September 25, 2007

Formula on WebQueryOpen

I'm used to putting lookups inside fields, but it's also possible to have this kind of code directly inside WebQueryOpen or in formula-agents that are run on WQO.




Form on web:


You can also run ViewRefreshFields back-end, before the form is rendered.
You may want to do this if you have computed fields that get their values from fields that are set by code in WQO, or by a WQO-agent.



The subject2-field in the first image is computed with value:
@If( @Elements(subject) = 0 ;
    "" ;
    "First name in view: " +
    @Word( subject[1] ; "|" ; 1 ) + " " +
    @Word( subject[1] ; "|" ; 2 )

)



>> Simple Demo-application

Thursday, September 20, 2007

Comments inside formula loops

It isn't possible to put REMs inside @-loops, and therefore I haven't commented these before.

I just discovered that you can put strings that don't belong to a variable, anywhere within the loop.

I've now started commenting like this (ignore the bad example):

@For( i:= 1 ; i <= 5 ; i := i+1 ;
"## If counter is above one, add comma before next number ##";
str := str + @If( i > 1 ; ", " ; "" ) + @Text(i)
);
str
==
Result: 1, 2, 3, 4, 5

The ##'s to make it easier to distinguish comments from code.

This is a basic knowledge kind of tip, so I tag it with Tims proposed tag.

Tuesday, September 18, 2007

NotesFX 0.2 - Stable, but still not perfect

Since intensive string concatenation with JS crashes the JS-engine in Notes, I moved the concatenations to the formula-engine in Notes, which is more or less rock solid.






>> Download Demo Application

To do this, I use a "proxy" (between JS and Formula) field per effect, where I set x and y values with JavaScript. I then use document.forms.nameOfButton.click() to click a hidden formula-button which makes the string needed to inflate the layer/sets fieldvalue.

The problem now, is that the mouse pointer "blinks" (between hourglass and regular pointer) when the button(s) are clicked with JS. *d'oh!*

In the flash, you can't see the mouse pointer blinking as much as it does in the app.

I'll do my best to find a perfect solution, but I'm not sure the right idea will pop up. Suggestions are appreciated :)

If you're downloading/running the demoapp:
The new demo-form is called DemoAnimation_formula.

Saturday, September 15, 2007

NotesFX 0.11

If you're coming to this post directly, NotesFX 0.2, which is stable here.

Changes since 0.1 A:
One field per effect, less JS, speedier, possibility to set speed of animation.

Dropped the alpha (still in the DB-title). It's still nothing more than a proof of concept (mostly because of instability in the Notes Client).

Will still kill JS in Notes (unable to save JS-libraries/run JS in the client). Solution, KillNotes.exe.








>> Download NotesFX 0.11 Alpha

Thanks goes out to Nathan for input on the first version. I planned on having the weekend blog-free, but when people mail me about my experiments, I sometimes get a boost in motivation.

If I ever get this stable, I came up with a couple other possible uses than I have in my demo. Layer-based progress bar, horizontal and vertical bar charts (dynamic, based on field values and animated if you wanted).

Friday, September 7, 2007

Introducing NotesFX 0.1 Alpha

If you're coming to this post directly, NotesFX 0.2, which is stable here.

When I made the BlindDown demo, I thought that was the best I could do with layers in the client. Just the other day, I did a little pondering, and now I've found out how to animate size and position (sliding) in two dimensions, use/switch images from image-resources with one JS-command (and slide them in two dimensions), all contained in one(!!) subform :)

>> View demo

>> Download Demo Application

Setting borders on layers was one of the possible limitations I feared, but thanks to my good friend, "Google Search", I found this gem by Jane Griscti.

The biggest problem with the current version of NotesFX is that it seems to crash the JS-engine in the Notes Client. When this error occurs (after running 5-6 demos), you have to KillNotes.exe/start Notes again to run more demos, since Notes doesn't seem to have trash management for JS in the client.

I'm planning to look at acceleration in movement/size, and see what I can do with that in an upcoming version. Not sure when/if it is possible to have these effects without getting memory leaks (which is what I think occurs).

If you're downloading/running the demoapp:

You're going to have to kill Notes!! (the JS-engine crashes)


The demo-form is named "DemoAnimation". Open it in the Client, and click the different actions to see demos of effects.

The JS is really messy, but if I find a way to do effects, and not overload Notes, I may clean up the code, and document usage of the JS "classes".

Real-life usage (if/when stable/usable):
If someone is only saving/not closing the window, slide in message, "Document has been saved.", slide out.
Maybe small blind-up/down-effects combined with hide-whens on forms.
Popping in please-wait images before running something heavy.

Thursday, September 6, 2007

Preview of next upcoming SNTT

This app has been postponed. I'm currently more interested in seeing what I can get out of my NotesFX-project.

The app is going to be a demo of "Ticket"-based logins (you log in, you get a ticket that lasts until you log out or 24 hours).

The ticket is not the same as temporary registration. You still have to register, but instead of a NAB, I make user-documents in the application.

I recently used this approach in an application that had to go inside a frameset (actually an iframe inside a frame) from a different (not sub-) domain. Cookies and IE don't play well in that kind of environment.

I believe this approach also can be used if security isn't critical, but in those kinds of applications I'd probably use a cookie to store the ticket-id, and not pass it around in the URL, which I'm going to do in the demo.

I aim for making it browser independent (IE/FF/Opera), but will look a little worse in IE6, because of lack of PNG transparency support. Not sure if I'm going to spend time on learning to use behavior-hacks to make it look the same as newer/better browsers.


Click the image to view full size

Horrible colors, but I'm no designer/the app is not released.. :)

Tuesday, September 4, 2007

BlindDown-effect in the Notes Client using JavaScript

If you find this interesting, take a look at my "NotesFX", which has more effects.

I don't know if this could be considered a hack. It's probably not useful (at least not in this state), but still fun.

The BlindDown-effect is found in DHTML-libraries like Scriptaculous and Mootools. Scriptaculous demo-page







>>Download demo-application

To achieve this effect, I have a layer with a table inside. In the table, I have three computed fields. One of them contains the title of the "help message", one contains the message, and one is used to add line-breaks into (-> what looks like animation). The "line-break" field has a font-size of 3, so that the animation looks somewhat smoothly, but is not too slow.

I have a javascript-function that takes the delay between each line-break, number of line-breaks (iterations), message title, and title. The iteration-parameter is put into a global variable that decrements for each added line-break.

I use setTimeout to call the same function until iterations=0.

The messages is put into a javascript-object with arrays corresponding the parameters that should be passed to the "blind down" function. This to make it easier to maintain the messages.

I use Notes' (or rather JavaScript 1.3's) apply-method to apply the array to the blind-down function.

Each field's onFocus code:
blindDownMessage.apply( "", messages[this.name])

Example of the messages-object:
var messages = {
name: [ 10, 10, "Your name", "Here you fill inn your name.\n\nE.g. Donald Duck, Dillbert, etc." ]
}


If you're downloading the demo:
The demo-form is named "BlindDown". Open it in the Client to test the demo.

You could also put more advanced things in the layer.

Hide the cell-content initially. After the "animation" is finished, set the value to make the cell visible, click a refresh-button (hide it with a layer/etc), using javascript. E.g. document.forms.nameOfButton.click()

I haven't tested the above suggestion, so I don't know if it's possible to do in a usable way.