Creating a response document via a button - lotus-notes

I have two forms, Organisation and Contact. Contact is a response to Org, and each form has an XPage where the form can be filled in, saved etc. When opening edit_contact.xsp directly and creating a document (not as a response to an org), everything works fine.
On edit_org.xsp, I have a button with 2 events. The first copies some values into sessionScope so I can inherit them into the Contact. The second is a "Create Response Document" event which creates a new response with the parent ID being the current org document, and sends the user to edit_contact.xsp. Pressing the button changes XPage correctly and the field inheritance works fine, but pressing "Submit" on the Contact form doesn't save anything, and no document is created.
This exact same setup works 100% as desired in another database, I have no idea why it won't work properly here. Is there an obscure setting somewhere I am missing?
<xp:button value="Create Contact" id="button1" rendered="#{javascript:!document1.isEditable()}">
<xp:eventHandler event="onclick" submit="true" refreshMode="complete">
<xp:this.action>
<xp:actionGroup>
<xp:executeScript>
<xp:this.script>
<![CDATA[#{javascript:var doc = document1.getDocument();
sessionScope.PFirstName = doc.getFirstItem("P_Firstname").getValueString();
sessionScope.PSurname = doc.getFirstItem("P_Surname").getValueString();
sessionScope.PFamily = doc.getFirstItem("P_Family").getValueString();
sessionScope.PDOB = doc.getFirstItem("P_DOB")
sessionScope.PAGE = doc.getFirstItem("P_Age").getValueString();}]]
</xp:this.script>
</xp:executeScript>
<xp:createResponse name="/edit_contact.xsp" parentId="#{javascript:document1.getNoteID()}">
</xp:createResponse>
</xp:actionGroup>
</xp:this.action>
</xp:eventHandler>
</xp:button>`
Here is a link that shows what I am trying to do (minus the field inheritance):
http://min.us/mKSJED8tT
Currently the forms and views all work, but the document created with the "Response" form appears not to be a response document - it has no $REF field. This setup works perfectly in a different database - what is going on?

It's hard to tell what might be happening without seeing any code. Since it works for you in another database, could it be an ACL issue? Where the user you're logging in as - possibly - anonymous - doesn't have the ability to create documents?

Instead of "push" approach go for "pull" - just open response page with url parameter of parent document. In its postNewDocument event initialize field values from it.

It would be useful to get an update on two key points made by others:
Is ignoreRequestParams set on your response document datasource? If not, regardless of what you're trying to define for the second datasource, it's UNID etc are pulled from the request parameters. So both datasources are effectively the same datasource.
Is it throwing a validation error? If so, nothing will be saved.

There are two possible issues:
First the use of getFirstItem("x") is not a best practice. So:
sessionScope.PDOB = doc.getFirstItem("P_DOB")
would be storing a NotesItem in the sessionScope which will not work. It is recommended to use:
sessionScope.PDOB = doc.getItemValueString("P_DOB");
Second the use of getNoteID() might not be returning what you want (Which is the UNID of the document). Use .getDocument().getUniversalID() instead.
<xp:createResponse
name="/edit_contact.xsp"
parentId="#{javascript:document1.getDocument().getUniversalID()}">
</xp:createResponse>
-edited-
/Newbs

Related

XPages: ask a confirmation when saving and set a value if confirmed

I have a document that gets published using an xagent (that does all sorts of things to the document).
Before sending to the xagent, I would like to ask the user if he wants the effective date of the document to be set to today's date. For now, I don't have that field available in edit mode on the page, but I guess I'll need it.
The big question is how to ask a confirmation (do you want the date to be set to today?) and put the date in the field before actually saving the document and sending it to the xagent page. I already have some simple actions into that save button. Here is the code:
<xp:button value="Save and Publish" id="button6">
<xp:this.rendered><![CDATA[#{javascript:database.queryAccessRoles(session.getEffectiveUserName()).contains('[Admin]') && currentDocument.isEditable()}]]></xp:this.rendered>
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action>
<xp:actionGroup>
<xp:modifyField name="Status" var="pageDocument">
<xp:this.value><![CDATA[#{javascript:if(getComponent("publishLater1").getValue() == "1") {
return "Scheduled Publication";
} else {
return "To Be Published";
}}]]></xp:this.value>
</xp:modifyField>
<xp:saveDocument var="pageDocument">
</xp:saveDocument>
<xp:executeScript>
<xp:this.script><![CDATA[#{javascript: //remove the lock doc
//unlockDoc(pageDocument.getDocument().getUniversalID());
//for scheduled publications, a LotusScript agent will do the work
var res=facesContext.getExternalContext().getResponse();
if(getComponent("publishLater1").getValue() == "0") {
// Now load the publish Agent
res.sendRedirect(#Left(facesContext.getExternalContext().getRequestContextPath(),".nsf")+".nsf/xPublish?OpenAgent&docid=" + pageDocument.getDocument().getUniversalID());
} else {
//send to the drafts view, to show it has the clock icon in the draft view
res.sendRedirect(#Left(facesContext.getExternalContext().getRequestContextPath(),".nsf")+".nsf/adminDrafts.xsp");
} }]]></xp:this.script>
</xp:executeScript>
</xp:actionGroup>
</xp:this.action>
</xp:eventHandler>
<i class="fa fa-newspaper-o pull-left fa-2x">
</i>
</xp:button>
You could do it several ways I'd think. If you're using the extension library you could use a dialogBox. so your save and publish button opens a dialog box with your question or even additional fields. Then you add a cancel button of course to the dialog box but also a "continue" button. That button accesses the fields if you put any in or knows that they want "today's" date and then that button calls the xagent passing in any appropriate parameters.
Presumably pageDocument is a dominoDocument datasource. A dominoDocument datasource is either all read only or all editable. And SSJS has access to that datasource. So add another executeScript action and you can modify whichever other field you want to as well.
What I'd recommend, though, is to skip the simple actions and do everything in script. The SSJS editor allows you to see all the methods available for a dominoDocument datasource. With a little knowledge of LotusScript or a little investigation, it should be obvious which method to use to replace an item value for the "Modify Field" simple action (quick tip, again go to the datasource rather than the publishLater1 component) and which method to save the document. If you start breaking away from simple actions and building your confidence in SSJS, it will give you greater flexibility in the long run.

dataContext binding to data

I am trying to understand the dataContext better and tried creating a dataContext referencing a document.
<xp:this.dataContexts>
<xp:dataContext var="doc1">
<xp:this.value>
<![CDATA[#{javascript:
var db:NotesDatabase = sessionAsSigner.getDatabase("","privDb.nsf");
var adoc:NotesDocument = db.createDocument();
return adoc }]]>
</xp:this.value>
</xp:dataContext>
</xp:this.dataContexts>
I then tried using EL and javascript to bind fields on my xpage to the dataContext
<xp:inputText id="inputText2" value="#{doc1.lastname}"></xp:inputText>
<xp:inputText id="inputText1" value="${javascript:doc1.firstname}"></xp:inputText>
But when I save, it does't save anything.
<xp:button value="save" id="button1">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:
print(doc1.getClass().getName() )
doc1.save();
}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
The print command showing me the class name is showing up as lotus.domino.local.Document
The document is saved to the database, but it has no values other than $UpdatedBy. I can't seem to bind fields to the edit boxes.
The reason I am going down this path is twofold, 1. I want to use sessionAsSigner so I can keep the database security on the remote db (privDb.nsf) at No Access for anonymous and default, and 2. I want to learn a little more about dataContext, data sources and binding. I have read the "easy" way of using a Public Document, using the $PublicAccess field, etc, which is the "Old School" Notes way, and yes, I could do it that way, but want to understand how to do it using dataContexts, if possible.
A dataContext is basically a scoped variable, scoped lower than viewScope but higher than requestScope, scoped to a component. That component can be an XPage, a Custom Control or a Panel (yes, dataContexts can be added to a Panel too).
Like other scoped variables on the page, a simple save action does not save dataContexts. If you want a variable that's create-able and save-able, that's the Data Object. That has specific createObject and saveObject properties, where you define what should happen when they're called by the XPages runtime.
Similarly, like the other scoped variables, it needs to be serialized, so you cannot store a Domino object in them. So you can't store a NotesDocument in them. You need to wrap a normal Java object around a NotesDocument. With a greater understanding of XPages it becomes apparent that's what the dominoDocument datasource is doing (creating properties for all fields on the document, storing its note id, UNID, adding other properties like whether it's in edit mode or new etc).
A final point, as Jesse says, dataContexts are re-evaluated multiple times during a partial refresh. I haven't re-tested recently, but under 8.5.3 dataContexts bound to an XPage or Custom Control were re-evaluated more than dataContexts bound to a Panel, so I'd recommend the latter.
The immediate problem that you're running into is that #{}-bound dataContexts are re-evaluated constantly, several times during a page load and, I believe, every time they're referenced. Generally, the rule of thumb with dataContexts (and don't get me wrong - I like them) is that they should either be extremely low-cost, like a quick mathematical calculation, or be ${}-bound. The latter wouldn't work here, though, since the document wouldn't survive past the first load.
The tack you may want to try is to use a dataContext like this:
<xp:dataContext var="docData" value="${javascript: new java.util.HashMap() }" />
Basically, using a simple object as a holding pen. Then, in the save action, create the new document and set all of the values from "docData" there, like:
var db = sessionAsSigner.getDatabase("", "privDb.nsf");
var doc = db.createDocument();
doc.replaceItemValue("firstname", docData.get("firstname"));
doc.replaceItemValue("lastname", docData.get("lastname"));
doc.save();
There are a few caveats to this approach:
Each save will create a new document, rather than editing the existing one. You could change this by storing the UNID back into the map and fetching it from the DB, though
It should work as-is in this case, but you'll have to be mindful of data types. For example, if you have a multi-value control, then the XPages runtime will probably create an ArrayList, which you would have to convert to a Vector for storage unless you're using the OpenNTF Domino API
And as a final note, that binding you have for firstname is almost definitely not what you want. That may just be an artifact of the testing you were doing, but I'd be remiss if I didn't mention it.

How can I refresh the field in CSJS

How to refresh one field in csjs? the code is here:
<xp:repeat id="repeat1" rows="30" var="currentDetail" indexVar="detailIndex" value="#{LeaveBean.details}">
<xp:inputText id="leavefrom" value="#{currentDetail.subfromtime}">
<xp:eventHandler event="onblur" submit="false" refreshMode="partial" refreshId="repeat1">
<xp:this.script><![CDATA[
XSP.partialRefreshPost('#{id:view:_id1:repeat1:0:leavefrom}');
var detailIndex = document.getElementById("#{id:detailIndexText}").innerHTML;
myRPC.checkfromdate(detailIndex).addCallback(function(returnAlert){alert(returnAlert);});
]]></xp:this.script>
</xp:eventHandler>
</xp:inputText>
</xp:repeat>
what I want to do is call ssjs from csjs as you can see, but the strange thing to me is the click thing didn't work immediately, for example, when I first open the page, and click the input field, and enter "1" in it, it should be popup alert just when i left the field, but nothing happend, then I click this field again, and enter "2", then I left the field, but I just got the alert "1" on the secreen...sorry for my poor expressions...
so my question is I should refresh the field just when I left the field immediately, but why the XSP.partialRefreshPost not working? I tried to refresh repeat1, not working either
update 2015/06/13:
At last, I found view.postScript....so....
One obvious problem is the partialRefreshPost id won't work. You're combining #{id:...} which runs server-side to calculate the corresponding client-side ID. But the ID that you're passing is a client-side ID for the first row of the repeat. Even if that corresponded to a server-side component, you're trying to hard-code every row to map to a field in row 1 of the repeat ("repeat1:0").
Is there a reason for not using a normal partial refresh and using view.postScript in your SSJS to post CSJS back to the browser? You're triggering a partial refresh posting data back anyway. I'm not sure what the final line of your code is doing, but it seems like it's calling another server-side request to add a response. So the code seems to do two calls to the server, to trigger an alert. I don't know the design and what that subsequent rpc call is doing, but if it can be achieved in a single partial refresh, that would seem better.

XPages: Wait for backend document to be saved and reload

In one of my XPages application (for Client, XPiNC) I have a link to "Edit" an attachment of this document. I use the technology described in this blog article to get the attachment edited.
In that scenario I have three windows interacting:
The XPage (XPiNC) containing the document where the link is clicked
The Notes Document (called by notes:// URL) containing the attachment
The application window containing the launched attachment (e.G. MS Word).
I managed to let the notes document (2) wait for the application (3) to finish and upload the modified attachment.
Now I need the XPage to wait for the backend change to occur and then to reload the XSP Document from the backend document to prohibit save conflicts.
I think the easiest way would be to pull out the attachments to attachment documents that are responses to the current document, but this is a rather old application that has large amounts of data in many many databases, and it is not feasible to convert all of these existing documents for that.
Now I search for the second best way to do it.
So again the question: How can I make the XPage "wait" for a backend change to occur and then reload the current page.
Second: How do I best "visualize" to the user, that something is running in the backend (Spinner, fade out, etc.).
The servers are 9.0.1FP2, the Clients unfortunately are 8.5.3 (cannot be updated at the moment).
Here is the Code, that I use in my "Open" Link:
<xp:link escape="true" text="Edit" id="attachmentEdit">
<xp:eventHandler event="onclick"
submit="true" refreshMode="partial" refreshId="tableAttachment">
<xp:this.action><![CDATA[#{javascript:var strAttachmentName:String = docs.toString();
var strReplid = database.getReplicaID();
var strUnid = document1.getDocument().getUniversalID();
var strServer = #Name( "[CN]" , database.getServer() );
var strUrl:String = "notes://" + strServer + "/"+strReplid+"/(ShowAttachment)?OpenForm";
session.setEnvironmentVar("InfothekCurrentAction","EDIT~" + strUnid + "~" + strAttachmentName);
view.postScript('window.location.href="'+strUrl+'"');}]]>
</xp:this.action>
</xp:eventHandler>
</xp:link>
I am using an xagent for that. Thats what i do:
1) I am setting the background of a div to a moving css gradient.
2) Set the src of the div to an xagent that is doing some things in the backend
3) The xagent is calling some csjs after its loaded(in your case, you could call a button to refresh the page)
Take a look at this proof-of-concept demo: http://dontpanic82.blogspot.sk/2010/01/xpages-custom-control-that-can-help.html
Simple CSJS chcecks for updated property of the document. In case it was modified (attachment saved), you can show pop up or just reload the XPage.

learning how to use xe:dominoViewEntriesTreeNode

I realized my question was too vague on adding navigation items dynamically, so I am rewriting the question.
I have discovered the xe:dominoViewEntriesTreeNode control from the xpages. I think I can use this to add navigation items to the navigator control based on entries in the view.
I am struggling to find very much in the way of documentation or resources that break down how to do that. Can anybody to me to a good reference or example code?
You can use dominoViewListTreeNode to build a menu based on views in a database (and not documents in those views).
Here is an example of using xe:dominoViewListTreeNode to dynamically build a menu based on all views called "Test*" (using regex in the filter property). When selecting a menu item from the menu, the name of the view is submitted to the server (using EL notation for the viewEntry.getName() method).
The example also contains an onItemClick event handler that "catches" the name of the view as the submitted value and stores this in a sessionScope variable. The event handler then redirects to a views.xsp XPage that could contain a Dynamic View Panel control where you could use the sessionScope variable to control what view to show.
The sessionScope variable is also used to mark the selected menu item as "selected".
<xe:navigator id="navigator1">
<xe:this.treeNodes>
<xe:dominoViewListTreeNode filter="Test.*" submitValue="#{viewEntry.name}" var="viewEntry">
<xe:this.selected><![CDATA[#{javascript:viewEntry.getName() == sessionScope.clickedView}]]></xe:this.selected>
</xe:dominoViewListTreeNode>
</xe:this.treeNodes>
<xp:eventHandler event="onItemClick" submit="true" refreshMode="complete">
<xp:this.action>
<![CDATA[#{javascript:sessionScope.clickedView = context.getSubmittedValue();
context.redirectToPage("views.xsp");}]]>
</xp:this.action>
</xp:eventHandler>
</xe:navigator>
Instead of the onItemClick method to redirect to an XPage, you could compute the href property of xe:dominoViewListTreeNode to return the name of an XPage.
I have a short presentation called "XPages Extension Library - Create an app in 1 hour (almost)" that presents this technique (and other techniques).
I assume you have an area on your page carrying the navigation items, e.g. links to some pages with link texts?
I would then use a repeat control with a datasource/javascript source that returns the document item values from your profile document or something.
If you are not into repeat controls then you should consider to read this: http://xpageswiki.com/web/youatnotes/wiki-xpages.nsf/dx/Work_with_repeat_controls
On this page there is also a sample dealing with a profile document.
By the way: using profile documents was always a crutch, so consider to youse "normal" config documents instead.

Resources