Processing multiple documents from a view
As Domino has matured over the years, the number of features only available to the Notes Client developer has decreased. For example, one feature of the Lotus Notes client is the ability to select documents in a view and perform an operation on all those selected. This ability doesn't translate well to the web browser and it wasn't until Lotus released Domino R5.0.5 that they introduced the feature that allowed the selection of documents in a view.
The Problem:
The problem with Lotus's solution is that it only really solved the problems that they deemed worthy. Mainly the ability to move documents between folders in the Mail template. What we really want is the ability to select the documents and do what we like with them. That is what I aim to cover in this article.
A Solution:
The solution most often suggested to this problem involves creating a long URL that consists of a set or document Unique IDs. Pass this list to an agent and it can decode the list and process each document in turn.
There are problems with this solution. For a start there are the limits in the allowed length of a URL using the GET method. This limit varies from browser to browser and between servers but, if we assume it to be 1,024 characters, the number of documents we can process at any one time would have to somehow be limited to about 30!
Another possibly more serious problem is that the browser will cache the URL. All GET requests from a browser are stored in the browser's history. Hence a user could inadvertently re-submit their request for the same URL and cause any code to run again, re-processing the same set of documents, at any time in the future. This can easily be done by either pressing the refresh button or by cycling through the history using the Back and Forward buttons.
The Solution:
In order to remove the chance of either of the above two scenarios it is advisable that we use the POST method instead. In fact you could argue that we should be using this method in the first place anyway. In the words of the W3C:
In particular, the convention has been established that the GET method SHOULD NOT have the significance of taking an action other than retrieval. These methods ought to be considered "safe". This allows user agents to represent other methods, such as POST, in a special way, so that the user is made aware of the fact that a possibly unsafe action is being requested.
So how do we go about doing the same thing with but using the POST method? Well, we take a method I described on this site some time ago and mould it to this scenario. The basics being that we trick Domino in to letting us submit forms and data we generated ourselves. Let's see how.
As an example I will use a database that I made available online after a reader asked me for help. In this database there is a view of documents that are in one of two states. Published or Un-published. What is needed is a nice and easy way to publish/unpublish more than one at a time without having to edit and save each one. To do this we create a set of checkboxes (one for each document) and two buttons, as in the screenshot below:
From this view an administrator can easily select any number of documents and, using one of the two buttons, quickly change their status.
Implementing the solution:
Before going any further it's probably a good idea to download the example database so that you can follow my directions without me having to go in to too much detail. I will assume you have done.
The first thing we need to do is create a view that allows us to select documents. To do this we need the view to be treated as HTML, like below:
Notice that the property "Allow selection of documents" is not selected. We are about to do this part ourselves. In the first column of the view (called "All" in the example DB) we use the following formula:
"<input type=\"checkbox\" name=\"processdocs\" value=\"" + @Text(@DocumentUniqueID) + "\" />"
The result is a set of fields sent to the browser that all have the same name. We can send them back the Domino server and it will treat it as though they were all simply one multi-value field containing the list of IDs for the selected documents.
Obviously these fields need to be on a form. In the example database it's the form called "$$ViewTemplate for All". Take a look at this form and notice that we have included some HTML that ends the default Domino form and starts a new one called "processor". This is where the checkbox fields reside. When the user clicks either of the two buttons it is this form that is submitted and not anything that Domino created. Notice also that the action parameter of this new form. It point to a URL that will create a document using the form called "Processor".
If you have a look at the Processor form you will notice I added a field called SaveOptions with a value of "0" so that no document is actually created. Notice also that there is nothing much to this form other than the two fields to hold the data passed in to it and that it has a WebQuerySave agent setting. When the user submits the form's view, the server "creates" a document with the Processor form and this agent gets triggered. It's in this agent that we do the processing work. The code is extremely simple and the guts of it are shown below:
Dim process As NotesDocument
Dim item As NotesItem
Dim value As String
Set db = session.CurrentDatabase
Set view = db.GetView("all")
Set context = session.DocumentContext
Set item = context.GetFirstItem("processdocs")
value = context.status(0)
Forall v In item.values
Set process = db.GetDocumentByUNID( v )
process.Status = value
Call process.Save( True, True)
End Forall
Call view.Refresh
So the field that was submitted called "processdocs" holds the list of IDs for each document that we want to work on. The field called "status" holds the new value for status of all these documents. Hopefully how this code works is fairly self-explanatory and so I won't go in to any more detail than to say that it loops through each document, sets the new field value and saves it. The final step is to refresh the view so that when the $$Return field redirects the browser back to the view all the changes are obvious.
Taking it further:
What I've shown as an example here is only a basic application of this method. It could be used for other far more varied and advanced techniques.
For example, it would be easy to add a free-text field to the view's template. This way you could change the value of any field on any number of documents. All in the click of one button!
In Summary:
This might not be useful to everyone but I can guarantee you come across a situation where this technique, or a an application of it, couldn't make somebody's life an awful lot easier.
I like this.
Could there be a way to change the value of the button from a list? What I am getting at could you use this to replace one person for another from a list? If you had a dropdown list where you could pick a person and it would replace the value in the button and then change the documents you select. I hope this makes sence. Thanks for all of your hard work.
Reply
Re: I like this.
You can change the value to whatever you want Brian.
Pass the value from the dropdown to the agent and take it from there....
Jake -webmaster
Reply
help!!
Hi there, It's a great program that I need it, however, I got it to save the processor document but somehow there are no document id of the selected documents in the processordocs field so it did not process the selected documents. What did I do wrong?
Thank you for your help, Pat
Reply
iternal server error in site
somebody help me to how to enter the sites. that i like but theres a problem b coz when im enter thats site said.... the server encountered an internal error or misconfiguration and was unable to complete my request plzzzzzzzzzzzzzzz////??????????????
Reply
Great, but ...
... you're late. I would have needed this fine approach three weeks ago. O.K., looks like I'm somewhat fastidious. Usually, when I'm struggeling with some special problem, the solution is already available here at the CodeStore. ;-) Yet Another reason to praise CodeStore wherever I go.
Reply
mutiple doc selected
have you seen this example jake?
http://edia.dominodeveloper.net/members/edia/DXWeb.nsf
this is from my projectdx.org website examples. You press right mouse click over the view and select enable view picking - then you can click on documents to select them
regards
steve
Reply
what about NoteID?
seems that you could use NoteID instead of UNID to make the long URL thing less problematic?
but either way, this is pretty cool. Great site-
-Seth Richmond, VA
Reply
Re: mutiple doc selected
Question: I am using subforms on my original form and $$DefaultView form for the navigator bar and the company logo as the header... When I put the code in... The form looks right, but when I click on All Documents. It is replaces the body with my view and does not do the column spacing or leave the Select: Deselect: All Documents.... Do I have the code in the wrong spot?
Reply
Cool soultion
Very nice solution. Really simple and cool. Great site to refer when there are problems to solve :-)
Keep up the good work.
Regards,
Uma
Reply
Doing it all on one form
Thanks for the article - I was just about to do something very similar so you have saved me a bundlw of work.
I would however do it slightly differently. Instead of using a $$View form I propose to embed the view in a normal form (I need to do this in any case because I want to deal with one category at a time). I can then put the ProcessDocs field on that form and hide it. While Domino doesn't like getting data for fields that are not on the form it doesn't mind if the field is there but hidden.
Reply
Write/process as XML
Another solution to this continual problem is to have some javascript that creates a XML document in a text field. A java agent then processes the xml document.
Gives you a good excuse to explore XML and Java!!
Reply
What if you need to check documents in > 1 page
Very nice solution but we have a view with more than 1000 documents and we need to select, for instance, a document in page 1 and a document in page 2. How to keep the selection ?
Reply
Cookies?
Reply
Re: What if you need to check documents in > 1 page
Hello! Put in server configuration setting in DominoWebEngine Maximum lines per view page to 0 (zero), then in browser put in link &count=2000, now you can select 2000 documents. Elvin
Reply
Hmm, I did it way back in 4.5, but
since R5.0.8 you do not need this trick anymore. Using standard Lotus provided selection you can perform ALL this staf much easier. IT IS NOT LIMITED TO MOVING BETWEEN FOLDERS, you can execute agents upon selected douments.
Reply
Can you point us to some documentation?
Reply
What about more than two fields?
Dear Jake, First, thank you very much for this article which is very helpful to me. Second, I found that if I tried to make more than two fields (e.g. one is the checkbox, the other is an textbox) in the view, it then fails to retrieve the multi-values of the checkbox field and textbox field but only the last one value of the checkbox field and none of the textbox. (I had also added an additional fiels for the textbox) I am wondered! What was wrong? Please advise. Thank you very much.
Reply
Re: What about more than two fields?
I think that when domino encounters a checkbox field in a webform, it starts to evalutate it until it finds another field to evaluate too.
If, later in this form, domino finds again our checkbox field, it starts a new computation of it (Checkbox=the value) rather than continue to evaluate it (Checkbox+=the value...
Do you understand something in this explanation ? Oh, just because I'm french, and not at ease on english...
Reply
Re: What about more than two fields?
Dear Jake,
Firstly wish you a very happy holiday season.
I downloaded the database in this article. Runs fine on local but fails when ported onto a server.
Any changes needed to make it run on a server??
Thanks -- Nagesh
Reply
Show the rest of this thread
catching up
Hey cool... funny but my WebMail template has been doing this for 3 years and since r4... even before Lotus decided they could do it.. sometimes it takes a while... ;-)
Reply
Make value equal to field
I'm sure this is really obvious, but as someone who has got by with formula language i get a little unstuck when javascript is employed.
If I was to make the value of the status field, equal to the value of a field rather than '0' or '1', how would I do this?
Reply
Re: Make value equal to field - duh
Worked it out, just replaced the value with a computed text; ie '<computed text>' instead of '0'
Reply
Show the rest of this thread
Similar article...
Perhaps this explains why a similar solution I found on Notes.net didn't work! Mike Goulding published an article on 03/11/2002 dealing with the same topic. I could not get his solution to work since it used the default form Domino generates. The agent specified in the webquerysave never fired. The Notes log contained an entry that the reference to the checkbox field did not exist. I'm a relatively inexperienced Domino developer, just barely a year of use. Can anyone explain to me why the view had to be placed in a different form for this to work??? Thanks!
Reply
Text update
Hi Jake,
Exellent, simple approach. I would like to thank you for providing this valuable resource (codestore.net).
I would like to take this a step futher and use text boxes (i.e. StartDate, FinishDate, etc.).
All I could find on NotesNet is http://www.notes.net/today.nsf/0/d069d5eca629d66a852565b6006df580. Their approach is to create a static field on the processing form for each textbox/doc in the view (hideous).
Do you know of a more elegant way to do this. For example, aggregate each column of text values into a single notes list field and then loop through it like you do with the checkboxes?
Any input would be awesome. If it works, I'll update it into your sample db and send it to you for "Processing multiple documents from a view, Part II".
Thank you, Gangle
Reply
ATTN: Using this with "require SSL" - solution
Jake,
This is a great technique! I unfortunately got stumped for a while because exact copies of your design elements (re-signed) were not working for me after being pasted into my database. The problem presented itself as the agent not seeing any POST data, only GET data. I knew like all computer problems I was just missing some single character somewhere, and sure enough that held true again.
I finally realized because I had "require SSL for this database" checked on the database properties, combined with a conflicting HTML Head Content on the form that Domino kept rewriting the POST into a GET.
The solution was simple: change the HTML Head Content on the $$ViewTemplate you provided to have HTTPS instead of HTTP as follows: @NewLine + "<base href=\"https://" ... rest of your code.
In the process of doing this, I did hack together a previous agent from one of your other readers, along with updating it a bit to use @URLDecode. The purpose of this agent is to spit out on the server console what kind of data is coming in on a request to an agent (GET or POST). Maybe someone else will find it handy also. It will display any data POST-ed to it on the server console for debug purposes.
'CodeStoreRequestProcessor:
Option Public
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
Sub Initialize Dim session As New NotesSession Dim db As NotesDatabase Dim context As NotesDocument Dim process As NotesDocument Set db = session.CurrentDatabase Set context = session.DocumentContext Messagebox "Request_method: " + context.Request_Method(0) Messagebox "Request_context: " + context.Request_Content(0) Const NotesMacro$ = |@URLDecode("Domino"; Request_Content)| Dim request_content_decoded As Variant request_content_decoded = Evaluate( NotesMacro$, context) Messagebox "Decoded: " & request_content_decoded(0) 'This line gets the field you are looking for from the decoded data Dim fieldvalue As String fieldvalue = fnGetRCFieldValue(request_content_decoded(0), "processdocs") Messagebox "processdocs: " & fieldvalue End Sub
Reply
the online version does not work
the online version creates an error when you hit the buttons the download version also does not work correctly (i've tried it several months ago then there where no problems)
i liked the concept
Reply
Re: the online version does not work
Thanks Richard. The idiot that I am, I forgot to sign the agent when I moved servers a while back. Thanks for the reminder. Fixed now. If you download a copy you need to sign the agent as well. Jake
Reply
Great job, thanks
Hi Jake, I'm using your site for quite a while now - think it's time to give credits to the artist: fantastic site, many thanks for it! Best regards, Benni
Reply
Delete selected
I have been searching for something like this! Thanx!
Another problem thou! If I want to delete the selected document. How can I do that?
/Richard
Reply
Re: Delete selected
To delete them/it have another button called delete which submits the forms but calls another WebQuerySave that uses LotusScript Remove method (or whatver its called).
You might need to read the article called Conditional WebQuerySave agents in the Agents section.
Jake
Reply
Show the rest of this thread
Any idea if this could used along with a text box?
Howdy,
I've implemented this code to accomplish things such as changing field data from one condition to another, select documents that need to be e-mailed as a prompt for updating, send newsletters contaning selected documents via e-mail, etc.
The hurdle I can't seem to overcome with this system is to add a text field to the view where the user could type in, say, a message to be included in the body of an e-mail that is unique t othat document which is being e-mailed. Then the user clicks the javascripted button to send these unique text messages in e-mails for the documents checked. The only one that seems to fire off is the last item checked.
Has anyone found a way around this?
Cheers,
Kelvin
Reply
Re: Any idea if this could used along with a text
The problem here is the way that Domino receives and interprets the field value. With checkboxes, all the values need to be sent in one long list. If you add a text field to the form there's a chance it will split the values and Domino will only receive the last value.
I've talked about this before as a solution to another similar problem:
http://www.codestore.org/store.nsf/cmnts/FE2C6FE0BEF8749986256A810039B39B?OpenDo cument
Jake
Reply
How do you implement a free-text field
You mention that this code could be modified to include a free-text field . Do you have more information about this?
I'd like users to be able to select a document from a view, click on a button, which would prompt them with a dialog box. After filling in the box, the text would get written to a text field in the document.
thanks
Reply
I'm not autorized to access db ?
I'm not authorized to access the database when I try to open it. How come ?
Is your solution also workable with embedded views in a document ?
Johan
Reply
Printing from it...
Hi,
Does anyone know if it would be possible to use this method to print multiple documents from a web view?
Could the agent handle it?
Reply
Processing Multiple docs
Jake, how would I take the selected document and pre-fill fields on a new document, then open and display that new document? I was trying to do the same thing you were doing with an agent but not successful.
Here is my agent code: Can you help?
Sub Initialize Dim session As New NotesSession Dim db As NotesDatabase Dim view As NotesView Dim doc As NotesDocument Dim newdoc As NotesDocument Dim item As NotesItem Dim value As String Set db = session.CurrentDatabase Set view = db.GetView("all") 'Set context = session.DocumentContext Set item = doc.GetFirstItem("processdocs") Forall v In item.values Set doc = db.GetDocumentByUNID( v ) Set newdoc = db.CreateDocument newdoc.Form = "Status" newdoc.Project_Title = doc.Project_Title(0) newdoc.Priority_Number = doc.Priority_Number(0) newdoc.Status_report = doc.Status_report(0) newdoc.Phase_update = doc.Phase_update(0) newdoc.When_get = doc.When(0) newdoc.Phase_get = doc.Phase(0) newdoc.When_update = doc.When_update(0) newdoc.When = doc.When(0) newdoc.Phase = doc.Phase(0) newdoc.Stat_Post = doc.Stat_Post(0) newdoc.SR_Date = Now() 'newdoc.year = Now(year) 'newdoc.month = Now(month) doc.Stat_update = "No" newdoc.Stat_update = "No" newdoc.DocumentNumber = doc.DocumentNumber(0) newdoc.report_date = doc.report_date(0) doc.ParentID = doc.UNID(0) newdoc.NeCom = "y" newdoc.Owner = doc.Owner(0) Call newdoc.Save( True, False) Call doc.Save( True, False) doc.builtflag = "y" End Forall 'Call view.Refresh End Sub
Reply
Re: Processing Multiple docs
You probably need to print the new document's URL back to the browser. Like so:
print "[/" + db.filemame + "/0/"+newdoc.universalid + "?OpenDocument]"
Make sure this is the last line of the agent (after newdoc.save) so that it gets the new doc's right UNID.
Jake
Reply
Only want to see the document
Hi, I want to use the view to open documents not to create new document. Is it possible ? How ? Thanks for your response.
Reply
Appending to a text field from the view
Jake or anyone else
I've been able to get this to change the text field. Two questions:
1. Have you thought of a way to get it to only process on one document?
2. On the text field is there a way to get it to add the typed in text to beginning and to append the previous value to the end.
Here is my situation...The customer wants an easy way to update a comments field on a form. They want a quick comments button on the view so that they can add their comments to an existing document. I will add to their typed in text the date and thier user name but I need the previous comments to be appended to the new comments and placed in the text field. I also don't want them to select more than 1 document from the view.
One last thing I also need to check the .length of the field before writing to it. I actually have 4 comment fields if the first is too large I go to the next -- if its to large on to the next and so forth until the 4th one. If its too large then they have to go an consolodate the comments into a word doc and attach it to the document.
By the way very slick app.
Thanks
Reply
Thank You Jake (about view different of "all")
Thank very much Jake, You have done a great job. Now, I want to use this code with a lot of views instead of the view "all", with only one form "$$ViewTemplateDefault" to display these views. So I put a field "SALUT" in this form which contains a view name and I try to send it to the Processor form by action="Processor?CreateDocument&supp=documents._DominoForm.SALUT.value" . In the Processor form I have a Query_String field with contains "supp=documents._DominoForm.SALUT.value" . How Could I have the value of the field "SALUT" instead of the string "documents._DominoForm.SALUT.value". Or how could I convert this string to have the value of "SALUT". Sorry for my french.
Reply
What's that? HTTP 500: Internal Server Error
sorry for double post because i cant find the edit button.
Jake, I tested this demo on my server. When I clicked on the Publish or Un-publish button, it displays "HTTP 500 Internal Server Error". whats the problem? can help to figure out? the rest of the links work well
Reply
Re: What's that? HTTP 500: Internal Server Error
Sounds like it might be an agent signing problem. Sign the agent and see if that fixes it it.
Jake
Reply
Error validating user's agent execution access
Thanks Jake for the reply, I disabled the "show friendly HTML error" then it displayed error above. I'd searched through Notes Official forum, I've found out mostly replied to ask to set the "RUN AS WEB USER". Then, i followed the instruction but I still failed to get what i want.
This is my ACL: User as Person, Access as Manager. For anonymous ACL, i set as what you set in your dbase. I've checked that i have no authority to sign, run restricted agent (i'm not the member of note admin, only note admin can sign the agent on my server). I have no idea with ACL or authority this thinggy (like signing agent, run restricted agent and etc) cause Domino/Notes is totally new to me. Hope Jake you can give me more clues bout the problem.
Reply
Re: Error validating user's agent execution access
There is another setting you need to be aware of. In the Domino Directory server document there's a field that sets who can run agents and of which kind. Make sure your users can run agents if you select "Run as web user". If run not as web user then make the person who last signed the agent is listed as being able to run them in the server doc.
Reply
Show the rest of this thread
Re:problem if two input type column in view
This code dosnt work good if there are two input type columns in the view.i.e. A checkbox and a input box in same row of the view then above code dosnt work.Is there any solution that only those documents are processed whose checkboxes are checked and at tha same time the value filled in the input boxes is also processed simultaneously.
Reply
Why can't we use Allow selection of documents...
Why can't we use allow selection of documents option for the same purpose.. does it there any issues to use that optioin.
can't we achive the same thing by checking the allow selection of documents option.
I am working on same context.
Please adivse.
Thanks, Kamalesh
Reply
Re: Why can't we use Allow selection of documents...
Hi,
now we are in 2006. this proplem canbe solved with AJAX in domino. So u dont have to post the form document.
1- Just imagine a Main form. In thes form we have a embedded view of its responses. And in the embedded view, we see just related responses. 2- And we have to write a checkbox control. 3- We need a button. In java script code must be written in it. It post to the servlet for all checked documents. (for statement) 5- for AJAX we need a Jcript library. U can write it for once. via XMLHttpRequest, we can do this update without posting. 4- We need e servlet in java. in the servlet we get the 1 doc UNID and changed field value (s).
Thats all .
Reply
Re: Why can't we use Allow selection of documents.
I was able to tweak the code to utilize the $$SelectDoc field. Now you can use any view and the "Allow selection of documents" option.
Basically the action button loops thru the $$SelectDoc values and creates an array of the docunids. It then writes the docunids and desired status field to the hidden HTML fields and submits the form. The rest of the process is relatively the same. The Domino server uses the actual back-end form for the post, executes the WQS agent and $return to refresh the page.
Enjoy!
####PASS-THRU HTML FOR $$VIEWTEMPLATE#### </form><!-- ends the default domino form // --> <form name="processor" method="post" action="BatchStatusChangeFrm?CreateDocument"> <input type="hidden" name="docunids" size="50"> <input type="hidden" value="" name="status"> name="viewname">
/*####### JavaScript Action Button #######*/ function bsc(status) { var cb = document.forms[0].$$SelectDoc; var selectcount = 0; var docUNID = new Array(); if (cb.length) { //has more than one value for(i = 0; i < cb.length; i ++){ if (cb[i].checked){ docUNID[selectcount] = cb[i].value; selectcount++; } } } else { //has only one value if (cb.checked){ docUNID[selectcount] = cb.value; selectcount++; } } if (docUNID[0] != null ){ document.processor.docunids.value = docUNID; document.processor.status.value = status; document.processor.submit(); } }
Reply
Design Idea Please
I have a Quiz application where I need to randomly pick questions from question bank and show it to user on a browser on "One Page" where participant plays quiz and submits.
I was trying HTML tags in notes view for generating quiz paper but actually not sure if this approach is right or there are some better idea Code Store has.
My requirement is all Qustion should be shown in One page! Ideas Please
Reply
Categorised View
Hi
I am using this, and it works great....however by going down the "Treat View Contents as HTML" route, i can't figure how to have categorised columns in the view - is this possible?
Jase
Reply
Thank you for this example, I used it at work. Your example is a little more complex then it needs to be though, you do not need to build the entire $$SelectDoc / checkboxes.
All you really need is a new form "ProcessView" which inherits values from selected documents and you need to end the Domino </form> tag and create a new <form> to post to that new Form "ProcessView"
Reply