I finally took some time in between gaming (and having a life ;-) to write a technical blogpost. It's longer then I initially anticipated, but I hope you like it. Yes I know it's not a Thursday, but I started it that day :-)
Wouldn't it be great if you could 'generate' something like this in a Notes document:
I hear you thinking: That's easy.
Yes, you can do something like this in a view, but here's the twist: the above is dynamically generated inside a rich text field on a document using HTML and CSS.
Below is the outline of the work involved, step by step. If you want to dive into the code and play with it, you'll find a link to the final app at the bottom of this post.
1) Building the UI
2)
Adding, Editing an Deleting a Task Just some basic scripting to make the UI work.
(below is the important part of the subs; declarations and initialization is not shown):
- Getting the input from the user to add a Task
Set rdoc=db.createdocument
If ws.Dialogbox("Dlg Task",True,True,False,False,False,False,"Add Task",rdoc) Then
If rdoc.Date1(0)<>"" Then
rdoc.Form="Task"
Call rdoc.MakeResponse(doc)
Call rdoc.save(False,False)
GenerateTasks uidoc
Call uidoc.Save
End If
End If
This sub asks the user to enter a new task using the subform in a dialogbox. It then creates the document, en makes it a response to the current project document. Finally 'GenerateTasks' is called which does the update of the html table. - Editing is done like this:
Set dc=ws.PickListCollection(PickList_Custom, False, db.server, db.FilePath,_
"SubTasks", "Edit existing Task", "Select the task to be edited:", doc.UniversalID)
If dc.Count=0 Then Exit Sub
Set rdoc=dc.GetFirstDocument
If ws.Dialogbox("Dlg Task",True,True,False,False,False,False,"Edit Task",rdoc) Then
If rdoc.Date1(0)<>"" Then
Call rdoc.MakeResponse(doc)
Call rdoc.save(False,False)
GenerateTasks uidoc
Call uidoc.Save
End If
End If
Here the users picks the tasks to be edited from a picklist. The selected task is shown in the same (subform-)dialogbox. when the user clicks OK, the response doc is updated, and the Tasks are regenerated. - Finally the 'Delete' button:
Set dc=ws.PickListCollection(PickList_Custom, False, db.server, db.FilePath,_
"SubTasks", "Delete existing Task", "Select the task to be DELETED:", doc.UniversalID)
If dc.Count=0 Then Exit Sub
Set rdoc=dc.GetFirstDocument
Call rdoc.Remove(True)
GenerateTasks uidoc
Call uidoc.Save
About the same here, except that the response doc is simply removed, before refreshing the table.
3)
Generating and importing the HTML This script has been broken up in 3 parts, to make if more readable.
- The first one is the GenerateTasks, called by all 3 buttons, to refresh the HTML:
Sub GenerateTasks (source As NotesUIDocument)
Dim s As New notessession
Dim view As NotesView
Dim thefile As String
Set view=s.CurrentDatabase.GetView("(SubTasks)")
thefile="c:\temp\temp_table.htm"
WriteHTML thefile, source.document.UniversalID, view
Call source.GotoField("Tasks")
Call source.SelectAll
Call source.Import("HTML File",thefile)
Kill TheFile
End Sub
This sub makes sure the file is ready (we use a fixed location here, but in real life this could be a profile setting). After the HTML file is generated it is imported (overwriting the current content) in the Tasks rich text field. Afterwards the HTML file is deleted, as we don't need it anymore, and don't want to leave traces of temp files. - The second sub actually generates the HTML file, used in the previous sub:
Note: as I had trouble formatting some < and > I replaced them with « and ».Sub WriteHTML (TheFile As String, Key As String, TheView As Notesview)
Dim fileNum As Integer
Dim tdoc As NotesDocument
Dim i As Integer
Dim ym_min As Variant
Dim ym_max As Variant
Dim ym_end As Variant
Dim ym As Variant
Dim NrMonths As Integer
fileNum = Freefile()
Open TheFile For Output As fileNum
Print #fileNum, |«html»«body»|
Set tdoc=theview.getdocumentbykey(Key,True)
If Not (tdoc Is Nothing) Then
WriteHeader fileNum
ym_min=tdoc.Date1(0)
ym_max=ym_min
While Not (tdoc Is Nothing)
If tdoc.Date2(0) > ym_max Then ym_max=tdoc.Date2(0)
Set tdoc=theview.GetNextDocument(tdoc)
If Not (tdoc Is Nothing) Then If tdoc.columnvalues(0)<>Key Then Set tdoc=Nothing
Wend
NrMonths=Int((ym_max-ym_min)/30) + 2
For i = 1 To NrMonths
Print #fileNum, |«TD class=title»| & Format(Dateserial(Year(ym_min),Month(ym_min)+i-1,1),"mmm yy") & |«/TD»|
Next
Print #fileNum, |«/TR»|
Set tdoc=theview.getdocumentbykey(Key,True)
While Not (tdoc Is Nothing)
Print #fileNum, |«TR vAlign=top»|
Print #fileNum, |«TD class=action»| & tdoc.Description(0) & |«/TD»|
Print #fileNum, |«TD class=resp»| & tdoc.resp(0) & |«/TD»|
Print #fileNum, |«TD class=status»| & tdoc.status(0) & |«/TD»|
For i=1 To NrMonths
ym=Dateserial(Year(ym_min),Month(ym_min)+i-1,1)
ym_end=Dateserial(Year(tdoc.Date2(0)),Month(tdoc.Date2(0))+1,1)
If ym>=tdoc.Date1(0) And ym<ym_end Then
Print #fileNum, |«TD class=| & Replace(tdoc.Status(0)," ","") & |»«br»«/TD»|
Else
Print #fileNum, |«TD»«br»«/TD»|
End If
Next
Print #fileNum, |«/TR»|
Set tdoc=theview.GetNextDocument(tdoc)
If Not (tdoc Is Nothing) Then If tdoc.columnvalues(0)<>Key Then Set tdoc=Nothing
Wend
Print #fileNum, |«/TBODY»«/TABLE»|
End If
Print #fileNum, |«/body»«/html»|
Close #fileNum
End Sub
This sub initializes the HTML file, and if there are tasks, the HTML CSS and partial Table is written out (in the sub WriteHeader). Then follows a bit of logic to determine the earliest month in the range, and the latest, so the rest of the month-column-headers can be added. Finally all tasks are looped thru, to write them one at the time.
Take a look at the generated HTML (comment the 'kill' line first). - On order to have a nice layout, we use CSS, which is generated in the 'WriteHeader' sub:
Sub WriteHeader (f As Integer)
Print #f, |«style type=text/css»|
Print #f, |.tTable {border-width: 1px;border-style: solid;border-color: #aaaaaa;}|
Print #f, |TD {font-family: Arial, Helvetica, Geneva, sans-serif;font-size: 10px;font-weight: normal;color: black;border-width: 1px;border-style: solid;border-color: #dddddd;}|
Print #f, |TD.title {text-align: center;background-color: #aaaaaa;}|
Print #f, |TD.action {text-align: left;}|
Print #f, |TD.resp {text-align: left;}|
Print #f, |TD.status {text-align: left;font-weight:bold}|
Print #f, |TD.mark {text-align: left;background-color: #cccccc}|
Print #f, |TD.NotStarted {text-align: left;background-color: pink}|
Print #f, |TD.InProcess {text-align: left;background-color: lightblue}|
Print #f, |TD.Completed {text-align: left;background-color: lightgreen}|
Print #f, |«/style»|
Print #f, |«TABLE class=tTable cellSpacing=0 cellPadding=1»«TBODY»|
Print #f, |«TR vAlign=top»«TD class=title»Action List«/TD»|
Print #f, |«TD class=title»Person«br»Responsible«/TD»|
Print #f, |«TD class=title»Status«/TD»|
End Sub
All CSS is embedded, the table is initialized, and some column headers added. The rest of the column headers is generated dynamically in the WriteHTML sub.
There are a couple of drawbacks to this HTML-import method:
- You have to be in edit mode, as the import is done in the UI.
I posted the idea in Idea Jam to be able to do the import in the back-end.
Please promote this idea, as I hope you can see the advantage. - Notes does not support all HTML/CSS, so you have to do some testing before it looks the way you want
Is know there are other ways of doing this: e.g. using DXL (might be a topic for a future post), or by using the
Midas rich text LSX.
Anyway, I hope this will inspire you to do crazy stuff with HTML too.
Here's the link to the test database:Tasks.nsf
Category: Domino/Notes | Technorati: Show-n-Tell Thursday, SnTT
Comments (4)
this a very nice example. I have used HTML tables in the Notes client also some time ago. The problem was then that the Notes client does not show changes in the pass through HTML unless you ope and close the ui.document. Is this also the case here?
The coloring etcetera looks pretty nice and clear for the enduser.
Thank you for showing!
@Patrick
It's not pass-through HTML ! The import method converts the HTML to Rich Text format. You don't need to close and reopen the uidoc.
hej,
even better!
I dive a bit more into the code, thank you
Looks great!
Sady, the functionality to make great looking reports using HTML/CSS in Notes is one of the tools that "nobody" uses.
Regarding closing the uidoc (or switching read->edit->read) for documents in read-mode. Although it is irritating to write, the NotesClient does this so fast the users in most cases wouldn't notice that it's happening.
Example my "live-search"-demo in the NotesClient:
{ Link }