Passing arguments to a LotusScript agent
One of the topics that seems to keep popping up on the Notes.net Café is passing arguments to agents on the web. Half the postings asking "can you do it?"; the other half asking "how do you do it?". In answer to the first half: yes you can. In answer to the other half: read on...
Passing paramaters to an agent works in exactly the same way as passing parameters to a form or a page: we do it using the Query String portion of the URL. A typical URL looking something like this:
../db.nsf/url?OpenAgent&Name=Jake&Age=26&Country=UK
The code required in the agent to strip these values out of the URL can often become quite complicated and often required hard coding the name of the value that we want to look for. The following code can be re-used in any agent, and simply place all the arguments and their corresponding values in to a nice list. We can then look in this list and retrieve a particular value simply by supplying its name.
The agent starts with the standard declarations:
Dim s As New NotesSessionFollowed by declaring the list variable that will hold all the arguments and their values:
Dim db As NotesDatabase
Dim doc As NotesDocument
Set db = s.CurrentDatabase
Set doc = s.DocumentContext
Dim AgentArgs List As Stringand a call to the subroutine that does all the work (code appears further down the page):
Call ExplodeQueryString (doc.Query_String_Decoded(0)This list would normally be used to let the agent go and retrieve the required information, but, for the sake of a demo, lets loop through all the parameters and send their key/value pairs back to the browser:
, AgentArgs)
Forall Args In AgentArgsThis method of looping through ALL the arguments may not be necessary if you already know the name of the parameter's key. For example, if a key had the name "Country", you could get to its value like this:
Print "Parameter Key: " + Listtag(Args) + "<br />"
Print "Parameter Value: " + Args + "<p />"
End Forall
Print "Country Code: " + AgentArgs("country")The ExplodeQueryString sub-routine:
Private Sub ExplodeQueryString (QueryString As String,In order to reference a member of the list explicitly, like we did above, it is wise to check that it exists first. If you try to access a list item that does not exist the script will error. Use the IsElement method to help avoid errors:
AgentArgs List As String)
Dim Args As String
Args = RightStr(QueryString, "OpenAgent&")
Dim ArgsList As Variant
ArgsList = Evaluate ({@Explode("} & Args & {"; "&")})
Dim ArgKey As String
Dim ArgValue As String
Forall Arg In ArgsList
ArgKey = LeftStr(Arg, "=")
ArgValue = RightStr(Arg, "=")
AgentArgs(ArgKey) = ArgValue
End Forall
End Sub
if IsElement( AgentArgs ("ID") ) thenOverall, not a particularly useful example, I know, but I'll leave it to you as to how you wish to implement it.....
print "Your ID number is " + AgentArgs ("ID")
else
print "There is an error in the URL that was used."
end if
The LeftStr and RightStr functions
Function LeftStr(OrigStr, LeftOf ) As String
Dim Pos As Integer
Dim OrigStrLen As Integer
Pos = Instr( Lcase(OrigStr), Lcase(LeftOf) )
OrigStrLen = Len(OrigStr)
If pos>0 Then
LeftStr = Left( OrigStr, (Pos-1))
Else
LeftStr = OrigStr
End If
End Function
Function RightStr(OrigStr, RightOf ) As String
Dim Pos As Integer
Dim OrigStrLen As Integer
Dim RightOfLen As Integer
Pos = Instr( Lcase(OrigStr), Lcase(RightOf) )
OrigStrLen = Len(OrigStr)
RightOfLen = Len(RightOf)
If Pos>0 Then
RightStr = Right( OrigStr, OrigStrLen -
(RightOfLen+Pos-1))
Else
RightStr = OrigStr
End If
End Function
Another way of passing arguments to an agent
I found another effective method of passing values to an agent which I used to get around the limitation of the Query_String CGI variable.
First you must create a form tag on your current form.
<FORM NAME="formsubmit" METHOD="post" ACTION="<agentname>?openagent"> <input type="hidden" name="selecteddocs" value="<Computed Value>"> </FORM>
Next create input tags for all fields you need to send to the agent inbetween the form tags.
From your action button on the main form, submit this form which causes the agent associated with the form action to run.
In the agent you will have to use the functions below to get the values of the fields from the Request_Content CGI variable.
'This line decodes the request_content field
request_content_decoded=fnDecode(doc.request_content(0))
'This line gets the field you are looking for from the decoded data
fieldvalue = fnGetRCFieldValue(request_content_decoded,"<fieldname>")
function fnDecode(inString As String) As String Dim L As String Dim M As String Dim R As String Dim P As Integer P% = Instr(inString,"%") If P%>0 Then L$=Left$(inString,P%-1) M$=Mid$(inString,P%+1,2) R$=Right$(inString,Len(inString)-(P%+2)) fnDecode1 = L$ & Chr(Cint("&h" & M$)) & R$ If Instr(fnDecode1,"%") Then fnDecode1 = fnDecode1(fnDecode1) Else fnDecode1 = inString End If End Function
Function fnGetRCFieldValue( requestContent$, fieldToGet$ ) As Variant 'Owen Enraght-Moony 28-01-99 'returns: the value of a field in the Request_Content CGI var (case sensitive) Dim search$, retStr$, offset%, ends% search$ = fieldToGet$ & "=" retStr$ = "" offset% = 0 ends% = 0 offset = Instr(requestContent$, search) If (offset <> 0) Then offset = offset + Len(search) ends = Instr(offset, requestContent$, "&") If (ends = 0) Then ends = Len(requestContent$)+1 retStr = Mid(requestContent$, offset, ends-offset) End If fnGetRCFieldValue = Trim$(retStr) End Function
This method is very handy when you have to run an action from a $$viewTemplate for xx form. You can have as many form tags you like and run different agents for each action button.
Jake, thanks again for all the great tips. This is my immediate contribution for all the tips I have received from your site.
Reply
Re: Another way of passing arguments to an agent
I have been using a similar technique for a number of years. I have created a lotuscript class to parse the document context and access its fields and just like a notes document e.g. webDoc.GetItemValue("MyFIeld")(0) I then access these fields to create and edit documents with added advantage that Users only have READER access to the database but can submit and edit documents. Although the class does not work with multipart web forms so you cannot upload files. You can also create new forms by just creating web pages and not adding them to the design of the notes database. So no waiting for the Admin department to update the design of the database, just add a couple of new web documents to the database. You can create a whole web site based on just a few forms and views in the design of the database.
Jake I have already partly modified your forms Validation database to use this method if your interested I can send it to you when i have finished.
Reply
Show the rest of this thread
Re: Another way of passing arguments to an agent
what are the limitations of the Query string? is it simply a size limitation?
Reply
Show the rest of this thread
RequestContentParser class
This thread helped me out a LOT today.
Here's my version of the code for parsing the Request_Content field
[<br clear="all" /><code>] Public Class RequestContentParser Private psRC As String Private piAmpPos As Integer Sub new(Byval sRequest_Content As String) psRC = sRequest_Content piAmpPos = 0 End Sub Public Function Unescape(Byval s As String) As String Dim iP As Integer Dim sL As String Dim sM As String Dim sR As String iP = Instr(s,"%") If iP <> 0 Then sL = Left$(s,iP-1) sM = Mid$(s,iP+1,2) sR = Mid$(s, iP+3) Unescape = sL & Chr(Cint("&h" & sM)) & Unescape(sR) Else Unescape = s End If End Function Public Function GetNextField(sFieldName As String, sValue As String) As Variant 'boolean, true if successful, false if no more fields Dim iEqPos As Integer Dim iFieldPos As Integer On Error 1 Goto ErrorSub If piAmpPos = -1 Then Error 1 iFieldPos = piAmpPos + 1 iEqPos = Instr(iFieldPos, psRC, "=") If iEqPos = 0 Then Error 1 sFieldName = Mid(psRC, iFieldPos, iEqPos - iFieldPos) piAmpPos = Instr(iEqPos, psRC, "&") If piAmpPos <> 0 Then sValue = Unescape(Mid(psRC, iEqPos + 1, piAmpPos - iEqPos - 1)) Else sValue = Unescape(Mid(psRC, iEqPos + 1)) piAmpPos = -1 'flag that there are no more fields End If GetNextField = True ExitSub: Exit Function ErrorSub: GetNextField = False Resume ExitSub End Function Public Function GetFieldValue(sFieldName As String) As String Dim iAmpPos As Integer Dim iFieldPos As Integer Dim iValPos As Integer Dim sValue As String iFieldPos = Instr("&" + psRC, "&" + sFieldName + "=") If iFieldPos = 0 Then GetFieldValue = "" Else iValPos = Len(sFieldName) + 2 iAmpPos = Instr(iValPos, psRC, "&") If iAmpPos = iValPos Then GetFieldValue = "" Else GetFieldValue = Unescape(Mid(psRC, iValPos, iAmpPos - iValPos)) End If End If End Function End Class [</code>]
You use it by putting this code in your declarations, and having the line
From there, you do something like: [<code>] Set Session = New NotesSession Set RCP = New RequestContentParser(Session.DocumentContext.Request_Content(0)) Do while RCP.GetNextDocument(sFieldName, sValue) Print sFieldName + { = "} + sValue + {"<br>} Loop [</code>]
The GetFirstField method is unnecessary unless you need to start from the begining a second time.
You can also get a specific field value with GetFieldValue.
Enjoy, and Thanks!
Reply
Show the rest of this thread
Re: Another way of passing arguments to an agent
Do you know if I can use another char intead "&" in the query string?
I can't use the "&"...
Thanks in advance.
Andrea
Reply
How do i get same functionality in a form?
I want to set the value of one field of a form by passing it a web parameter.
It is possible to load the parameter from inside the form using this trick ??
In case it is possible, I'd to know wich event should I code to achieve it.
Thank you!!
Reply
Re: How do i get same functionality in a form?
If your form will have the hidden field called "Query_String" on it and the parameter is on the URL for loading the form, it will work.
(http://servername/dbname.nsf/formname?openform&Parameter).
Domino will put the parameters in the hidden field.
Reply
You are driving me crazy
Good stuff, but if this is about passing parameters to an agent, why is you sample code full of references to fields on a form (which you do not describe): lines from your sample code below...
Dim doc As NotesDocument ---This is a document Set doc = s.DocumentContext ---A form open in the browser
Call ExplodeQueryString (doc.Query_String_Decoded(0) , AgentArgs)
--Where is the field called "Query_String_Decoded"? --What is the form that holds it? --What are the fields and default value formulas? --If we're using a form, the "Openform" URL can have the parameters --so why use an agent?
Reply
You drive me crazy
Have you *actually* tried to create an agent with this code and then opened it in the browser by typing an "?OpenAgent" URL with some parameters appended to it??
Until you have don't come here telling me I drive *you* crazy!
Jake
Reply
Show the rest of this thread
Beware: Query_String_Decoded isn't always there
We are using "Site Minder" on our Web Mail severs and have found to our dismay, that the Query_String_Decoded variable is blank (Query_String has a value).
This has caused some of Lotus' code for web mail (R4.6) to fail. The problem has been turned over to the vendor.
Be sure to check your CGI variables on the production server before using them in your code.
Reply
Maybe I'm missing something?
Jake,
I understand the value of pasing arguments to an agent but In this line of code (your example) ../db.nsf/url?OpenAgent&Name=Jake&Age=26&Country=UK
How are you passing Jake, 26, and UK to the url?
Are these values hardcoded?
Reply
Re: Maybe I'm missing something?
Does it matter? I don't see your point. How you pass in values is irrelevant...
Jake -codestore
Reply
Show the rest of this thread
This was a great help. Thanks!
Jake,
I know you wrote this a long time ago, but I wanted you to know that it's helped me out tremendously even 3 years later. Thanks for this site.
Reply
Agent
This Code saved my life, Thank you so much!
Reply
Re: Agent
Here is a short java agent that shows how to retrieve the value of Query_String from a Java agent.
The code retrieves the value of Query_String and then outputs it - does not parse it at all...
Should be enough code to get someone started in the right direction with Java agent. Derek
import lotus.domino.*; import java.util.Vector; import java.io.*; public class JavaAgent extends AgentBase {
public void NotesMain() {
try { Session session = getSession(); AgentContext agentContext = session.getAgentContext(); Document current_doc = agentContext.getDocumentContext(); // Parse Query String Vector v1 = current_doc.getItemValue("Query_String"); PrintWriter pw = getAgentOutput(); pw.println("Query String: "+v1); } catch(Exception e) { e.printStackTrace(); } } }
Reply
getArg function.
String getArg(StringTokenizer vST, String Arg) { while(vST.hasMoreTokens()){ if(vST.nextToken().toLowerCase().equals(Arg)) return vST.nextToken(); } return null; }
Reply
Lotus Script Agents
How to change the status of the documents
Reply
Problems with passing "=" in a parameter?
Hello,
when I try to pass the canonical username (CN=Peter Pan...) as an agent parameter, where I have to encode it before (CN%3DPeter+Pan...), then my QUERY_STRING_DECODED only returns "CN" and not the full name. It seems, that I can not pass an encoded "=" as a parameter. Any idea?
regards, Harold
Reply
Re: Problems with passing
Solved... I was using a buggy class QSParser from notes.net... never trust someone else code...
Reply
Help query String
Do you know if I can use another char intead "&" in the query string?
I can't use the "&"...
Thanks in advance.
Andrea
Reply
Thanks a lot for this website!
Perhaps one hint:
If the the cgi-variable "Query_String(0)" or "Query_String_Decoded(0)" are empty, create a hidden field with the same name (Query_String or Query_String_Decoded) in your form. This fields automatically get the right value in the web. Then you can use
doc.Query_String_Decoded(0)
Greetings,
David
Reply
I too query_string is empty
but my web agent is called with url http not by a form
how do it ? adding &Query_String= in URL does not change...
Reply
Show the rest of this thread
Dear All,
Thanks for ur posting. Its very useful for me.
Regards,
Maheswari.K
Reply
Here is a LS routine I have been using.
Option Public
Public nSession As NotesSession
Public naCurrent As NotesAgent
Public ndbCurrent As NotesDatabase
Public ndocCurrent As NotesDocument
Public ndocUser As NotesDocument
Public vURLParams List As String
Sub Initialize
Set nSession = New NotesSession
Set naCurrent = nSession.CurrentAgent
Set ndbCurrent = nSession.CurrentDatabase
Set ndocCurrent = nSession.DocumentContext
Call ParseURL
End Sub
Sub ParseURL
On Error Goto CommomErrHandler
Dim vURL As String
Dim vPairs As Variant
Dim vLabel As String
Dim vData As String
vURL = ndocCurrent.Query_String(0)
vPairs = Split(vURL,"&")
Forall vItem In vPairs
vDelimiter = Instr(vItem,"=")
If vDelimiter > 0 Then
vLabel = Left(vItem,vDelimiter-1)
vData = Mid(vItem,vDelimiter+1)
Else
vLabel = Left(vItem,vDelimiter)
vData = ""
End If
vURLParams(vLabel) = vData
End Forall
Exit Sub
'treats commom errors
CommomErrHandler:
'This line displays in browser
Print "Got error on (" + naCurrent.Name + "-ParseURL) #" + Cstr(Err) + " - " + Error$ + " on line " + Cstr(Erl)
'This line displays on the Domino server console & logs
Msgbox "Got error on (" + naCurrent.Name + "-ParseURL) #" + Cstr(Err) + " - " + Error$ + " on line " + Cstr(Erl)
End Sub
You can then recall any of the args simply by value = vURLParams("argname")
e.g. url = htt://server/db.nsf/agent?OpenAgent&city=Boston
you get the city by city = vURLParams("city")
Reply