Exception thrown when deleting document in XPages via doc.remove() method - xpages

I added a Delete link in an xp:repeat to delete the document for the row, with "rowVar" the variable for the repeat to access thye document entries.
Here is the code for the Delete link in the repeat:
<xp:link escape="true" id="link2"
style="width:50.0px;display:inline-block" text="Delete">
<xp:eventHandler event="onclick" submit="true"
refreshMode="partial" refreshId="panelChemLog" immediate="true">
<xp:this.action><![CDATA[#{javascript:
var deleteUNID = rowVar.getUniversalID();
println(deleteUNID);
var doc:NotesDocument = database.getDocumentByUNID(deleteUNID);
println(doc.getCreated() );
doc.remove(true);
}]]></xp:this.action>
</xp:eventHandler>
</xp:link>
I included the println statements and I can see in my log that the deleteUNID and doc object is set, but the code throws this exception on the final doc.remove(true) statement:
Script interpreter error, line=5, col=5: [TypeError] Exception occurred calling method NotesDocument.remove(boolean) null
How do I code the Delete link to delete the document for the row in the repeat?

I've seen this error before several times. Just tested to be sure. If the user does not have deletion privilege, it will throw such a meaningless error.
If you look at the Java stack trace, you can confirm by the following at the bottom of the trace.
NotesException: Notes error: You are not authorized to perform that
operation
You might use database.queryAccessPrivileges(...) method to be sure within the code.

Related

XPages appendDocLink on Anonymous User Error

I am attempting to replace my 'traditional Notes' User Name and Password Request form with one designed in XPages, mainly because the #SendMail Formula no longer works with Firefox or Chrome.
Anonymous users are to complete an XPage Form, hit a submit button which then sends an email to our support team, which is to include a Doc Link back to an internal Notes form of the same document, - I don't want to change this internal form as there is lots of lotusscript programming associated with it for processing.
I have the XPage marked as Available to Public Access as is the form associated with it. I also have ComputeWithForm set to both.
I can access the data document components e.g. append to the mail rtf body using doc.getItemValueString("field"), but I cannot do the same with appendDocLink it appears to fail due to access problems. If I set Anonymous to Manager access in the ACL the Doc link is appended.
<xp:button value="Submit" id="button1" style="margin-top:50.0px">
<xp:eventHandler event="onclick" submit="true" refreshMode="complete" immediate="false" save="false" id="eventHandler1">
<xp:this.action>
<xp:actionGroup>
<xp:saveDocument var="document1"></xp:saveDocument>
<xp:executeScript>
<xp:this.script><![CDATA[#{javascript: if(document1.isNewNote()){document1.save();}
var doc:NotesDocument = document1.getDocument(true);
var receiverEmail = "Support Requests";
var requestor = "xyz#company.co.uk";
db = sessionAsSignerWithFullAccess.getCurrentDatabase();
var memo = db.createDocument()
memo.appendItemValue("Form","Memo");
memo.appendItemValue("Principal","Company#NotesDomain");
memo.appendItemValue("From",requestor);
memo.appendItemValue("INetFrom",requestor);
memo.appendItemValue("DisplaySent",requestor);
memo.appendItemValue("SMTPOriginator",requestor);
memo.appendItemValue("Subject","NEW: Name & Password Request for "+getComponent("reqCompany1").getValue());
var rtitem:NotesRichTextItem = memo.createRichTextItem("Body");
rtitem.addNewLine();
rtitem.appendText("A new Name & Password Request has been logged by "+getComponent("reqFirstName1").getValue()+" " + getComponent("reqSecondName1").getValue()+" of "+ getComponent("reqCompany1").getValue());
rtitem.addNewLine();
rtitem.appendText("Click the Doc link to action it. ");
rtitem.appendDocLink(doc);
memo.send(receiverEmail);}]]></xp:this.script>
</xp:executeScript>
<xp:openPage name="/UserNandPSubmitted.xsp"></xp:openPage>
</xp:actionGroup>
</xp:this.action>
</xp:eventHandler>
</xp:button>
The following errors occur.
com.ibm.jscript.InterpretException: Script interpreter error, line=25, col=8: [TypeError] Exception occurred calling method NotesRichTextItem.appendDocLink(lotus.domino.local.Document)
null
NotesException: Notes error: You are not authorized to perform that operation
lotus.domino.local.RichTextItem.appendDocLink(Unknown Source)
lotus.domino.local.RichTextItem.appendDocLink(Unknown Source)
Any help would be greatly appreciated.
Make sure Anonymous user can access the default view of the database - so make it accessible to public access users. appendDocLink() method requires this view in order to append doc link to RT item.

How to perform multiple page updates behind a single button in XPages

This is a high level question but does have specific answers.
I have a requirement where I need to perform an action two times, and update the user with a message before trying the action a second time. I also want to update the user prior to the first action.
Here is the timeline/sequence of events:
User presses button
Display message "operation starting"
Perform operation
If successful, show "operation successful", if operation fails
show "operation failed - retrying"
Retry Operation
If successful, show "operation successful", if operation fails show "please try again later".
When I coded this in my java method, the only thing that displayed was the final message. I tried using Thread.sleep(1500); to give the message a chance to display, but ONLY the final message will ever be displayed.
I then tried using SSJS to accomplish this task, but the result is the same. I think I understand why this is happening.
My question is: Is it even possible to do multiple partial refreshes like this. What is the best approach that I can take to accomplish this requirement. What workarounds or hacks can anyone think of.
If you are wanting to just try twice, you could create an rpc control that does the functionality you want, then call it with a call back
var deferred = service.doSomethingCool();
deferred.addCallback(function(data) {
if (data.status == 'error') {
//set UI Label Fail
var insideDeferred = service.doSomethingCool();
insideDeferred.addCallback(function(data) {
if (data.status == 'error') {
//set UI Label Fail
}
else {
//set UI Label Success
}
}
}
}
This should work for you assuming you have an RPC control named service and method called "doSomethingCool"
I tried Mark's post for a progress bar once. http://linqed.eu/?p=174 Didn't get it to work but didn't go deep into the effort. The solution is probably in there.
What you want should be doable of course. I would think at the least you use CSJS to do the heavy lifting really. Have CSJS call SSJS probably via RPC or XAgent and that does all the work of first and secondary message.
I'm just light on the actual details of making that work. :)
Yes, it's possible. We can do back and forth client side and server side operations n number of times in a single button click.
Create a button with CSJS code for alert.
Write SSJS code for your operation.
Now the trick is depends on whether your operation is partial or complete refresh.
If partial refresh, use oncomplete / onfailure event in eventhandler to write CSJS code to perform button click operation. Use a hidden input field to store number of retry and to determine at what count the retry has to stop.
If full refresh, oncomplete event does not triggered. Write your CSJS code on clientload event. In this case, Create a hidden input field and bind it to a view scope variable. Use it to store the success or failure in SSJS code and retrieve the value in CSJS code to check and retry.
I have included a sample code with buttons however it can be implemented in many different ways.
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:button value="Label" id="button1">
<xp:eventHandler event="onclick" submit="true" refreshMode="partial" refreshId="label1">
<xp:this.script><![CDATA[alert("operation starting");]]></xp:this.script>
<xp:this.action><![CDATA[#{javascript:viewScope.status = "first operation completed";
print(getComponent("fail").getValue());}]]></xp:this.action>
<xp:this.onComplete><![CDATA[alert("first operation completed. starting second");
document.getElementById("#{id:ihRetry}").value = "False";
document.getElementById("#{id:button2}").click();]]></xp:this.onComplete>
<xp:this.onError><![CDATA[if ( document.getElementById("#{id:ihRetry}").value == "False" ) {
alert("first operation failed. retry again");
document.getElementById("#{id:ihRetry}").value = "True";
document.getElementById("#{id:button1}").click();
} else {
alert("first operation failed again. starting second");
document.getElementById("#{id:ihRetry}").value = "False";
document.getElementById("#{id:button2}").click();
}]]></xp:this.onError>
</xp:eventHandler>
</xp:button>
<xp:button value="Label" id="button2" style="display:none">
<xp:eventHandler event="onclick" submit="true" refreshMode="partial" refreshId="label1">
<xp:this.action><![CDATA[#{javascript:viewScope.status = "second operation completed";}]]></xp:this.action>
<xp:this.onComplete><![CDATA[alert("second operation completed. starting third");
document.getElementById("#{id:ihRetry}").value = "False";
document.getElementById("#{id:button3}").click();]]></xp:this.onComplete>
<xp:this.onError><![CDATA[if ( document.getElementById("#{id:ihRetry}").value == "False" ) {
alert("second operation failed. retry again");
document.getElementById("#{id:ihRetry}").value = "True";
document.getElementById("#{id:button2}").click();
} else {
alert("second operation failed again. starting third");
document.getElementById("#{id:ihRetry}").value = "False";
document.getElementById("#{id:button3}").click();
}]]></xp:this.onError>
</xp:eventHandler>
</xp:button>
<xp:button value="Label" id="button3" style="display:none">
<xp:eventHandler event="onclick" submit="true" refreshMode="partial" refreshId="label1">
<xp:this.action><![CDATA[#{javascript:viewScope.status = "third operation completed";}]]></xp:this.action>
<xp:this.onComplete><![CDATA[alert("third operation completed.");
document.getElementById("#{id:ihRetry}").value = "False";]]></xp:this.onComplete>
<xp:this.onError><![CDATA[if ( document.getElementById("#{id:ihRetry}").value == "False" ) {
alert("third operation failed. retry again");
document.getElementById("#{id:ihRetry}").value = "True";
document.getElementById("#{id:button3}").click();
} else {
alert("third operation failed again. end");
document.getElementById("#{id:ihRetry}").value = "False";
}]]></xp:this.onError>
</xp:eventHandler>
</xp:button>
<xp:inputHidden id="ihRetry">
<xp:this.value><![CDATA["False"]]></xp:this.value>
</xp:inputHidden>
<xp:label value="#{viewScope.status}" id="label1"></xp:label>
</xp:view>

xpages validation on field having onChange script

There is a required field:
<xp:this.validators>
<xp:validateRequired
message="Required field. Please add some text.">
</xp:validateRequired>
</xp:this.validators>
Also, the value from this field is copied ( using the onChange event ) to other fields:
<xp:eventHandler event="onchange" submit="true"refreshMode="norefresh">
<xp:this.action><![CDATA[#{javascript:Cdoc.setValue("dlg_Localitate",Cdoc.getValue("txt_LocalitateCompanie"));
Cdoc.setValue("dlg_Localitate_1",Cdoc.getValue("txt_LocalitateCompanie"))}]]>
</xp:this.action>
</xp:eventHandler>
An inconvenient issue appears when I just click the field to fill it: the validation message appears. Is because the field initially is empty and the code I added is into the onChange event?
I'd like to use this field as required before users can save the doc.
I tried set the values by CSJS, but without a result...
var string = XSP.getElementById("#{id:inputText1}").value
XSP.getElementById("#{id:txt_LocalitateS}").value = string
XSP.getElementById("#{id:txt_LocalitateP}").value = string
Also, let say I enter a value for inputText1 and later on I enter a new value... How can I update automatically the other 2 fields with the new value?
I tried something like this:
<xp:inputText id="inputText1" value="#{Cdoc.txt_LocalitateCompanie}"
style="height:20.0px;width:122.0px;font-weight:bold;font-size:10pt;font-family:verdana"
required="true">
<xp:this.validators>
<xp:validateRequired message="Completarea localitatii este obligatorie.">
</xp:validateRequired>
</xp:this.validators>
<xp:typeAhead mode="full" minChars="1" ignoreCase="true"
id="typeAhead1">
<xp:this.valueList><![CDATA[#{javascript:#DbLookup(#DbName(),"vwLocalitati",Cdoc.txt_LocalitateCompanie,1,"[PARTIALMATCH]");}]]></xp:this.valueList>
</xp:typeAhead>
<xp:eventHandler event="onchange" submit="true"
refreshMode="norefresh">
<xp:this.action><![CDATA[#{javascript:Cdoc.setValue("dlg_Localitate",Cdoc.getValue("txt_LocalitateCompanie"));
Cdoc.setValue("dlg_Localitate_1",Cdoc.getValue("txt_LocalitateCompanie"))}]]></xp:this.action>
<xp:this.script><![CDATA[XSP.partialRefreshGet("#{id:txt_LocalitateS}", {
onComplete: function() {
XSP.partialRefreshGet("#{id:txt_LocalitateP}", {
onComplete: function(){ }
});
}
});]]></xp:this.script>
</xp:eventHandler>
</xp:inputText>
Thanks in advance
Two things here. First, you should disable validators for onChange event, therefore it won't display the validation error.
Second, when you use a CSJS script together with a SSJS, it will fire the CSJS one and if it returns true, proceed with the SSJS. So if you want your CSJS code run after SSJS, you can place it into oncomplete.
If I understood your question correctly, the following code would solve it.
<xp:inputText
id="inputText1"
value="#{Cdoc.txt_LocalitateCompanie}"
style="height:20.0px;width:122.0px;font-weight:bold;font-size:10pt;font-family:verdana"
required="true">
<xp:this.validators>
<xp:validateRequired
message="Completarea localitatii este obligatorie.">
</xp:validateRequired>
</xp:this.validators>
<xp:typeAhead
mode="full"
minChars="1"
ignoreCase="true"
id="typeAhead1">
<xp:this.valueList><![CDATA[#{javascript:#DbLookup(#DbName(),"vwLocalitati",Cdoc.txt_LocalitateCompanie,1,"[PARTIALMATCH]");}]]></xp:this.valueList>
</xp:typeAhead>
<xp:eventHandler
event="onchange"
submit="true"
refreshMode="norefresh"
disableValidators="true">
<xp:this.action><![CDATA[#{javascript:Cdoc.setValue("dlg_Localitate",Cdoc.getValue("txt_LocalitateCompanie"));
Cdoc.setValue("dlg_Localitate_1",Cdoc.getValue("txt_LocalitateCompanie"))}]]></xp:this.action>
<xp:this.onComplete><![CDATA[if(dojo.byId("#{id:txt_LocalitateP}")) {
XSP.partialRefreshGet("#{id:txt_LocalitateP}", {
onComplete: function() {
XSP.partialRefreshGet("#{id:txt_LocalitateS}", {
onComplete: function(){ }
});
}
});
}]]></xp:this.onComplete>
</xp:eventHandler>
</xp:inputText>
UPDATE: In your case, the field you want to refresh is on the second tab with partialRefresh="true". It means that at the time of partialRefreshGet, the target fields might not exist in the DOM. I have added a check now.
this is taken from my comments and put into an answer:
onChange events are generally frowned upon due to performance and user experience. If, however, the field is a listed control ie combobox it is not so dramatic. The following options/ideas are available
Take out the onChange() to test whether that makes a difference. If so, move your code.
Use an update button to change all the fields en masse also preventing information that is already inputted from being deleted unwanted-ly
create your own validation method and show/hide a label manually (hack-y)
Research how to manually put text into an errors control
If the field is in a dialog box, move the onChange() to the open/close methods of the dialog
FURTHER EDIT
An idea that I might suggest is using the xspDoc.getDocument(true) method to push all changes from the xpage to the background document. Something tells me that this might make a difference with the server reading the changes to the document and realizing that it is not empty.
ADDITIONAL IDEAS
I did not mention this because it is a bit more advanced, but should also get the job done assuming the refreshes are done. Even that is not that big of a deal. You could read all of your data from the document into a java bean. This bean is then the "data source" for your page and you bind all of your controls to the properties of this bean. You will then use EL to bind your controls to the bean. In the setters for those variables that trigger changes in other fields, change those values. So,
public PageBean(){
//read connection information out of the URL and get the correct information out of the document
//set all variables
firstName=doc.getItemValueString("firstName");
}
private String firstName;
public String getFirstName(){
return firstName;
}
public void setFirstName(String firstName){
this.firstName = firstName;
setLastName("Schmidt");
}
....
Once you register your bean with faces-config.xml, you can then use EL to access the data
#{PageBean.firstName}
#{PageBean.lastName}
Then you can get your document again in save and reset the values, save and release.

Unexpected Error: executing JavaScript/Reference error

I haven't made any changes to this database, and all of a sudden, I get an (500) error when loading various Xpages. On this one, when I switch to "Display the Xpage runtime error" on xpages tab of the database properties, it displays the errors below web.
If I remove the button that this Client side code is contained (it had worked perfectly to validate an Edit Box before), the error just moves to the next piece of JavaScript on the page and gives a similar error.
I've tried to "CLEAN" project. I've tried to Sign the entire database with the server id.
Any help would be much appreciated!
The runtime has encountered an unexpected error.
Error source
Page Name:/msr.xsp
Control Id: _id20
Exception
Error while executing JavaScript computed expression
Script interpreter error, line=3, col=8: [ReferenceError] 'XSP' not found
------the button control:
<xp:eventHandler event="onclick" submit="true" refreshMode="complete">
<xp:this.action>
<xp:actionGroup>
<xp:actionGroup>
<xp:modifyField
name="Status">
<xp:this.value><![CDATA[#{javascript:if (document1.isNewNote()) {
"Submitted to Project Officer";
}
else if (document1.Status == "Submitted to Project Officer"){
"Submitted to Supervisor";
}
else{
"Completed";
}}]]></xp:this.value>
</xp:modifyField>
<xp:saveDocument
var="document1">
</xp:saveDocument>
<xp:changeDocumentMode
mode="readOnly" var="document1">
</xp:changeDocumentMode>
</xp:actionGroup>
</xp:actionGroup>
</xp:this.action>
<xp:this.script>
<xp:executeClientScript>
<xp:this.script><![CDATA[#{javascript:
if(XSP.getElementById("#{id:ProjectTitle}").value == ""){
XSP.getElementById("#{id:ProjectTitle}").focus();
XSP.getElementById("#{id:ProjectTitle}").style.backgroundColor = "pink";
alert("Please enter a Project Title.");
return false;
}
else{
XSP.getElementById("#{id:ProjectTitle}").style.backgroundColor = "#ffe";
}
///NOT sole source
if(XSP.getElementById("#{id:RT}").innerHTML == "MSR"){
if(XSP.getElementById("#{id:SoleSource}").checked == false){
if(XSP.getElementById("#{id:SS_Name1}").value == ""){
XSP.getElementById("#{id:SS_Name1}").focus();
XSP.getElementById("#{id:SS_Name1}").style.backgroundColor = "pink";
alert("Please fill in the Name of Suggested Source 1.");
return false;
}
else{
XSP.getElementById("#{id:SS_Name1}").style.backgroundColor = "#ffe";
}
}
}
}]]></xp:this.script>
</xp:executeClientScript>
</xp:this.script></xp:eventHandler>
</xp:button>
The script property of your executeClientScript action looks like it is being computed. The source should look something like this:
<xp:executeClientScript>
<xp:this.script>
<![CDATA[if(XSP.getElementById("#{id:ProjectTitle}").value == ""){...
]]></xp:this.script>
</xp:executeClientScript>
Instead, you have this:
<xp:executeClientScript>
<xp:this.script>
<![CDATA[#{javascript:if(XSP.getElementById("#{id:ProjectTitle}").value == ""){...
}]]></xp:this.script>
</xp:executeClientScript>
That syntax implies that, instead of simply entering client-side JavaScript to execute, you're running server-side JavaScript to compute what the client-side JavaScript should be. Server-side JavaScript does not define a global XSP object like client-side JavaScript does, which is why you're getting a ReferenceError.
Remove #{javascript: from the beginning of your CDATA block and the final } from the end, and your code will execute in the context you intended.

NotesXSPDocument - Inserting a boolean breaks getDocument

I have an XPage which has just broken due to (what should have been) a small change.
If I run (where document1 is NotesXSPDocument datasource) :
document1.replaceItem("ItemName", false); //or true, or any variable/formula that results in a boolean
followed by:
document1.getDocument(true);
I end up with an error
[TypeError] Exception occurred calling method NotesXspDocument.getDocument(boolean) null
This is on a new document (so not saved yet, which I've not tested, but might make a difference), hence I can't just set the field on the underlying doc.
It doesn't seem to be an artifact of anything else in the page, as a basic test page confirms it. It doesn't happen with anything (I've found) other than a boolean.
Any advice, other than just change the data type? I guess that's what I'll end up doing (along with a bug report) but I'd like to know I'm not missing something first.
Thanks
------------Test XSP code------------
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"><xp:this.data>
<xp:dominoDocument var="document1" formName="Test" />
</xp:this.data><xp:span style="font-weight:bold">
Button 1 code:</xp:span><xp:br></xp:br>document1.replaceItemValue("Test",false);
<xp:br></xp:br>var doc:NotesDocument = document1.getDocument(true);
<xp:br></xp:br>print(doc.toString());<xp:br></xp:br>
<xp:button value="Test 1" id="button1">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:document1.replaceItemValue("Test",false);
var doc:NotesDocument = document1.getDocument(true);
print(doc.toString());}]]></xp:this.action>
</xp:eventHandler></xp:button>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:br></xp:br><xp:span style="font-weight:bold">
Button</xp:span><xp:span style="font-weight:bold"> 2</xp:span><xp:span style="font-weight:bold"> Code:</xp:span><xp:span style="font-weight:bold"></xp:span> <xp:br></xp:br>document1.replaceItemValue("Test","Test);<xp:br></xp:br>var doc:NotesDocument = document1.getDocument(true);
<xp:br></xp:br>print(doc.toString());<xp:br></xp:br><xp:button value="Test 2" id="button2">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:document1.replaceItemValue("Test","Test");
var doc:NotesDocument = document1.getDocument(true);
print(doc.toString());}]]></xp:this.action>
</xp:eventHandler></xp:button></xp:view>
If you have a look in the stack trace you will see the following:
....
NotesException: Unknown or unsupported object type in Vector
lotus.domino.local.Document.NreplaceItemValue(Native Method)
lotus.domino.local.Document.replaceItemValue(Unknown Source)
com.ibm.xsp.model.domino.wrapped.DominoDocument.applyChangesToDoc(DominoDocument.java:1698)
com.ibm.xsp.model.domino.wrapped.DominoDocument.applyChanges(DominoDocument.java:1649)
com.ibm.xsp.model.domino.wrapped.DominoDocument.getDocument(DominoDocument.java:544)
com.ibm.xsp.script.WrapperDominoEx$fct_DominoDocument.call(WrapperDominoEx.java:254)
....
The problem is that you use the replaceItemValue method with an unsupported data type.
Here is a list of all allowed data types:
String Text
Integer Number
Double Number
DateTime Date-time item
java.util.Vector with String, Integer, Double, or DateTime elements Multi-value text, number, or date-time item
Item Same data type as the Item
The error is raised in the moment the backend document is synchronized with the datasource document. That's why it fails while calling document1.getDocument(true).
Hope this helps
Sven
EDIT:
Why are you setting the value to false?

Resources