XPages Domino Document datasource and documentid : how to trap error? - xpages

When we declare a dominoDocument as an XPages datasource, we can specify the documentid programmaticaly. However, I've not found a way to trap the error if the specfied id does not exist. I get an error 500 / Could not open document error on the log.
I would expext to get a null "document1" or something but be able to catch error nicely.
<xp:this.data>
<xp:dominoDocument var="document1" action="openDocument" documentId="some noteId here" formName="Document" ignoreRequestParams="true">
<xp:this.databaseName>...</xp:this.databaseName>
</xp:dominoDocument>
</xp:this.data>
Any hint ?
thanks

You can put the error handling in your code for calculating the documentid.
<xp:this.documentId><![CDATA[#{javascript:
var id = "your calculated id";
try {
database.getDocumentByUNID(id);
} catch(e) {
context.redirectToPage("pageError", true);
}
return id}]]>
</xp:this.documentId>
Like in example above you can open e.g. an error page.

Related

documentId resolving code doesn't get executed XPages

In my XPage I need to set a data source (Domino document)
I try to do it as follows:
<xp:this.data>
<xp:dominoDocument var="requestDocument" action="openDocument" databaseName="#{javascript: print('db ok'); return database.getFilePath();}"
documentId="#{javascript:
print('heloooo');
var conclusion = database.getDocumentByUNID(doc_source.getDocument().getParentDocumentUNID());
var oConclusion = new OsnovaUI_document(conclusion);
var requestDoc = oConclusion.getMainDocument();
print('docID: ' + requestDoc.getUniversalID());
return requestDoc.getUniversalID();
}">
</xp:dominoDocument>
</xp:this.data>
The thing I've noticed is that code section in documentId doesn't get executed. At all. That's why I've put heloooo in there. However, the databaseName works as expected. In console I always see
09.03.2020 00:52:11 HTTP JVM: db ok
But not heloooo :(
What am I doing wrong? Thanks in advance
The most likely cause is ignoreRequestParams is not set to true. Unless you set that, the data source is retrieving all specifics about which document to edit based on the URL query string parameters (the HTTP request parameters). As a result, the URL query string parameters take precedence, and in the case of nothing being set, that means "use a new document". If you've defined that the URL query string parameters should take precedence, running your code to merely ignore it afterwards is inefficient. As a result, action="openDocument" is also being ignored - if you have a docId in the query string it will open that, otherwise it will create a new document.
documentId can only get processed once, when the page first loads. Depending on whether the datasource is bound to a panel or an XPage / Custom Control, it will run before the beforePageLoad event as well. So runtime binding (#{javascript:...) has no effect. ${javascript:... will avoid confusion.
Error handling may help identify if there is an error. XPages OpenLog Logger is one of the most pervasive (disclaimer, I'm the author) https://openntf.org/main.nsf/project.xsp?r=project/XPages%20OpenLog%20Logger.
Change documentId to be computed on page load ($) and not dynamically (#):
<xp:this.data>
<xp:dominoDocument var="requestDocument" action="openDocument">
<xp:this.documentId><![CDATA[${javascript:
var conclusion = database.getDocumentByUNID(doc_source.getDocument().getParentDocumentUNID());
var oConclusion = new OsnovaUI_document(conclusion);
var requestDoc = oConclusion.getMainDocument();
print('docID: ' + requestDoc.getUniversalID());
return requestDoc.getUniversalID();
}]]></xp:this.documentId>
</xp:dominoDocument>
</xp:this.data>
The databaseName is not required if the database is itself.

ssjs to save multiple documents

With the onclick event of a button I would like to save multiple documents, but only the last one gets saved.
<xp:this.data>
<xp:dominoDocument var="document1" formName="tg"></xp:dominoDocument>
</xp:this.data>
and in the onclick event of the button :
...
while (re.next()) {
document1.replaceItemValue("TGARKD",tgarkd);
document1.replaceItemValue("TGKDOM",tgkdom);
document1.replaceItemValue("TGARGR",tgargr);
document1.replaceItemValue("TGDLGR",tgdlgr);
document1.save();
}
If you want to create multiple Documents in the NSF, you'll need to use backend classes. The DominoDocument datasource is tied to a single backend document. var doc = document1.getDocument(true) will get a handle on the (first) backend document, then in your loop use
var doc2 = database.createDocument();
doc.copyAllItems(doc2, true);
doc2.save(true, false);
Alternatively you could define your data source inside a repeat control and bind the fields to that one. Then outside the repeat you call save() which saves all data sources

Select2 in Xpages Cannot add new value: Error is "Validation Error: value is not valid"

I am using Select2 (4.0) in an XPage. I have a ComboBox whose value is tied to a property in a bean, and the select items are populated (for now) by a viewScope variable I populate in before page load.
The user is able to type in a new value, but when I go to save the data I am getting a validation error:
This is not a validation error that I wrote but from Xpages. Although I am able to enter a new value ("Apple") when I save the page, it displays this error and removes my new value.
I believe this has something to do with the fact that the new value is not in the list of possible values, but I am not sure.
I found other posts that suggested that I have to add the new value to the list of values before submitting the page. I tried this but it didn't work either.
I really like select2 and want to use it for all comboBoxes and ListBoxes, but there are many times where the use needs to enter new values.
Code for the comboBox:
<xp:comboBox id="model"
value="#{javascript:PCModel.model}" xp:key="field">
<xp:selectItems>
<xp:this.value><![CDATA[#{javascript:viewScope.models}]]></xp:this.value>
</xp:selectItems>
</xp:comboBox>
And my script block:
<xp:scriptBlock id="scriptBlock1">
<xp:this.value><![CDATA[$(document).ready(
function()
{
x$("#{id:model}").select2({
tags: true,
createTag: function (params) {
return {
id: params.term,
text: params.term,
newOption: true
}
;},
templateResult: function (data) {
var $result = $("<span></span>");
$result.text(data.text);
if (data.newOption) {
$result.append(" <em>(new)</em>");
}
return $result;
}
}
)
}
)]]></xp:this.value>
</xp:scriptBlock>

xpages Trying to get document parentdoc is returning null error

I am trying to get the parent document of a new response document so I can duplicate the functionality of form inheritance in xpages. The following is my code and the error being returned:
Error while executing JavaScript action expression
Script interpreter error, line=3, col=60: 'parentDoc' is null
JavaScript code
1: if (document2.isNewNote()) {
2: var parentDoc:NotesDocument = database.getDocumentByID(document2.getParentId());
3: getComponent("immediateParentSubject1").setValue(parentDoc.getItemValueString("Subject"));
4: }
I usually use a dataContext and getParentDocumentUNID() when I need a handle on the parent document for the variable "document". You can use this for a new document (not saved yet):
<xp:this.dataContexts>
<xp:dataContext var="parentDoc">
<xp:this.value><![CDATA[#{javascript:
try {
if (document.isResponse()) {
return database.getDocumentByUNID(document.getDocument().getParentDocumentUNID());
} else {
return "";
}
} catch(e) {
return "";
}}]]></xp:this.value>
</xp:dataContext>
</xp:this.dataContexts>
You can then use parentDoc in other controls and do parentDoc.getItemValueString("Subject") etc.
datasource.getParentId() does not return the NoteID as you might expect. It returns the UnID and that's why you need to use database.getDocumentByUNID as Per is doing.
Another way is to get the parent UnID from the URL:
param.get("parentId")
Consider also looking up the parent subject whenever a child is opened instead. That way it is stored only in one place which is always a good thing.

Can anyone give me example how to create new IBM Connections Activity using xPages Social Enabler?

Can anyone give me an example how to create new IBM Connections Activity using xPages Social Enabler? I cant find any usefull info in documentation so I have adapted an example from Niklas Heidloff on how to create a new bookmark in Connections. I have the following code for creating a new activity:
try {
var svc = new sbt.ConnectionsService("/activities/service/atom2/activities");
var sb = new java.lang.StringBuilder();
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
sb.append("<entry xmlns:snx=\"http://www.ibm.com/xmlns/prod/sn\" xmlns:opensearch=\"http://a9.com/-/spec/opensearch/1.1/\" xmlns:thr=\"http://purl.org/syndication/thread/1.0\" xmlns=\"http://www.w3.org/2005/Atom\">");
sb.append("<title type=\"text\">");
sb.append("test activity from xpages");
sb.append("</title>");
sb.append("<content type=\"html\">");
sb.append("</content>");
sb.append("</entry>");
var msg = svc.post(null, sb.toString(), "xml");
} catch(e) {
print(e)
}
But code above do not create anything but raises error on Domino console. This is returned by svc.post() command:
[31726:00075-3041917840] 11/19/2012 01:03:59 PM HTTP JVM: Client service request to: http://vhost1279.site1.compute.ihost.com:81/activities/service/atom2/activities did not return OK status. Status returned: 415, reason: Unsupported Media Type, expected:information, please consult error-l
[31726:00075-3041917840] 11/19/2012 01:03:59 PM HTTP JVM: g-0.xml located in /local/opt/ibm/lotus/notesdata/domino/workspace/logs
[31726:00075-3041917840] 11/19/2012 01:03:59 PM HTTP JVM: com.ibm.xsp.extlib.sbt.services.client.ClientServicesException: HTTP Status 415, Unsupported Media Type. HTTP error response code received in response to request to url: http://vhost1279.site1.comties/service/atom2/activities
Can anyone give me a hint how to use it properly or point me to some usefull documentation?
Don't use a StringBuilder to create XML. At least use SAX or better Apache Abdera for creating XML (Tutorial here). This ensures that your XML is valid and in case of Abdera also valid ATOM.
It is important to use this approach, since you get a Node object in return, that automatically triggers the needed content type.
Then check how to create an Activity in the Connections documentation wiki (yes - confusing). In this article you find the code to retrieve an activity - I actually recommend to use CURL to get the valid format as an example. Some CURL URLs are here. The closest you get to a full example is Luis' demo of a status update.
To explore connections I use the following batch file:
set server=[server]
set HOME=c:\work
curl %server%%1 –-netrc -G --basic -k -v -L -o %2 %3 %4 %5 %6 %7
with an .netrc file (see the CURL documentation)
machine [server] login [user] password [password]
This is the XML format you need for an activity:
<?xml version="1.0" encoding="utf-8"?>
<entry xmlns="http://www.w3.org/2005/Atom">
<category scheme="http://www.ibm.com/xmlns/prod/sn/type" term="activity" label="Activity"/>
<title type="text">Posted activity</title>
<content type="html">
This is an activity that has been automatically uploaded from the cURL command line
</content>
</entry>
And post it like this:
post activities/service/atom2/activities newactivity.xml activityresult.xml
Open the activityresult.xml and locate ocate the href attribute of the app:collection element - you need it to add actions. Use the following XML:
<?xml version="1.0" encoding="utf-8"?>
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:snx="http://www.ibm.com/xmlns/prod/sn">
<category scheme="http://www.ibm.com/xmlns/prod/sn/type" term="todo"/>
<category term="Connection4.0"/>
<category term="Test"/>
<title type="text">Some things that need to be done</title>
<content type="html">
This is an <b>action</b> in an activity that has been automatically uploaded from the cURL command line.
</content>
<snx:assignedto>noreply#ibm.com</snx:assignedto>
</entry>
and this command:
post [the-url-you-found-above] newaction.xml actionresult.xml
Once the CURL version works you can try yourself using Abdera code.
Here is a working sample from REST client in Firefox:
https://vhost1279.site1.compute.ihost.com/activities/service/atom2/activities
Header: Content-Type application/atom+xml
<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns:snx="http://www.ibm.com/xmlns/prod/sn" xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns="http://www.w3.org/2005/Atom">
<category scheme="http://www.ibm.com/xmlns/prod/sn/type" term="activity" label="Activity" />
<content type="html"/>
<title type="text">
test
</title>
</entry>
The problem with the code above is that you pass in a String to the post method. That however doesn't set the right content type. Please use APIs Stephan suggests to create a org.w3c.dom.Node with the XML and pass this in instead. This will set automatically the right content type in the header.
SOLVED!!! I looked to source and problem was obvious. This is rather a bug or at least misconception but can be easily resolved . According the docs and my testing proves this Connections requires the following header in request: Content-Type = application/atom+xml .... but in source code of Social Enabler I found these two related methods:
protected void prepareRequest(HttpClient httpClient, HttpRequestBase httpRequestBase, Options options) throws ClientServicesException {
// TODO: add support for gzip content
//httpClient.addRequestHeader("Accept-Encoding", "gzip");
if(options.getHeaders()!=null) {
addHeaders(httpClient, httpRequestBase, options);
}
if (options.content != null) {
String contentType = null;
HttpEntity entity = null;
Object content = options.content;
try {
//If a subclass overrides com.ibm.xsp.extlib.services.client.Service.processRequestContent(HttpRequestBase, Object, Options)
//the the subclass must set the content type of the request, and also set the request's entity!
if(processRequestContent(httpClient, httpRequestBase, options)){
if (content instanceof IValue) {
JsonFactory jsFactory = new JsonJavaScriptFactory(DesignerRuntime.getJSContext());
entity = new StringEntity(JsonGenerator.toJson(jsFactory, content, true));
contentType = "application/json";
}
else if (content instanceof JsonObject) {
JsonFactory jsFactory = JsonJavaFactory.instanceEx;
entity = new StringEntity(JsonGenerator.toJson(jsFactory, content, true));
contentType = "application/json";
}
else if (content instanceof Node) {
entity = new StringEntity(DOMUtil.getXMLString((Node) content, true));
contentType = "application/xml";
}
else {
entity = new StringEntity(content.toString());
contentType = findRequestTextContentType(options);
}
}
} catch (Exception ex) {
if(ex instanceof ClientServicesException) {
throw (ClientServicesException)ex;
}
throw new ClientServicesException(ex, "Error while parsing request content");
}
if (entity != null && (httpRequestBase instanceof HttpEntityEnclosingRequestBase)) {
httpRequestBase.setHeader("Content-type", contentType);
((HttpEntityEnclosingRequestBase) httpRequestBase).setEntity(entity);
}
}
}
protected String findRequestTextContentType(Options options) {
return "text/plain";
}
As you can see there is no such header (application/atom+xml) for any case. But if you provide XML content as string, the code uses the 'findRequestTextContentType' method to return a default content type, which is 'text/plain' that is not correct for our situation. It is hardcoded so there is no way how to setup the default encoding. But, at least, the 'findRequestTextContentType' is type protected so it can be overriden. So I have created my own ConnectionsService class that extends the former one and overrides the findRequestTextContentType method to return correct content type for my case. And this works fine and resolved the problem !!
import sbt.ConnectionsService;
public class ConnectionsServiceCustom extends ConnectionsService {
public ConnectionsServiceTcl(String serviceUrl) {
super(serviceUrl);
// TODO Auto-generated constructor stub
}
#Override
protected String findRequestTextContentType(Options options) {
return "application/atom+xml";
}
}
As Niklas and Stephen point out you need to use a Dom object (Node, Document etc).. If you are getting an error when creating such an object then it is most likely because the contents of the document/node is poorly formatted or incorrect..
There is a built in XPages util class that allows you to create documents from strings
com.ibm.commons.xml.DOMUtil
Check out
com.ibm.commons.xml.DOMUtil.createDocument(String, String)
e.g.
com.ibm.commons.xml.DOMUtil.createDocument("my xml string", null);
The first parameter is the contents of the XML document, the second is the format.
This class provides several utility methods for parsing and constructing DOM documents.

Resources