Blogs

  • Browse Blogs
  • My Blog
  • My Updates

Tags Help

  • View as cloud  | list

Similar Blogs

photo

Patrick Picar...

62 Entries |  Patrick Picard
Updated 
RatingsRatings 2     CommentsComments 112
photo

Lotus Nut

111 Entries |  Chris Whisonant
Updated 
RatingsRatings 23     CommentsComments 157
photo

Uh Clem's Adm...

54 Entries |  Chris Mobley
Updated 
RatingsRatings 8     CommentsComments 55
photo

Life is too s...

33 Entries |  Barbara Skedel
Updated 
RatingsRatings 3     CommentsComments 56
photo

Yellow is the...

72 Entries |  Tim Tripcony
Updated 
RatingsRatings 2     CommentsComments 34

Jan Schulz

Blog Authors:  Jan Schulz  

All entries tagged with lotusscript

Drag&Drop to the sidebar: calling code at the D&D ...

Jan Schulz  |     |  Tags:  lotusscript drag&drop sidebar  |  Comments (2)
Dear lazyweb: I've some problems with Drag&Drop into the sidebar.

The goal: drop any notes document to a sidebar target and do some LS magic with them (create a new document, call some functions from a LS-lib -> add a eProductivity Action from any NotesDocument).
The Idea: use a Folder and the Drag&Drop/AddToFolder events, add it to a Outline, add the outline to a form, add the form to the sidebar.
The Problem: using the "QueryAddToFolder"-event (and probably "QueryDragDrop" -> couldn't get it to fire...) on a folder will not work, because the event is called on the source folder, not the target :-(

Is there any way to get to the dropped documents in the target design element? And call LS function from there?

I also thought about java sidebar views (example @ SNAPPS), but some functions are in LS and I can't replicate them in java (source is hidden) :-( And I didn't find a way to call LS from Java.

Any Ideas?

Diff strings with the new "LotusScript Gold Collec...

Jan Schulz  |     |  Tags:  diff lotusscript  |  Comments (0)
Andre Guirard released some of his little treasures on OpenNTF: several lotusscript libraries. I especially like one if them: StringDiff.

This diff util was part of the replication conflict tool he posted some years ago. I used it to generate field diffs for a field level history:
Bild


Then I had to refactor it to get text output instead of html, now I only need to write a "TextDiffWriter" implementation (there is already a RichText and a HTML one), which should be about 20 lines of code, most of it function definitions and so on. Nice!

Thanks Andre!

The Adapter Pattern or working around that LS has ...

Jan Schulz  |     |  Tags:  oop lotusscript  |  Comments (0)
Peter Pressnel wrote a post about class casting and polymorphism in LotusScript. Unfortunatelly, in LS you can only extend one class, not implement Interfaces (like in every other OO language). Interfaces lets you add functionality to a given class by implementing the Interface: a Person will also by "Compareable" by implementing that interface. Now every method which needs a "Compareable" argument can work with Persons.

This missing feature in LS basicly means that you have to build this functionality anew every time you need it: you need a person.compareToAnotherPerson(aPerson as Person) and also your sorted list (or whatever needs something compared) needs to know about that method. This means a lot of unnecessary programming and also it binds your code together and you will very soon get either a very big LS file or the famous "Illegal circular use" error. Not nice!

But we have help from the Adapter Pattern. Instead of adding the functionality by implementing an interface we ask for an adapter of the object which implements this interface [1].

The adapater pattern is usually also implemented via an interface: IAdaptable.getAdapter(classname) which either returns an object of that classname or nothing. As there is no Interfaces in LS this method is implemented in a basic class from which every other of my custume classes is derived: BasicObject. This class also implements "toString() (usefull as a message to logError()...) and such basic things.

The API contract for that method is very easy: either return an object of the specified class, which represents the original object or nothing. Classcasting an object now becomes:
dim managerĀ  as Manager
set manager = person.getAdapter("MANAGER")
if manager is nothing then Error 1000, "Person is not a Manager!"
[...]
instead of
dim manager as Manager
if not (person isa ucase("Manager")) then error 1000, "Person is a not a manager!"
set manager = person
[...]

So the insert method of a sorted list, which expects a "Compareable" object (or nothing), would be something like this: list.insert(me.getAdapter("COMPAREABLE")).

This is the BaseObject.getAdapter implementation.
Class BaseObject
' [ public function toString() as String ... ]

'/**

' * Returns an Adapter of the current Object of the aked-for type or nothing
' * The basic implementation returns this object if the caller asked for the class
' * or a subclass of the class of the current object
' * Should be overwritten to do something more usefull!
' * @autor Jan Schulz
' */

Public Function getAdapter(nameOfObject As String) As BaseObject
If Ucase(nameOfObject) = Typename(Me) Then
Set getAdapter = Me
End If
If Me Isa Ucase(nameOfObject) Then
Set getAdapter = Me
End If
End Function


End Class
This LotusScript was converted to HTML using the ls2html routine,
provided by Julian Robichaux at nsftools.com.

An addition would be the implementation of an AdapterManager (see the article on eclipse.org): This would even solve the problem that you need to know the adapter class in the "adaptable" object (-> problems with "Illegal circular use"...). This would need a small AdapterFactory with one methods: getAdapter(object as BaseObject, classname as String). This factory would be implemented for each used combination and then an instance of such an object added to the AdapaterManager. Now you can query the AdapaterManager for an Adapater of your object: getAdapterManager().getAdapter(object, "SomeClassName"). In this method you would ask each adapterFactory for this combination and return if you get an object back (or nothing if no factory can return such an object). Now the object wouldn't need to know that it is adaptable to a "SomeClassName".

[1] This pattern is usually used in other OO languages to keep the API of an class small and clean: if you need your person compareable, the methods of the "Compareable" Interface would be part of the API contract for the "Person" class. This is usually not desireably, especially if you add serveral interfaces.

Compile Problem fixed: Scripname was too long

Jan Schulz  |     |  Tags:  public lotusscript name duplicate compiling  |  Comments (0)
Just a short follow up to my problem from this afternoon about recompile going into an endless loop or not saving at all, when changing the order of the Use statements (Error "Duplicate PUBLIC Name"): it seems that the compiler can't cope with long names of script libs. My scriptlib was named "de.katzien.ls.MVC.Model.MonitoringCapability". Changing it to "de.katzien.ls.MVC.Model.Monitoring" fixed the problem.

Sending Designer into endless loops with "Recompil...

Jan Schulz  |     |  Tags:  lotusscript rant designer  |  Comments (1)
... or to hell...

Currently I'm trying to build an LS framework, which is runtime configurable. Instead of using static functions or simple class extention, I try to build an object graph, where each object adds some functionality. So something which needs to run after a Document is saved will add a observer to the document model and do the work in the right callback method. This results in small objects and class libs, but lots of use of "Use". Unfortunatelly designer seems to be unable to cope with this. Right now I'm in a situation, where designer compiles my code, if I press strg+s, but is sent into an endless loop if I use "Recompile all Lotusscript".

Other fun includes "Use" ordering:
this compiles with strg+s (but sends "Recompile all" into an endless loop):
Use "class::logger"
Use "de.katzien.App.Infomail"
Use "de.katzien.App.InfomailDB"
Use "de.katzien.ls.Email"
this not:
Use "class::logger"
Use "de.katzien.App.InfomailDB"
Use "de.katzien.App.Infomail"
Use "de.katzien.ls.Email"
The only change is the ordering... Error: Duplicate PUBLIC Name "CHANGE_LISTENER_EVENT_BEFORE_CHANGING" in USE de.katzien.App.Infomail
This name is the public Const in script lib "MonitoringCapability", which is use'd directly by .App.Infomail and indirectly by .App.InfomailDB (uses InfomailEntry which uses MonitoringCapability). setting this to private just ends up with the next error and I can't set a complete class to private if I want to use that class in some other lib...

And don't even ask about "Illegal Circular Use".

@IBM: Please improve the Compiler for LotusScript!

How to test if an array is initialized?

Jan Schulz  |     |  Tags:  lotusscript array  |  Comments (1)
I like to write code like this:


public function getSomething() as Something
if oSomething is nothing then
set oSomething = new Something()
end if
set getsomething = oSomething
end function

How do this with dynamic arrays? I was very surprised when this threw an error:


dim arrSomethings() as Something
if isArray(arrSomethings) then print ubound(arrSomething) ' unitialized array error when calling the ubound function

You have to call a redim arrSomething(0 to 0) bevor you can do anything with that array. I tested it with every isXYZ function I could find in the designer help, but i couldn't find a way to detect that...Somehow I agree with Tommy Valand: LS Arrays are strange. Give me a Java collections like API and I'm happy :-)

BTW: datatype (arrSomething) gives "8738", even after the redim, but the designer help says that "8704" is a Dynamic array. Bug?

Notes becomes stranger every day: binding to onHel...

Jan Schulz  |     |  Tags:  event lotusscript binding  |  Comments (3)
Binding to an onHelp event via "On Event Onhelp From fUIDoc Call delegate_onHelp" is only working, if you have any code/Comment in the onHelp event on the Form: just leaving it with no comment in it in the client lotuscript part will not trigger delegate_onHelp. Putting a comment into the event on the form works:
image
I found that only because I wanted to test if this code element is triggered at all and then commented out my code. And behold: it worked from there...

Can someone explain this?

I think I saw a different case, where putting a comment into some otherwise empty code block made a difference, but I can't remember anymore. IMO this is just weird...

Now I only need the LS version of OpenHelpDocument :-)

Fun with events

Jan Schulz  |     |  Tags:  events lotusscript  |  Comments (0)
After seeing how much fun everybody else had with events, I tried to get my own version together.

I tried my luck with delegating the actual event handling to EventHandlers, which do the actual job. So the called QueryOpen Method looks like this:

Sub delegate_Queryopen(Source As Notesuidocument, Mode As Integer, Isnewdoc As Variant, Continue As Variant) Forall evhandler In listEventHandlers Call evHandler.Queryopen(Source , Mode, Isnewdoc , Continue ) If Not Continue Then Exit Sub End Forall End Sub


The actuall handlers extend a basic EventHandler (which does nothing in each object method) with the required functionality. The rest of the design is modeled after the MVC Pattern as described in this presentation/example DB.

Fun: binding object methods to an event and not having the object around at "call time" (like when you forget to declare your object in Declaration and just do a <code>dim controller as New BaseDocumentController(source)</code> on the PostOpen-Event of the Form) will do funny things in the debugger: after the QueryClose Event the client goes into a endless loop. Without debugger, just nothing happens.

This is the complete code for Tims example:
Private Const ERR_ABSTRACT_INSTANTIATION = 1000 Private Const MSG_ABSTRACT_INSTANTIATION = | Attempt to instantiate as an object instance the abstract class | Public Class BaseDocumentController %REM The Controller Part of MVC Binds to a UIDocument, delegates all events to the EventHandlers %END REM Private fUIDoc As NotesUIDocument Private fModel As BaseDocumentModel Private fIsInitialized As Boolean Private listEventHandlers List As AbstractDocumentEventHandler Sub new(uidoc As NotesUIDocument) If Not uidoc Is Nothing Then Call initializeWithUIDoc(uidoc) End If End Sub Public Function getUIDoc() As NotesUIDocument Set getUIDoc = fUIDoc End Function Public Function initializeWithUIDoc(uidoc As NotesUIDocument) If uiDoc Is Nothing Then Error 1111, "Need a UI Document" End If Set fUIDoc = uidoc Set fModel = New BaseDocumentModel( fUIDoc.Document) fIsInitialized = True Call bindEvents() Call setupEventHandler() End Function %REM adds the default EventHandler (Validation) the to eventhandler List Can be overwritten to extend the EventHandler List, needs to call BaseDocumentController..setupEventHandler() to get the basic EventHandler setup or you need to add validation yourself %END REM Function setupEventHandler() Dim evHandler As New ValidationHandler(fModel) Call Me.addEventHandler( "validation", evHandler) Dim evHandler2 As New AuditTrailHandler(fModel) Call Me.addEventHandler( "AuditTrail", evHandler2) End Function Public Function addEventHandler(evName As String, evHandler As AbstractDocumentEventHandler) Set listEventHandlers(evName) = evHandler End Function Private Function bindEvents() On Event Queryopen From fUIDoc Call delegate_Queryopen On Event Postopen From fUIDoc Call delegate_Postopen On Event Querymodechange From fUIDoc Call delegate_Querymodechange On Event Postmodechange From fUIDoc Call delegate_Postmodechange On Event Queryrecalc From fUIDoc Call delegate_Queryrecalc On Event Postrecalc From fUIDoc Call delegate_Postrecalc On Event Querysave From fUIDoc Call delegate_Querysave On Event Postsave From fUIDoc Call delegate_Postsave On Event Querysend From fUIDoc Call delegate_Querysend On Event Postsend From fUIDoc Call delegate_Postsend On Event Queryclose From fUIDoc Call delegate_Queryclose End Function Sub delegate_Queryopen(Source As Notesuidocument, Mode As Integer, Isnewdoc As Variant, Continue As Variant) Forall evhandler In listEventHandlers Call evHandler.Queryopen(Source , Mode, Isnewdoc , Continue ) If Not Continue Then Exit Sub End Forall End Sub Sub delegate_Postopen(Source As Notesuidocument) Forall evhandler In listEventHandlers Call evHandler.PostOpen(Source) End Forall End Sub Private Sub delegate_Querymodechange(Source As Notesuidocument, Continue As Variant) Forall evhandler In listEventHandlers Call evHandler.Querymodechange(Source, Continue) If Not Continue Then Exit Sub End Forall End Sub Private Sub delegate_Postmodechange(Source As Notesuidocument) Forall evhandler In listEventHandlers Call evHandler.Postmodechange(Source) End Forall End Sub Private Sub delegate_Queryrecalc(Source As Notesuidocument, Continue As Variant) Forall evhandler In listEventHandlers Call evHandler.Queryrecalc(Source, Continue) If Not Continue Then Exit Sub End Forall End Sub Private Sub delegate_Postrecalc(Source As Notesuidocument) Forall evhandler In listEventHandlers Call evHandler.Postrecalc(Source) End Forall End Sub Private Sub delegate_Querysave(Source As Notesuidocument, Continue As Variant) Forall evhandler In listEventHandlers Call evHandler.Querysave(Source, Continue) If Not Continue Then Exit Sub End Forall End Sub Private Sub delegate_Postsave(Source As Notesuidocument) Forall evhandler In listEventHandlers Call evHandler.Postsave(Source) End Forall End Sub Private Sub delegate_Querysend(Source As Notesuidocument, Continue As Variant) Forall evhandler In listEventHandlers Call evHandler.Querysend(Source, Continue) If Not Continue Then Exit Sub End Forall End Sub Private Sub delegate_Postsend(Source As Notesuidocument) Forall evhandler In listEventHandlers Call evHandler.Postsend(Source) End Forall End Sub Private Sub delegate_Queryclose(Source As Notesuidocument, Continue As Variant) Forall evhandler In listEventHandlers Print "Calling eventhandler for QueryClose" Call evHandler.Queryclose(Source, Continue) If Not Continue Then Exit Sub End Forall Print "End QueryClose" End Sub End Class Class BaseDocumentModel %REM The model part of MVC Binds to the backend document and does validation (not implmented properly) and other stuff with the data %END REM Private fDoc As NotesDocument Private fIsInitialized As Boolean Sub new(doc As NotesDocument) If Not doc Is Nothing Then Call initializeWithDocument(doc) End If End Sub Function initializeWithDocument(doc As NotesDocument) If doc Is Nothing Then Error 1111, "Model needs a Document" End If Set fDoc = doc fIsInitialized = True End Function Public Function validate() As ValidationResult If Not fIsInitialized Then Error 11111, "Uninitialised Model!" ' Checks the model: something like a array of validation rules and the ValidationResult is passed to each rule to collect the result ' Just for show: Set validate = New ValidationResult() End Function End Class Class validationResult %REM Collects the result of a validation %END REM Sub new () ' Nothing to do End Sub Public Function addError(fieldname As String, Message As String) ' unimplemented End Function Public Function hasErrors() As Boolean ' unimplemented hasErrors = False End Function Public Function getFirstInvalidField() As String ' unimplemented getFirstInvalidField = "Subject" End Function Public Function getFormattedErrorMessage() As String 'unimplemented getFormattedErrorMessage = "Some Field: invalid" End Function End Class Public Class AbstractDocumentEventHandler %REM Handels the delegated events from the Controller Overwrite specific methods to let them do something %END REM Private fModel As BaseDocumentModel Public Sub New (model As BaseDocumentModel) Dim classname As String Let className = Typename(Me) If (className = "ABSTRACTDOCUMENTEVENTHANDLER") Then Error ERR_ABSTRACT_INSTANTIATION, MSG_ABSTRACT_INSTANTIATION & classname Exit Sub End If Set Me.fModel = model End Sub Sub Queryopen(Source As Notesuidocument, Mode As Integer, Isnewdoc As Variant, Continue As Variant) ' To be overwritten End Sub Sub Postopen(Source As Notesuidocument) ' To be overwritten End Sub Public Sub Querymodechange(Source As Notesuidocument, Continue As Variant) ' To be overwritten End Sub Public Sub Postmodechange(Source As Notesuidocument) ' To be overwritten End Sub Public Sub Queryrecalc(Source As Notesuidocument, Continue As Variant) ' To be overwritten End Sub Public Sub Postrecalc(Source As Notesuidocument) ' To be overwritten End Sub Public Sub Querysave(Source As Notesuidocument, Continue As Variant) ' To be overwritten End Sub Public Sub Postsave(Source As Notesuidocument) ' To be overwritten End Sub Public Sub Querysend(Source As Notesuidocument, Continue As Variant) ' To be overwritten End Sub Public Sub Postsend(Source As Notesuidocument) ' To be overwritten End Sub Public Sub Queryclose(Source As Notesuidocument, Continue As Variant) ' To be overwritten End Sub End Class Public Class ValidationHandler As AbstractDocumentEventHandler %REM Handels the validation of the model in the QuerySave Event, - stops saving when the model is not valid - jumps to the first invalid field, as returned by the ValidationResult %END REM Sub new(model As BaseDocumentModel), AbstractDocumentEventHandler(model) ' nothing to do, just pass the model on to parent class... End Sub Public Sub Querysave(Source As Notesuidocument, Continue As Variant) Print "doing validation" Dim oResult As ValidationResult Set oResult = fModel.validate() If oResult.hasErrors() Then Continue = Continue And False Msgbox oResult.getFormattedErrorMessage(), 0 , "Validation error" Call source.GoToField(oResult.getFirstInvalidField()) End If End Sub End Class Public Class AuditTrailHandler As AbstractDocumentEventHandler %REM Adds a comment to the backend document in the QuerySave Event, - This should better be done in an event in the Model: Controller -> QuerySave -> Everything validates, etc -> model.QuerySave(....) -> Calls a listener, which does the Trail or the model does it itself -> even when you use the model without a controller, it gets a Trail %END REM Sub new(model As BaseDocumentModel), AbstractDocumentEventHandler(model) ' nothing to do, just pass the model on to parent class... End Sub Private Sub Querysave(Source As Notesuidocument, Continue As Variant) Print "Doing Audit" Dim sess As New NotesSession Dim newEdit As String Dim editType As String If (Source.IsNewDoc) Then Let editType = "created" Else Let editType = "modified" End If Let newEdit = Cstr(Now) & " - Document2 " & editType & " by " & sess.commonUserName Call Source.Document.ReplaceItemValue("AuditTrail", Fulltrim(Split(newEdit & Chr(13) & Join(Source.Document.GetItemValue("AuditTrail"), Chr(13)), Chr(13)))) End Sub End Class
This LotusScript was converted to HTML using the ls2html routine,
provided by Julian Robichaux at nsftools.com.

Skip to main content link. Accesskey S
IBM Lotus Connections Help Tools About

Tags

A tag is a keyword that is used to categorize an entry. To view the entries with a particular tag, click a tag name or enter a tag in the box.
The tag cloud indicates the frequency of tag use. Popular tags appear darkest. The slider control adjusts how many tags are displayed in the tag cloud.