Thursday, December 20, 2007

Readable lookups - Demo application

The demo contains:


I've made a simple index-page. If you download the app/open http://domain.com/path/to/demo.nsf, you should see the links to the demos.

The demos aren't very exciting/useable. Take a look at the design elements for the "magic".

The Ajax-demo



Link "To the frontpage" is a part of the above flash-animation

>> Download demo-application

Semi MVC templating

In the templating-demo, I (believe) I've used the MVC pattern for templating/transforming lookups. If anyone knowledgeable about patterns reads this, please let me know if I'm way off on this.

I'm not sure if this is similar to the technique Chris mentioned in a somewhat cryptic comment on another templating blogpost.

I use a naming convention for three kinds of fields. To do the actual transformation, I use a WebQueryOpen-agent.

Naming convention/description


lup_[label]
Model
Computed for display, allow multiple values. These fields fetches the data (using dblookup/-column). One field can be used by multiple templates.

template_[label]_[description]
Controller
Computed for display. These fields contain the description of how the data should be transformed. The label should correspond with the label of a lookup-field. Example of template.

transformed_[label]_[description]
View
Computed for display. These fields will be filled by the result of the transformation. The label/description should correspond the label/description of a template-field.

WebQueryOpen agent
Controller
This agent contains the transformation logic/fills the transformed-fields.

Wednesday, December 19, 2007

Doing lookups on the web

Update, 20.12.07: Demo-application done.

I've finally gotten started with my templating-demo again.

One of the examples I promised was Ajaxy lookups (using the same view on the Web as in Notes). To make this happen, I thought it would be enough going to use ?OpenView&startkey=hello, but the effect of that "command" is start with documents starting with hello, then show the rest of the documents in the view.

A quick visit to my friend, google, sendt me along to an article on CodeStore.net. "Hidden" in the comments-section was my answer:

Alex Hernandez (Thu 5 May 2005 10:00) website / e-mail

Jake, I've been facing a similar problem when reading the "startkey", if the key doesn't exist, it returns the next set of documents, which is wrong. Now, if you add the "untilkey" to the url you can grab only the documents you need. This is true everytime you know how to compose the untilkey key, What I did was just adding a "_" to the end of my know startkey.

for example:
http://server/db.nsf/view?openview
&startkey=alex
&untilkey=alex_


originally If I searched for "alexh" and the key doesn't exist, it returned the rest of the docs. now with the until key, it search for docs with ket between "alexh" and "alexh_" wich returns no documents, and that's what I expected.

.::AleX::.


Thank you Alex, Jake and google!

Wednesday, December 12, 2007

HttpRequest-class

Fetching Post/Get parameters with an agent isn't easy in LotusScript with inbuilt tools.

I made this simple class today as a replacement for a get-only getParameter routine I used earlier. This class should handle both get- and post-requests.

My "documentation" (something new I've been trying out) of the class:


>> Class in a text-file
(the tabs are a little off in Notepad/Wordpad, but should look correct in DD)

>> Simple demoapp

Update, 01.07.2009: A small codechange to support multi-value requests. For instance from a form with checkboxes.

Update, 30.05.2008: There was a bug in the class. If you had a parameter containing "=", the parameter value would be cut off before =. The class is updated with the fix (demoapp not updated).

Update, 25.03.2008: I've updated the class so that it should handle request_content above 64k. Read more here.

Update, 13.12.2007: I updated the class with a method to print a html-report of the request, using the docToHTML-procedure in the previous post.

Update, 25.09.2008: Added functionality to fetch cookies from a request.

Debugging: Inspect what's being posted from a form

Sometimes I make parts of a form using PassThruHTML/hidden notes-fields. Mostly because Domino-generated HTML is hard to style (especially radio-buttons/checkboxes). This sometimes work immediately, and sometimes it takes hours to make it work. I've found it a great help to see what is being posted to the server.

There are HTTP-sniffer applications that you can inspect the request with, but I find it easier to post the form to an agent, and then print the request-content as a HTML-table.

Example of output, Jake's FakeNames database, Person-form with a couple of fields filled in.

It's also handy to "echo" get-requests when developing agents: A simple ?OpenAgent

In the demo-application for this blogpost, I've made a (javascript) action-button that rewrites the action-field in the form to point to an agent. The agent tries to print every field in NotesSession.DocumentContext.

To make this work in your applications, (hopefully,) you only have to copy the Script Library, InspectRequest, the agent inspectrequest, and the shared action, Inspect current form-values. Insert the shared action into the form you want to test.

To inspect DocumentContext in other agents, use docToHTML in the Script Library, InspectRequest. As a rare occurence, I've actually documented this function.

To get the current field-values, I trigger the equivalent of @Command( [ViewRefreshFields] ), using the Domino _doClick function.

>> Download demo application

Tuesday, December 11, 2007

IdeaJam: Let SearchView return XML, JSON

I've wanted this functionality so many times in Domino (for Ajaxy applications). Hopefully blogging about it will increase it's visibility, and get more people voting for it.

Sunday, December 9, 2007

What if....IBM collected all the best tips of the N/D "blogosphere" into a Redbook?

(Sorry for stealing your subject "pattern", Bob)

I watched a japanese TV-show some weeks ago, where they interviewed a couple of japanese bloggers that converted their blogs into book-format.

It would be great, if someone like IBM could extract all the best tips/techniques/patterns/etc. from the N/D "blogosphere" into a Redbook (along with accompanying demo-dbs) yearly.

Maybe this would help motivating even more people to spread their knowledge through blogs.

Saturday, December 8, 2007

Readable lookups: Lookups -> HTML with formula

Update, 20.12.07: Demo-application done.

To avoid making a monsterpage, I'll split up the different tricks you can do with the readable lookups technique.

First example, generating HTML from lookups with templating, in a readable way. I'll post a demo-db when I'm done making examples.

The lookup-view


First, I create a lookup-view.

First column: firstName + " " + lastName
For real-life apps, I mostly use ComputedWhenComposed-fields with @Unique as a general lookup-field.

Second column:

The advantage in writing it this way is that all you have to do to add more data is to add the field-name to the fields-list. Another advantage is that there is less chance of typing errors, as less is written. The biggest disadvantage is performance. If you have a lot of documents (maybe 20 000++), I'd think the above way of writing the column formula would be more resource-heavy on view-indexing than if you simply concatenate strings.


Formula-templating-examples:


First, take a look at my previous, less readable technique, where i had pipe-separated lookup-data.

New, "self documenting" lookup -> HTML technique:
Definition lists
A table

>> Result of the above to examples

Top 5 browsers of my visitors Jan-Jun vs Jul-Dec

Inspired by Wissel.net, here is this blog's browser statistics:



A little disappointed that my favorite browser, Opera, is less represented at the end of the year than at the beginning.

Friday, December 7, 2007

Readable lookups

Update, 20.12.07: Demo-application done.

A colleague of mine, Torbjørn Halsne, found a better/more readable way of creating readable lookups than my "Keyword/Value lookup fields", on searchdomino.

The technique.

The worst part about my K/W-technique was that it wasn't really suitable for lookups that returns more than one value.

This new (to me) technique adds overhead on view-indexes, but you get a lot in return.

I'm working on a demo to show all the great stuff you can do with this technique. E.g. using the same view for server-side lookups as client-side lookups (XML), a lot more readable formula-templating and hopefully (if it's not too time consuming/me too lazy) LS-WQO templating demo.

Hopefully I will have it ready/online before the end of the weekend.

Thursday, December 6, 2007

Google Charting API

Google has just released an interesting, easy to use charting API.

Very simple example:


img src:
http://chart.apis.google.com/chart?
cht=p3&
chd=t:42,42&
chs=250x100&
chl=Panic|Don't&
chco=83B627,669922

The following types of charts are available:
  • Line chart
  • Bar chart
  • Pie chart
  • Venn diagram
  • Scatter plot

Read more about it here

Wednesday, December 5, 2007

I love Ray Ozzie

Well, actually I don't love Ray Ozzie, but someone does.

Just spotted this t-shirt on Amazon.com:

Tuesday, December 4, 2007

Rant about the apparent lack of "serious" Notes/Domino programmers

Started of as a comment on "The reason the Domino Development Platform Isn't Taken Seriously", where Craig asks if it's time for the DominoDevs to grow up. My rant is also influenced by the comments posted on Nathan's "Sanity Check" that OO is the way to go/most N/D developers are lazy.

Start rant:
This is just a wild guess, but don't think that the majority of N/D developers are willing/able to make the effort to write "serious" code.

Then again, how many applications need the complexity that is Java/OO/Inheritance/MVC/etc?

The recent discussions on the future of N/D remind me a lot about discussions about JavaScript, which also has had the toy stigma for most of it's life.

It's not until the Ajax-term is coined by a well respected person in the community, Jesse James Garrett, that suddenly JavaScript is THE language to learn. Then Douglas Crockford steps in and show the "serious programmers" all the amazing stuff you can do in JS, and all of a sudden, all platforms must have JavaScript.

Now the "serious programmers" want to tighten JS (with EcmaScript 4), and make it more serious (or more precisely, make it fit their world..?).

JavaScript is a forgiving language, just as LS. There is a lot of awful code being written in JS, making sites/applications brittle/insecure. If you force strong typing/classical inheritance/etc. on all JavaScript-developers (as some feel that EcmaScript 4 will), how many are you going to have left? Will the tightening of the language be worth the loss? Is the proposed tightening of the N/D-platform going to be worth the loss of less advanced programmers?

I believe that what we as a community (and Notes/Domino as a platform) need, are people who love and understand what is great about the platform, and also are able to sell the concept to developers -and- IBM. Until I see the next designer, I'm firmly convinced that the decision makers of Lotus/IBM doesn't understand what is great about the platform. From looking the latest releases (6/7/8), they see it as a collaboration/mailing platform. N/D isn't great because IBM has made a great mail/calendar application with it, but maybe the mail application is so great because of the (mysterious/genious) concept that is Notes/Domino..?

Regarding lazy/untrained developers:
Great code != Great application from the users perspective, just as Horrible code != Horrible application. Great code will probably lead to a more stable/secure/maintainable/expensive application.

If you want N/D developers to write serious code, and you believe you know how to teach them, why not go together with other great minds and write a book to enlighten them? There must be publishers willing to still publish N/D books..?

With all the Agile development books coming out recently, where is "Agile application development with Notes/Domino" (horrible title, I know).

Other titles I'd like to see:
"Web 2.0 with Domino" (although I hate the term web 2.0, it sells)
"Rich Internet Applications with Domino"
"Enterprise applications with Domino 8"
"Object Orientated programming in LS for dummies"
"Taking advantage of DB2 with Domino 8"
etc. etc.

/rant

Friday, November 30, 2007

Rant about Notes 8, Lotus/IBM

Nathan writes "Where is the love".

More and more, I think, "Where is the hate?" :)

I was on the point of starting a rant in the comment section on the post, but after half an hour of writing/rewriting my frustrations with (my perceived) impression of IBM/Lotus directions, I decided to keep the rant on my blog.

Every week 5+ blogposts enter my RSS-reader about how great Notes 8 is. Almost nothing about the bugs/bad integration between the new client and Domino Designer (maybe I am the only one having problems), or how, once again, the majority (the non-java) of developers were screwed over in a release.

What mostly irritates me is that the scripting tools in Domino Designer has less functionality your average notepad-clone. You (Lotus/IBM) want me to learn Java? Then give the Designer team the resources to make tools that doesn't stink of the 1990s.

It doesn't make me happy when I think about how much resources they probably put into making 100+ different-looking icons look more or less like one (orange-ish) icon.

From my point of view, it looks like the marketing-department in Lotus/IBM has a LOT more power/resources than the developers of Notes/Domino.

Most of what's great in Notes/Domino has been there since pre version 7. Of all of the things the developer-community has asked for (in partner-/public forums), what has been implemented?

Domino Designer in Eclipse? Great!! When did you say it's going to be released?

If you want to rant about how wrong I am, please do.. I don't deny that I could be. :)

Thursday, November 29, 2007

Notes/Web JS to get text from aliased combobox/dialog list

Something I needed today. Thought i could share it with you. Easy to make, but sometimes one forgets that Notes supports JS.

>>Flash-demo
I forgot to refresh, so the alias doesn't show :)

The reason I didn't use "Refresh field on keyword change" is that it's awful on web (reloads the page).

The alias of the combobox/dialog list was the corresponding number to the text (One|1, etc).

>>Demo-application

In Notes, the code also works with hidden/computed fields. For computed fields, set value = @ThisValue.

For the web, you should probably set the text-field to editable, "HTML Attributes" -> "type=\"hidden\"".

In Notes, onChange fires when the field looses focus/value has changed. On the web, the event fires when you select another value.

JS-header - Common Javascript:

function textFromMultivalue( multival, valuefield ){
var f = document.forms[0];
/*if valuefield is the name of the value-field,
get field from form */
var valuefield = ( typeof valuefield == 'string' ) ?
f[valuefield] :
valuefield;
/*if multival is the name of the multival-field,
get field from form */
var multival = ( typeof multival == 'string' ) ?
f[multival] :
multival;

valuefield.value = multival.options[
multival.selectedIndex ].text;
}

onChange in multi-value field (also Common Javascript):
textFromMultivalue( this, 'name_of_textfield' );

Tuesday, November 27, 2007

Maintaining Strong Typing in LotusScript

Update 28.11.07: Tim Tripcony wrote a somewhat related article about using ByVal for input parameters. Recommended read!

--

Upon reading "Code Complete", I discovered that a way of using Subs I've previously thought of as bad practice, actually is quite normal.

The author writes (I apologize if I interpreted this wrong) that some developers think that functions should be used for mathematical calculations/simple operations and procedures used for more advanced stuff. In LotusScript, from his description, a Sub is a procedure and a Function is a function (duh..).

He also suggest that when you make procedures, you put input parameters (read) first , and output-parameters (modify) second, to make it easier to read the code.


One of the advantages with using subs this way is that you can always maintain strong typing. This enables you to capture coding-errors at compile-time, instead of at runtime.

In the code below:
createToyotas is a sub that has an outputparameter, List As Toyota
toyotas is a function that returns a variant containing List As Toyota


The compiler recognizes that a "Toyota" doesn't have a method "destroyWorld", whereas the variant, also a Toyota, passes through compiling.

Error caught at compile-time with strong typing, whereas the variant doesn't cause an error until runtime.

In most cases you could probably pinpoint the runtime-error quite fast, but catching it at compile-time is preferrable for me at least.

Also, the most common error would probably be a spelling-mistake, not trying to run a random thought-up method.

Performance-wise, there may also be advantages, especially if you're using inheritance/creating many objects/etc.

When debugging there doesn't seem to be much advantage using strong typing over variants.

>> Extremely simple db with some (messy)test-code (test-agent, StrongTypingAdvantage)

If you can think of other advantages, or disadvantages for that matter, please post a comment!

Tuesday, November 20, 2007

Input Validation - On Invalid, open URL

This only works on the web.

If you put something like this in Input Validation on a field:
@If( @ThisValue = "dontpanic" ;
@Failure( "[http://dontpanic82.blogspot.com/]" ) ;
@Success )

When the user writes "dontpanic" in a field with this input validation, and try to save the document, they will be redirected to my blog.

The only use-case I can think of at the top of my head is lookup-based login-forms.

..code to check username/password..
@If( authenticated ;
@Success ;
@Failure( "[/" + @WebDbName + "/login?OpenForm&error=wrong_username]" )
)

Another use case is maybe to limit too wide searches. A customer recently searched for *ia*, in a several CMS' and thought that the search didn't work (when I tested, the search took 3-4 minutes to complete).

If you can think of/have other ways to make use of this, please post it in the comments..

Monday, November 19, 2007

Timesaver - Converting several values to text

I've so far only found use for it in lookup-columns, but different people, different needs.

If want to make a pipe-separated lookup-column of values like these: subject(string), ranking(number), previous_ranking(number), posted(date), modified(date)

You can combine values of the same data-type under one @Text like this:
@Implode( subject :
@Text( ranking : previous_ranking ) :
@Text( posted : modified ) ; "|" )

Apart from a timesaver, I also find it easier on the eye, than:
@Implode( subject :
@Text( ranking ) : @Text( previous_ranking ) :
@Text( posted ) : @Text( modified ) ; "|" )

Or, even worse:
subject + "|" +
@Text( ranking ) + "|" + @Text( previous_ranking ) + "|" +
@Text( posted ) + "|" + @Text( modified )

Friday, November 16, 2007

Templating: Lookups -> HTML

Update, 08.12.07: An improved, more readable technique

Inspiration, ExtJs, through Rich Waters.

I've more or less come to the conclusion that HTML in lookup-views is evil.

On one of the apps I work with, there were two access levels, reader, and editor. I used to have a column for each access, and pull HTML from the view.

<pseudocode>
col := @If( @UserRoles = "[Editor]" ; 4 ; 5 );
@Implode( @DbLookup( "" ; "" ; "(lupView)" ; key ; col ; [FailSilent] ) ; "" )
</pseudocode>

This became a headache sometimes. I'd change something in one of the columns to go with new design-desicions. Maybe it was lunch, the end of the day, or talking to a co-worker, not exactly sure, but sometimes only one column remained altered. I'd program javascript/make css for the new HTML, and think everyting is ok. Release the update. Users got JS-errors/corrupted designs/etc.

The errors wouldn't come as far as to applications in production now, as we test thouroughly before release, but it could still be a maintenance headache.

Step one to get rid of headaches:

Moving the presentation out of the view

Get rid of HTML-views, use simple lookup-views (value1|value2|value3) instead. Example of transformation to HTML.

(missing </table>)


Step two to get rid of headaches:

Using template-strings





You're probably not going to save any time writing the transformation to HTML using templates the first time, but you'll save time in the long run. Having the template in one string instead of concatenating bits of the html-per-item on several lines also increases readability of the code.

Example of simple evolution:


Result:


>> Simple demoapp

For my access-level-problem, I either write a separate template for each access (e.g. add edit link for [Editor]),
or put @If(@UserRoles = "[Editor]" ; "" ; @Return("") ) at the top (don't do anything if not editor).

Document generator - Discworld "characters"

I've mostly used Jakes fakenames-app for demos, but I've got tired of cleaning out design-elements to make it lean bytewise.

Run java-agent generateDiscworldCharaters to make documents. Currently set to 500 documents. No uniqueness check.

I use four arrays, first-name/title, last-name/"the Something", guild/company, address/country. Math.random() and a loop to create documents.

>> Discworld name generator

Saturday, November 10, 2007

Book recommendation, Code Complete

Just started reading this book:


Book description

Take a strategic approach to software construction and produce superior products with this fully updated edition of Steve McConnells critically praised and award-winning guide to software development best practices.


It deals with software development/project-management issues that I've seen at my place of work. A very interesting read.


Code Complete at Amazon
Code Complete at O'Reilly Safari (where I'm reading it)

Saturday, November 3, 2007

Templating using a NotesForm

Coding templates in an agent leads to very verbose agents/a maintenance nightmare (in my opinion). Coding them in a NotesDocument makes it hard to use them in a Design-template context.

This technique has little verbosity beyond the actual code to generate HTML, and works great in a Design Template-context. It supports modularity, as each field is a potential module. Since the templates are forms, the dynamic "modules" also work in stored documents.

You can combine Java (through LS2J), formula (through Evaluate) and LS , just like you can when you're using agents for templating web-pages. If you're a Java-guy, then you probably would need a little more code (testing field names with Regular Expression) to achieve the same.

Combine this technique with @UrlQueryString in Form Formula if you want the ability to present data in several content-types.

The technique relies on Execute, a WQO-agent and field-names following a pattern. I chose Execute because it's faster than making a testing-routine for patterns. If you're planning on putting this technique to use in real applications, I suggest you use Regular Expressions, Instr, etc. to look for valid field-names, as Execute is horrible to debug.

The demo


I have one pattern that I look for in a field-name, "wqo_actonfield_". The third token (separator = "_") is the method to be executed. The rest of the tokens are ignored to allow several fields executing the same function.

You could also have the rest of the tokens being parameters for the method being executed.

For instance field named "wqo_search_google_dontpanic" -> run a function that inserts HTML with search-results into the field.

>> Demo Application.

Screenshots


WQO-agent for demo:


Form, one demoBody-field:


Result, web


Form, six demoBody-fields:


Result, web (click for full size)



As always, comments are appreciated :)

Monday, October 29, 2007

Generating HTML on WQO with Formula-agents

If you mark a RT-field Pass-Thru HTML (add a space before and after, Text -> Pass-Thru HTML), you can fill it with HTML through formula.



You are limited to ~64k of data per field, and you will have to experiment a little with the generation of the HTML-string (using an array/implode), as Domino tends to give this error-message: HTTP Web Server: Lotus Notes Exception - The formula has exceeded the maximum allowable memory usage.

The advantage with WQO compared to setting the HTML in "Default value"/using a computed text is that you can move the code out of the form (I prefer as little as possible code in the form). You can run multiple agents on WQO if you have several RT-fields you want to fill (menu, maincontent, etc).

When generating HTML, I've been using more and more Evaluates inside LS-WQO-agents. In some cases, where the generated html is less than 64k, formula-agents may be the best "tool".

>> Demo-db with different formula-agents running by @UrlQueryString/a form with two formula WQO-agents.

Monday, October 22, 2007

onBeforeUnload - confirm moving away from page

After reading Patricks post about onbeforeunload, I implemented the event in the CMS I mostly work with at Compendia.

His code required Prototype. I've made a little demo that doesn't require a JS-lib. I use two event-listeners ( onClick/onKeypress ) on document, and "set the page dirty" when the event "indicates" that a form element has gotten its value changed (look at the sourcecode of the page).

The only weakness I've found so far is that IE6 doesn't register keypress on comboboxes -> user could change a combobox-value with the keyboard and move away from the form unsaved without getting a warning.

>> Demo-page

I think onBeforeUnload is supported i FF2.x, IE 4+, and newer versions of Safari.

Mozilla has a simpler demo, where one field is tested.

Patrick has a downloadable Demo-NSF.

Wednesday, October 17, 2007

Keyword/Value lookup fields

I sometimes get a bit frustrated by the lack of associative arrays in the formula language (more specifically in keyword lookup-fields). I’ve thought about ways to emulate this a couple of times, but never got anywhere.

Today, I’ve come up with one way. It’s not beautiful, and one can argue that it’s bad practice.

lupField-syntax:
standard @DbLookup( .. )

Column Value-syntax:
"Error message - keyword-list not found" :
"keyword" : value
"apple" : banana;

Syntax for getting value:
lupField[ @Member( "keyword" ; lupField ) + 1 ]

Error handling
  • Error message on "keyword value not found" maintained on a column basis.
  • First item in the Column Value list should be the error message. Reason being @Member( "itemNotInList" ) = 0 (+1=1)
  • "keyword-list not found" maintained in the lookup-field.

"Advantages":
  • Put keyword/value wherever you want in the column formula
    • Great for design-templates if you want to have configuration-settings in a logical order in the lookup-column (reorder them whenever)
  • Print all settings through a simple @For-loop
  • Readable
    • @Word( settings ; "|" ; 24 )
    • versus
    • settings[ @Member( "stylesheet" ; settings) + 1 ]
  • ..?

Disadvantages:
  • Unreadable syntax for people not aware of the technique
  • ..?

>> Ugly flash-demo
>> Ugly demo-application

If any of you have an even simpler syntax/better concept, please let me know!

Monday, October 15, 2007

String to char-array in Java

For those curious Java versus LS. It's lightning fast:

String to char[]: 15ms, string length, 700 000

Added to the string concatenation "benchmark"

String concatenation with a Java agent

Here are the results from string concatenation in Java:
Standard concatenation (75 000 concatenations): 192.594s, string length, 300 000.

java.lang.StringBuffer-concatenation (75 000 concatenations): 0.062s, string length, 300 000.

RTI-concatenation (75000 concatenations): 1.765s, string length, 300 000.

The best in LS, 75 000 concatenations, 0.25s. The numbers aren't directly comparable, as I'm running on different hardware than Julian.

By "Standard" concatenation, I mean bigString += string. Standard concatenation and RT-concatenation is slower in Java than in LS. Using Java's native StringBuffer class, is a lot faster than any of the competitors in LS (so far).

I'm not surprised that Java's StringBuffer beats Julian's array-based StringBuffer-class in LS, as Sun probably puts a lot more effort into optimization of the language than IBM does with LS.

My test ran on Notes 7.02, which I think runs version 1.4 of the Java runtime. Newer versions of Java may be even faster.

My Java-skills are mediocre at best. Let me know if my test-methodology is wrong in any way.

>> Code for the test

Sunday, October 14, 2007

NotesSession.SavedData - great for WQO-agents?

Update 3:
I got a little more useful information from Fabian today:
For whatever reason, even with the $PublicAccess item set to "1", Anonymous still needs at least reader or depositor access to actually write to the agent data note. I usually prefer to restrict public readers to No access with the privilege to read and write public documents, whenever possible (there is another oddity with shared fields, who don't have a GUI widget to enable public access). Reading item values works OK that way, but writing not.

Obviously (at least in certain releases) Domino doesn't do a perfect job deleting agent data notes when agents are deleted. IBM has a little tool to purge orphaned agent data notes, but TeamStudio has a better (and free) utility.


Update 2:
I got this by e-mail from a friendly German, named Fabian Brock.

If the WQO agent is not run as web user, there should be no problem whatsoever. If the signer of the agent has appropriate rights to the database, he/she can modify the agent data note just like any other document. However, anonymous users will generally not have the right to create or even edit documents.

The solution is the same as with conventional docs: Have your WQO agent add a $PublicAccess item set to "1", if it doesn't exist yet. Now, Anonymous is only required to have NoAccess plus the privileges to read and create public documents. Major drawback here, since SavedData is not available for public access yet, Anonymous will never be able to add this item. The agent must have been run once by a user with sufficient access rights to make the modification.

And because the agent data note is recreated every time you make a modification to your agent, this might turn out to be hard to handle. One more reason to put as much of your code as possible into libraries.


Update:
It seems (from a little testing) SavedData is not available for web-agents, which is a shame. I tested this in both Java and LotusScript, and got the same results. Next best solution, use a profile-document.

>> Demo-code (java agent), profile document

--

When I first read through Julians benchmark of different ways of String concatenation, I only skimmed through to the nice graph.

I read it again, and saw that he mentioned NotesSession.SavedData which I never heard of. Looked it up in the help, and got an idea (good? you decide). Wouldn't this be a great container for HTML created by WQO?

WQO that prints data from documents (e.g. menus/reports):
Create a lookup-view with a column containing @Modified.

When you run the agent,
Join(Evaluate(@Text(@DbColumn( "" : "nocache" ; ... )))) -> a string of all the dates. Compare this to a stored field in SavedData containing the @Modified from the previous run.

If the same/SavedData has "date-field", replace RT-field in DocumentContext with the saved field in SavedData, else generate HTML/store in SavedData.

Redim performance

Out of curiosity/"challenge" from Julian, I did a little test of ReDim performance.

I'm not that familiar with redimming arrays, so someone let me know if this way of testing is bad.

Result:

Code:


Percentwise, the difference is humongous, but in seconds, not that big of a difference.

>> Code as text

Friday, October 12, 2007

Simple header-generator in Java

Not sure if header-generator is the best word for it.

Fonts aren't platform independent. An image of a font is.

Just a little experiment with Graphics2D in Java. JavaAgent running on WebQuerySave generates an image (and embeds it in the document) from header-text, selected font/-size. Also adds a shadow if desirable.

Width is currently hard-coded (400px), so the biggest fonts, with font-size set to 96 won't fit.

Fonts are @FontList. Since this is not available on web, I made a keyword-document containing the text-list of fonts. Not all types of fonts are supported, it seems.

I use a Java WQO-agent that sets a multivalue field containing all font names in GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts(). This way, you should get the fonts available on your machine/server.

Header-images are saved as png in the systems tempfolder.

>> DemoDB

Examples:



LotusScript and Java agents on WQO

If you ever were wondering, yes, you can run both a JavaAgent and a NotesAgent on WQO. Write to the same RichTextItem/etc.

WQO in form:


The LS-agent:


The Java-agent:

(body.update() not needed.)

Result:

Thursday, October 11, 2007

Thinking outside the box, String Concatenation

If you're looking for the ultimate concatenation-tool:
Using Julians StringBuffer: 0.25s
Using NotesStream: 0.6s
Using NotesRichTextItem: 1.6s

==

A little while ago, I posted that NotesRichTextItem.AppendText is FAST.

On the train today home from work, I got an idea... Why not use a temporary NotesRichTextItem for string concatenation.

With 100.000 string concatenations, string = string + fourLetterString took about 140 seconds.

With NRTI.AT, it was finished in 1.6 seconds.. Read that again.. 100.000 concatenations in 1.6 seconds!

Simply do NRTI.GetUnformattedText to extract the concatenated string after you're done.

Code:

Result:


>> Download code

Wednesday, October 10, 2007

Ballmer Peak

Got this from a colleague today..

Scientific proof that programming and beer mix well.. :)

Tuesday, October 9, 2007

@Transform as Forall-loop

I didn't think this fit into the "Complex transforms".

To use @Transform as Forall loop, simply add a @Do inside @Transform, do whatever you want with the items, and have a return-value for @Transform (@Nothing,"", etc).

Example:

list := "Tommy|Valand" : "Ford|Prefect";

@Transform( list ; "item" ; @Do(
    html := html : "<tr><td>" :
    @Word( item ; "|" ; 1 ) : "</td><td>" :
    @Word( item ; "|" ; 2 ) : "</td></tr>";

    item
    )
);
@Implode( "<table>" : html : "</table>" ; "" )


The above example isn't good, as using @For actually results in less code:

list := "Tommy|Valand" : "Ford|Prefect";

@For( i := 1 ; i <= @Elements(list) ; i := i + 1;
    html := html : "<tr><td>" :
    @Word( list[i] ; "|" ; 1 ) : "</td><td>" :
    @Word( list[i] ; "|" ; 2 ) : "</td></tr>";
);
@Implode( "<table>" : html : "</table>" ; "" )


When concatenating large strings, you'll get better performance connecting the "snippets" in a list ( list := list : item ), than using regular string-concatenation ( string := string + item )

Complex transforms

I think there is a assumption amongst Notes/Domino developers that you can only have @If inside @Transform (I blame the documentation). This is wrong.

If you put a @Do inside the @Transform, you can have "as many lines as you want". Variables created inside the @Transform is reachable from outside the transform.

To document this, I've made an ugly example :)

Result:


Code:


>> Download formula

Monday, October 8, 2007

One Liners in LotusScript

Update, 15.10.07
Discovered that Run/RunOnServer also is "chainable"

Call s.CurrentDatabase.GetAgent( "TestWQO1" ).Run()
Call s.CurrentDatabase.GetAgent( "TestWQO1" ).RunOnServer()

Call s.GetDatabase( "server", "path" ).GetAgent( "TestWQO1" ).Run()
Call s.GetDatabase( "server", "path" ).GetAgent( "TestWQO1" ).RunOnServer()

==

Actually two lines, since you have to have a NotesSession-variable.
Dim s As New NotesSession

Get document:
s.CurrentDatabase.GetView( "viewname" )._
GetDocumentByKey( "key" )

You'd normally want to assign this to a variable -> more lines..

Based on the above, I thought you could do:
s.GetDatabase( "server", "path" ).GetView( "viewname" )._
GetDocumentByKey( "key" )

But it seems that a NotesDocument needs it parent database in memory. CurrentDatabase is a property of NotesSession.

Empty a view:
s.CurrentDatabase.GetView( "viewname" )._
AllEntries.RemoveAll( True )

s.GetDatabase( "server", "path" ).GetView( "viewname" )._
AllEntries.RemoveAll( True )

Empty a db:
s.CurrentDatabase.AllDocuments.RemoveAll( True )
s.GetDatabase( "server", "path" ).RemoveAll( True )

Delete documents based on form/etc:
s.CurrentDatabase.Search( |Form="SomeForm"| )._
RemoveAll( True )

s.GetDatabase( "server", "path" )._
Search( |Form="SomeForm"| ).RemoveAll( True )

AllDocuments and Search returns NotesDocumentCollection, so you could also use StampAll, etc.

Thursday, October 4, 2007

@UrlQueryString in Form Formula

I briefly mentioned in my "PHP-like templating in Domino" post, but I think it deserves it's own little tip-post.

You can use @UrlQueryString in form-formula of a view, to decide which form to open a document in.

I may remember wrong, but I think you can do comparison like @UrlQueryString = "print" for "stand-alone" parameters, since @UrlQueryString without a parameter returns a text-list of all request-parameters.

Example of form formula in view, "myview":
@If(
@UrlQueryString = "print" ; "print";
@UrlQueryString = "json" ; "json" ;
@UrlQueryString = "xml" ; "xml" ;
form
)

http://mysite.com/index.nsf/myview/blogpost?OpenDocument
^document is opened in default form

http://mysite.com/index.nsf/myview/blogpost?OpenDocument&print
^form that is printer-friendly

http://mysite.com/index.nsf/myview/blogpost?OpenDocument&json
^form that makes json of data in document

http://mysite.com/index.nsf/myview/blogpost?OpenDocument&xml
^form that makes xml of data in document

These are not Domino-parameters. You have to make the forms yourself.. :)

Wednesday, October 3, 2007

Click-to-sort "view" in one column

In this SNTT, I try to demonstrate some of the power of @Sort and Evaluate.

The demo I've made transforms a column containing pipe-separated values to a HTML table with click-to sort (Ascending/Descending) column headers, using a form with a WebQueryOpen-agent.

>> Flash demo

In the flash-demo, performance may seem a little low. The demo is running locally. I tested the form+agent in another application on a Domino-server, to see if it was as general as I wanted, and there, performance was a lot better. It took me about a minute or two to make a click-to-sort view in the other application, which I'm quite happy about.

Domino has click-to-sort columns in views, but I've never tested it on the web. The strength of this way of doing it is that you have the power over the HTML.

There are 20k+ documents in the demo-application, but the agent that generates the click-to-sort tables can only handle 100-1000 rows of data. I couldn't find a corrolation between the amount of data, and max number of rows. I tested a two-column table and a four column table, and I got more rows out of the four column one.

At the top of the MakeSortableTable-agent, there is a parameter (maxrows) that controls the max amount of rows to show in the view.

--

>> Demo Application

If you want to add this functionality to an application of yours, simply copy the form and agent in the demo into your application, make the "lookupview", in the same way I made my People-view. Column title should contain the table header titles (separated by "|") for the columns, column value should contain the column values for the table (also separated by "|")

Column1 in the People-view


Column2

Tuesday, October 2, 2007

NotesRichTextItem.AppendText is FAST

Did a little benchmark today.

Code:

Dim startTime As Single, i As Integer,_
streng As String
startTime = Timer()

For i = 1 To 20000
streng = streng + "1234"
Next

Call htmlbod.write( streng )
Call htmlbod.write( |<h1>Concatenation: | +_
Cstr( Round(Timer() - startTime, 4) ) + |</h1>| )

startTime = Timer()

For i = 1 To 20000
Call htmlbod.write( "1234" )
Next

Call htmlbod.write( |<h1>AppendText: | +_
Cstr( Round(Timer() - startTime, 4) ) + |</h1>| )


Result
Concatenation: 1,7188
AppendText: ,1719

The bigger the string-pieces, the more difference. Not a real-life benchmark, but at least it indicates that AppendText is lightning fast.

Simple LS2J Regular Expression class

A simple Regular Expression class. You may need to remove the line-breaks I've inserted to make it fit the width of the blog.

/*
Usage:
Uselsx "*javacon"
Use "whateverYouCallThisLS2J-class"
--
Dim js As JAVASESSION
Dim regexp As JAVACLASS

Set js = New JAVASESSION
Set regexp = js.GetClass("RegExp")

Print regexp.test("http://www.compendia.no",
".*compendia.*") -> True
Print regexp.split("test","e") -> "t,st"
Print regexp.replace("test pest hest",
"t\\w"," ", true) -> "tes pes hes"
*/

public class RegExp {
//test a string
public static boolean test(String streng, String expr){
return streng.matches(expr);
}
//returns a comma separated string
public static String split(String streng, String expr){
String temp[] = streng.split(expr);
if(temp.length == 0) return "";

String res = temp[0];
for(int i=1; i < temp.length; i++){
if(temp[i].length()==0) continue;
res += "," + temp[i];
}
return res;
}
//replace parts of a string
public static String replace(String streng,
String from, String to, boolean all){
return all ?
streng.replaceAll(from, to) :
streng.replaceFirst(from, to);
}

}

Monday, October 1, 2007

Rewriting the renderer to Java

Continuing my templating-experiment in Domino.

I'm doing this for two reasons, to brush up on my Java-skills (or lack thereof), and to make the templating syntax simpler (using the power of Regular Expressions).

Evaluating formulas are possible.

LotusScripts blocks not possible directly. You could write LS-statements to a field in a document, run a LS-agent that Executes the string, and prints the result in the same field. Open the document again in Java, and fill in the result, but I think that would be very hard to debug.

I hope to use this syntax (please give input if and why this is bad):
%moduleName% <- a module
$fieldName <- reference a field, wherever in the template
<@ .. @> <- formula block

Formula-blocks makes it possible to do stuff like:
<html>
<head><title>UFO Sightings</title></head>
<body>
<@
    @If( @UserRoles = "[Editor]" ; "" ; @Return("<h1 style=\"color:red\">Access denied</h1>") );

    lup := @DbLookup( "" ; "" ; "(lupTopSecret)" ; "ufo" );

    "<h1>UFO Sightings</h1><ul>" +
    @Implode( "<li>" + lup + "</li>" ; "" ) + "</ul>"
@>
</body>
</html>

This would be a standalone web-page, not a template referencing data from a stored document.

The above example is of course possible to do with a Form or a Page.

I'll see how long my interest in this experiment last. Hopefully I can make a demo that is hard to do with existing technology.

For instance,
..
<div id="nav">%menu%</div>
<div id="content">
<xmlTransform src="http://webpage.com/view?ReadViewEntries"
    xslt="http://webpage.com/fancyTable.xsl" />

</div>
..

Simplifying Evaluate

If you're only interested in getting a single value from Evaluate, you don't need to Dim a Variant to containt the Array Evaluate (almost) always returns.

Simply implode the evaluate, and you can assign the Evaluate directly.

Dim commonUsername As String
commonUsername = Implode( Evaluate( |@Name( [CN] ; @UserName )| ) )

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.

Wednesday, August 29, 2007

Ajax-like live-search in the client

This one was harder than I initially had hoped, and it is still just a proof of concept at best.






>> Download Demo Application

I built this demo on top of Jake's Fake Names application. The "autocomplete" performs a DbLookup based on the value of the field the user types in. Then a HTML (Pass Through) table with links is created from the result. Since the Notes Client is sooo dynamic when it comes to HTML, I just have to restart the universe reload the frameset to refresh the generated table of results.

I couldn't get this to work in a useful manner when not in a frameset, because I can't figure out how to set the pointer to the end of the query-field when not in a frameset. This results in the pointer always going at the start of the query-field when "reloading" the form.

E.g.:
User types: abb
Field value: abb
Refresh/lookup
User types: ie
Field value: ieabb

If you're downloading the demo:
All elements modified/created for the demo contains "DontPanic Demo" in the comments-column.

The first lookup may take some time, as the view-index has to be created in the lookup-view.

If you want to try out fixing the autocomplete when not in a frameset, I've made a non-frameset form, "AutoComplete". If you find a way to set the pointer at the end of the query-field, please let me know how, and I will fix the demo-application.

I spent WAY(!) more time than planned to make this demo (work), so please don't hate me for not cleaning up unused code, documenting code and design elements, etc.

The file-size of the demo-application is 6.66 MB according to Windows Explorer, so beware modem users and superstitious people!

Update: I originally called this post "Ajax-like Autocomplete in the client", but the functionality is most like live-search. Sorry about that.

Tuesday, August 28, 2007

Highlighting fields on "Entering" with CSS in the Notes Client

Since I have vacation now, I have more time to try out different stuff i've been wondering about.

This is one of those things.






>> Download Demo Application

For some months now, I've wanted to look at possibilities of dynamic usage of CSS in the Notes Client. It is somewhat possible, but you have to close/reopen the document you're working on (+mess with SaveOptions), and you need a subform/stylesheet per state.

Unless someone finds out how to set the id of an element dynamically, this example, with one state per field will probably require too many design-elements to be put into a real-life system.

What I have to do for this demo, as previously stated, is to have one CSS-file/subform per field (since each field-focus is a different state) in the form. This because I only want to highlight the field when it has focus. To do this without a dynamic parent class or id, seems impossible.

Inline CSS for Notes WYSIWYG tables doesn't work. Putting a Pass-Through-HTML element around Notes WYSIWYG elemements doesn't work. With WYSIWYG, I mean Create->Field/Table/Button/etc.

It seems Notes first renders one, then the other. E.g. first render P-T-HTML, then WYSIWYG. This makes it impossible to wrap WYSIWYG elements with other elements with dynamic id's and classes.

My example:


The name-field has focus -> load css for field1 (computed subform containing resource=Environment( "fieldName" ).

Every CSS-file has this structure:
#nameOfField{
/* where #nameOfField is a TD that contains the input element */
background-color: #ffd;
/*
Strange that Notes has different shorthand order than the W3C spec.
Notes, border: color style width;
W3C, border: width style color;
*/
border: #555 solid 1px;
}

What I want:


field1 gets focus. Set environment variable. Containing element of a part of the form has computed "class=\"" + @Environment("name") + "\"".

In form:
<div class=<computed value>>
##WYSIWYG BLOCK##
</div>

In CSS:

.field1 #field1 {/* CSS for td containing field1, when it has focus */}
.field2 #field2 {/* CSS for td containing field2, when it has focus */}


With a dynamically named parent element, I should be able to put all CSS into one file.

If any of you find out more elegant ways of doing this particular functionality, please let me know.

Good CSS-in-Notes resource I stumbled upon (german):
Description of possibilities/limitations of CSS in the Notes-Client

Partially translated by Google (I don't speak german)

Sunday, August 12, 2007

Sexifying the client with DHTML

I don't know why, but I've been really motivated program/test out different ideas this weekend. This is another product of my brainstorming.






Full size

I tried putting this video up on Google Video, but it got so compressed that it was impossible to see what was going on. This means that the front page currently is about 3-5MB heavy.. Sorry..

As I've stated a couple of times before, I'm not an expert programmer, and I'm quite lazy. Because of that, my demos are never production ready (stability, speed, etc). This demo is FAR from production ready. It is a proof of concept.

What I do in the demo is passing javascript commands ( e.g. javascript:doStuff() ) to a web-document inside a "Microsoft Web Browser" control. The commands are coded per field, "entering".

I use mootools' Fx.Scroll class to get the smooth scrolling.

Current issues with the OLE object:

  • I get OLE Automation Error when I try to resize the control (works with the debugger on)

  • Passing browser.navigate( url ), where browser is the browser object variable, is slow when not running on localhost (I only tried on one external server)

Maybe there is a faster way/more efficient object (mozilla, webkit, etc) to use.

To run the demo yourself (only tested in Windows XP):

  1. Download the Demo-db zip file

  2. Extract the db onto your hard-drive, and copy it to where you want to run it from

  3. Alter the postopen (in the member form) url to fit with the location you put the db. Make sure the HTTP-task is running

  4. Open the member form in the Client


Let me know if it doesn't work..

Fun stuff with the FieldListener class

BIIG flash.. Sorry, but don't know how to get it smaller.







In the demo above, I have a form with a "Microsoft Web Browser" object. If the "search" field contains the words wiki, amazon or amazonuk, I use the searchengine at the site to search for the rest of the parameters.

The search-sub:

Sub search
Dim ws As New NotesUIWorkspace
Dim arrAction As Variant
Dim sstr As String, baseURL As String

arrAction = Split(ws.CurrentDocument.FieldGetText( "search" ), " ")


Dim browser As Variant
Set browser = ws.CurrentDocument.GetObject("Microsoft Web Browser")
If browser.width <> 950 Then
browser.width = 950
browser.height = 700
End If

sstr = Replace( Implode(arrAction, "+"), arrAction(0)+"+", "")
Select Case Lcase( arrAction(0) )
Case "wiki"
baseURL = "http://en.wikipedia.org/wiki/Special:Search?search="

Case "amazonuk"
baseURL = "http://www.amazon.co.uk/s/026-5318277-1707602?&field-keywords="

Case "amazon"
baseURL = "http://www.amazon.com/s/026-5318277-1707602?&field-keywords="

Case Else
baseURL = arrAction(0)
sstr = ""
End Select

Call browser.navigate( baseurl + sstr )
End Sub

Saturday, August 11, 2007

FieldListener class for NotesUI - search on enter, etc.

To redeem myself from my previous SNTT post, I did a little brainstorming, to find something worthy for SNTT. I hope this one is..

A while back Chris Blatnick posted a method to listen for 'Enter', and search based on JavaScript in the Notes client.

Lotus did a bad implementation of setInterval in the Notes client (several actions by the user can cause the interval to run "forever" -> high CPU usage). Because of that, the method is probably not an option for most people.

The FieldListener class I made for this demo, uses the NotesTimer, and On Event Alarm to do the same. Since you're working with LS, you can add whatever functionality you want in response to something the user writes.

The downside with NotesTimer is that you can only specify whole seconds (Integer) as the intervals. Worst-case scenario, there is a delay of one second between a phrase being typed and the action execution.

Demo (the button calls the same method as the fieldListener, onEnter):






To implement the FieldListener-object on a form:
Globals in the form:

Option Public
Use "FieldListener"


Declarations:

Dim fieldListenerObject As FieldListener


In the QueryOpen event:

Sub Queryopen(Source As Notesuidocument, Mode As Integer,_
Isnewdoc As Variant, Continue As Variant)
Dim interval As Integer
interval = 1
Set fieldListenerObject = New FieldListener( Source, "fieldName",_
"substring(Chr(10) without quotes=enter)", interval, "actionToExecute" )
End Sub


I'm running two paralell "listeners" on one field in the demo, one for "johnny", and one for Chr(10) (Enter).

Download the application to see the implementation of the demo. Johnny can speak, but my screen capture software (free) cannot listen (there is a setting for it, but it doesn't work on my machine).

Friday, August 10, 2007

Strange bug(?) - Pass Me as a parameter works...sometimes

When I try to pass Me (As Variant) into a method that takes one parameter (getHåndbok in the pic), it works, when I try to pass Me into a sub that takes three parameters(getUtsnitt in the pic), this happens:
Error

I have to create a variant and assign Me to it to make it work (replace Me with obj). Bug?

The Subs being called:



Thursday, August 9, 2007

Updating the posts containing code-snippets

I'll be going through the blog, and use Joe Litton's online "Format Your LotusScript" (which is made from Julian Robichaux' ls2html) to convert the LS code snippets from flat text to HTML with the "correct" colors.

I've thought about it for some time, but never got around to it. A colleague of mine pointed out the bad readability (real word?) of the code (to which I responded that he should go get his eyes checked).

Guess it's about time.

Wednesday, August 8, 2007

Dynamic LS-methods - Allow fluid number of/types of parameters

The easier / better way, use Lists... :)

Thanks Dwight and Sean.


My example is quite simple, but with some data-type testing/object testing and decent error-handling, I think you could expand this "idea" into more advanced Subs/Functions.

Instead of setting the method to accept a fixed amount of parameters, set it to accept one object. In my example, I use a Class that allows you to add different objects (through a variant-array).

There is (at least) one weakness with this approach. You can't add arrays to the Argument-object, as (from what I've read) an array can't contain another array. If it's a string array, you could implode the array, add a symbol at the front of the string to identify it as an array, and split it in the receiving function.

Screenshots of test-agent:
Dim s As New NotesSession
Dim args As New Arguments

Dim doc As NotesDocument
Set doc = s.CurrentDatabase.CreateDocument
doc.subject = "Some mostly harmless planet"

Call args.add( "Tommy Valand" )
Call argumentsAsObject( args )


Call args.add( doc )
Call argumentsAsObject( args )


Call args.add( "Always pack your towel!" )
Call argumentsAsObject( args )


The Arguments-class
The argumentsAsObject-sub
The test-agent
Code in a text-file

The Arguments class:

'argsdemo scriptlib
Class Arguments
Private counter As Integer
Private args() As Variant

Public Sub add( var As Variant)
Redim Preserve args( counter )
If Isobject ( var ) Then
Set args( counter ) = var
Else
args( counter ) = var
End If

counter = counter + 1
End Sub

Public Function getNth( nth As Integer ) As Variant

If Isobject ( args(nth) ) Then
Set getNth = args(nth)
Else
getNth = args(nth)
End If
End Function

Public Function getAll As Variant
getAll = args
End Function

Public Function length As Integer
length = Ubound( args ) + 1
End Function
End Class


Sub that take one parameter (As Arguments):

'accept an Arguments-object
Sub argumentsAsObject( args As Arguments )
Select Case args.length
Case 1
Msgbox "Name: " + args.getNth(0)

Case 2
Msgbox "Name: " + args.getNth(0) + Chr(13) +_
"Address: " + args.getNth(1).subject(0)

Case Else
Dim strTemp As String
Dim counter As Integer
Forall item In args.getAll
'35 - Product object (I know it's a NotesDocument)
If Datatype( item ) = 35 Then
strTemp = strTemp + "Item at index " +_
Cstr( counter ) + ": doc[" + item.subject(0) + "]" + Chr(13)
Else
strTemp = strTemp + "Item at index " +_
Cstr( counter ) + ": " + item + Chr(13)
End If

counter = counter + 1
End Forall
Msgbox strTemp
End Select
End Sub


Full code for the test-agent:

'options
Option Public
Option Declare
Use "argsdemo"

Sub Initialize
Dim s As New NotesSession
Dim args As New Arguments

Dim doc As NotesDocument
Set doc = s.CurrentDatabase.CreateDocument
doc.subject = "Some mostly harmless planet"

Call args.add( "Tommy Valand" )
Call argumentsAsObject( args )

Call args.add( doc )
Call argumentsAsObject( args )

Call args.add( "Always pack your towel!" )
Call argumentsAsObject( args )
End Sub


If you have an easier way to allow this kind of flexibility please let me know.