duplicate items in Document after saving in a bean - xpages

I experience strange but reproduceable behaviour with an XPage which saves its values through a java bean. After copyAllItems to an document there are two richtext items in the document. First is empty, second is filled as expected.
This is my Xpage:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
xmlns:xc="http://www.ibm.com/xsp/custom">
<xp:this.data>
<xp:dominoDocument var="docDataSource" formName="test"></xp:dominoDocument>
</xp:this.data>
<xp:div id="test">
<xp:fileUpload id="fileUpload1" value="#{docDataSource.test}"></xp:fileUpload>
</xp:div>
<xp:button value="Label" id="button1">
<xp:eventHandler event="onclick" submit="true"
refreshMode="partial" refreshId="test">
<xp:this.action><![CDATA[#{javascript:registration.testCopyAllItems(docDataSource);}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
This is my java bean method:
public void testCopyAllItems(DominoDocument docDataSource) throws NotesException{
Document docUser = database.createDocument(); // <- get any database
docDataSource.getDocument(true).copyAllItems(docUser, true);
docUser.save();
}
This is the result in the document:
Does anyone have a hint on what could cause the trouble?

This seems to be a "normal" behaviour and I have seen it a lot working with RichtText fields. It shouldn't matter. Notes can deal with a RichText field constisting of more than one item.
As a workaround,
delete the RichText field after copyAllItems() with removeItem() and
copy it with copyItem() separately.
This should result in one item only.

Related

Save Document via XPage DataSource and computeWithForm="onsave"

when i save a Notes Document in a XPage with computeWithForm="onsave" there is nothing computed at the form.
I thought that the computeWithForm Option trigger the "QuerySave" Event in the form, but am i wrong ?
I know that i could use the querySaveDocument Event at the Xpage DataSource, but there is a lot of lotusscript logic :-)
form:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE form SYSTEM 'xmlschemas/domino_9_0_1.dtd'>
<form name='testform' xmlns='http://www.lotus.com/dxl' version='9.0' maintenanceversion='1.9'
replicaid='C125827800263D1B' noquery='true' publicaccess='false' designerversion='8.5.3'
recalc='true' renderpassthrough='true'>
<code event='querysave'><lotusscript
>Sub Querysave(Source As Notesuidocument, Continue As Variant)
Dim doc As NotesDocument
Set doc = Source.Document
doc.test = "123"
Call doc.Save( True, False )
End Sub</lotusscript></code>
<body><richtext>
<pardef id='1' leftmargin='1in' hide='notes web' tabs='L6.9375in'/>
<par def='1'/>
<pardef id='2'/>
<par def='2'><run><font name='Verdana' pitch='variable' truetype='true' familyid='20'/><field
usenotesstyle='false' height='0.2500in' width='4.7243in' multiline='true'
borderstyle='single' type='text' kind='editable' name='title'/></run></par>
<par def='2'><field type='text' kind='editable' name='test'/><compositedata
type='98' prevtype='65418' nexttype='222' afterparcount='6' containertype='65418'
aftercontainercount='1' afterbegincount='3'>
Yg4BAIQAAAAAAAAAAAA=
</compositedata></par></richtext></body>
<item name='$$ScriptName' summary='false' sign='true'><text>testform</text></item></form>
Xpage:
<?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="testform" computeWithForm="onsave" />
</xp:this.data>
<xp:inputText value="#{document1.titel}" id="titel1" />
<xp:br></xp:br>
<xp:button value="save" id="button1">
<xp:eventHandler event="onclick" submit="true" refreshMode="complete">
<xp:this.action>
<xp:saveDocument var="document1" />
</xp:this.action>
</xp:eventHandler>
</xp:button>
</xp:view>
computeWithForm only runs Input Translation and Input Validation formulas.
If you want to retain the LS logic, one option would be to move it to an agent (I believe you would need to change UIDocument classes to Document classes and re-code accordingly) and call the agent from XPages. It's not something I've done though, so I can't point you towards documentation.
That's a misunderstanding! compute with form executes the #Formula that are in the associated form. It does not execute any form events or run LotusScript. If you want to use LotusScript, you need to pull it into an agent and explicitly call that agent (hint: Don't bother, translate your script to SSJS or Java instead).
That aside: you shouldn't call doc.save in a querySave event - there will be a save action anyway. You hit the disk twice (that's for Notes client apps, as mentioned that code doesn't run in XPages)

How to sync edit mode with responses in xp:repeat

Notes-wise, I have a main doc and its responses documents.
XPage-wise, main doc and responses are displayed in the same xp:view, the responses being in a xp:table constructed by a xp:repeat
Each row of the table holds a panel, whose datasource is of course bound to the particular response.
So far, pretty straightforward, isn't it ?
I would like very much to ensure that, at any time, the edit mode of the main doc and the responses are in sync. Something along the lines of :
Everything is in read-only mode
User switches main doc to edit mode, automagically all responses switch to edit mode too
Ditto in reverse : switching main doc to read-only switches responses to read-only
I tried to dynamically compute the "action" attributes of the data sources in the rows, but that does not seem to work.
I'm thinking of somehow having the responses listen to the event "main doc changes mode" and acting accordingly, but I can't seem to be able to wrap my head around how to do it. Specifically, which event would that be ? Then, how to have the responses listen to an event happening somewhere else ?
Any hint appreciated
thx
edit : back a few days later, if anyone still there.
As per stwissel's suggestion, here be code :
<?xml version="1.0" encoding="UTF-8"?>
<xp:view
xmlns:xp="http://www.ibm.com/xsp/core"
xmlns:xe="http://www.ibm.com/xsp/coreex"
style="background-color:rgb(217,217,255)">
<xp:this.data>
<xp:dominoView
var="subdocsNotesView"
viewName="#{javascript:compositeData.subdocsNotesView}"
categoryFilter="#{javascript:compositeData.ctnrK}">
</xp:dominoView>
</xp:this.data>
<xp:table>
<xp:repeat
id="leRepeat"
value="#{subdocsNotesView}"
var="oneDoc">
<xp:tr>
<xp:panel id="rowPanel">
<xp:this.data>
<xp:dominoDocument
var="rowData"
formName="Person"
documentId="#{javascript:oneDoc.getDocument().getUniversalID()}"
action="editDocument">
</xp:dominoDocument>
</xp:this.data>
<xp:td>
<xp:inputText
id="Name"
value="#{rowData.Name}"
defaultValue="#{javascript:oneDoc.getDocument().getItemValueString('Name')}">
</xp:inputText>
</xp:td>
</xp:panel>
</xp:tr>
</xp:repeat>
</xp:table>
</xp:view>
Surround your repeat control with a panel.
Calculate panel's property readonly depending on main document's read or edit mode.
Use a boolean viewScope variable "editMode" to memorize the current mode.
<xp:table>
<xp:panel readonly="#{ not viewScope.editMode}">
<xp:repeat ...>
...
</xp:repeat>
</xp:panel>
</xp:table>
Change mode in buttons:
<xp:button value="Editmode" id="button1">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action>
<xp:actionGroup>
<xp:changeDocumentMode mode="edit" var="mainDocument">
</xp:changeDocumentMode>
<xp:executeScript>
<xp:this.script><![CDATA[#{javascript:
viewScope.editMode = true
}]]></xp:this.script>
</xp:executeScript>
</xp:actionGroup>
</xp:this.action>
</xp:eventHandler>
</xp:button>
<xp:button value="Readmode" id="button2">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action>
<xp:actionGroup>
<xp:changeDocumentMode mode="readOnly" var="mainDocument">
</xp:changeDocumentMode>
<xp:executeScript>
<xp:this.script><![CDATA[#{javascript:
viewScope.editMode = false
}]]></xp:this.script>
</xp:actionGroup>
</xp:this.action>
</xp:eventHandler>
</xp:button>
Even though repeat's dominoDocument has always property action="editDocument" repeat documents are in read mode when panel's property readonly is true.
If panel's property readonly is false then repeat documents are in edit mode.
This way edit mode of main document and repeat documents is in sync.
I would recommend using a Managed Bean which implements the Model View Controller (MVC). The Controller Bean in the view scope contains the current Main Doc model and coordinates access to the main doc and its responses. The Main Doc Model would include an array of the Response Doc Model so that the read mode is managed at the Controller while the data is managed in the model.
John Dalsgaard has an excellent presentation on this topic located here.
I use this approach for both Parent/Child documents and for documents that contain items which are related or synced lists.
Happy coding.

Rich text field in custom control with composite data

I am developing an xpages application and use 1 custom control where I have a rich text field on it.
The source document is on the main page.
The rich text field has it value via compositeData.
I pass the document and the field name.
In the CC I address the rich text field as
compositeData.DataSource[compositeData.Fieldname]
Where DataSource my document1 is and Fieldname the name of the rich text field on the notes document.
All works except inserting an image in the rich text and removing attachments.
I did find a lot of information about this but not a real solution.
Dit anybody ever found a solution for this?
Regards,
Peter
Here is the code:
Custom Control
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
xmlns:xe="http://www.ibm.com/xsp/coreex">
<xp:panel>
<xp:label id="label1" value="Title:"></xp:label>
<xp:inputText id="inputText1">
<xp:this.value><![CDATA[#{compositeData.DataSource[compositeData.Title]}]]></xp:this.value>
</xp:inputText>
<xp:br></xp:br>
<xp:inputRichText id="richtext">
<xp:this.value><![CDATA[#{compositeData.DataSource[compositeData.Field]}]]></xp:this.value></xp:inputRichText>
<xp:fileUpload id="fileUpload1">
<xp:this.value><![CDATA[#{compositeData.DataSource[compositeData.Field]}]]></xp:this.value>
</xp:fileUpload>
<xp:fileDownload rows="30" id="fileDownload1"
displayLastModified="false" allowDelete="true">
<xp:this.value><![CDATA[#{compositeData.DataSource[compositeData.Field]}]]></xp:this.value>
</xp:fileDownload>
</xp:panel>
</xp:view>
XPAGE using the CC
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core" xmlns:xc="http://www.ibm.com/xsp/custom"
xmlns:xe="http://www.ibm.com/xsp/coreex">
<xp:this.data>
<xp:dominoDocument var="document1" formName="testform"></xp:dominoDocument>
</xp:this.data>
<xe:applicationLayout id="applicationLayout1">
<xp:panel>
<xp:button id="button1">
<xp:this.value><![CDATA[Save & Close]]></xp:this.value>
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action>
<xp:actionGroup>
<xp:saveDocument var="document1"></xp:saveDocument>
<xp:openPage name="$$PreviousPage"></xp:openPage>
</xp:actionGroup>
</xp:this.action>
</xp:eventHandler>
</xp:button>
<xp:br></xp:br>
<xc:cc DataSource="#{javascript:document1}" Field="Body" Title="Title">
</xc:cc>
</xp:panel>
<xe:this.configuration>
<xe:bootstrapResponsiveConfiguration></xe:bootstrapResponsiveConfiguration>
</xe:this.configuration>
</xe:applicationLayout>
<xp:this.navigationRules>
<xp:navigationRule outcome="xsp-success" viewId="$$PreviousPage"></xp:navigationRule>
</xp:this.navigationRules>
</xp:view>
I use the following in a custom control. Notice that I use a different syntax for the value parameter for xp:inputRichText than for the upload and download controls - and that the value parameter is computed on page load ($) instead of dynamically (#):
<xp:inputRichText id="richtext" value="${javascript:'#{document.' + compositeData.fieldName + '}';}"></xp:inputRichText>
<xp:fileUpload id="fileUpload" value="#{document[compositeData.fieldName]}"></xp:fileUpload>
<xp:fileDownload id="fileDownload" value="#{document[compositeData.fieldName]}"></xp:fileDownload>

Get unid of document before it is saved and have it not change when saved

I have an XPage that has a single data source document1. I would like to know the unid of the document before it has been saved.
It's seems this is possible because document1.getDocument().getUniversalID() returns a value before it is saved.
However, this value always changes once the document is saved then it remains constant. Is there a way to set the unid so it doesn't change when saved? I've tried if (document1.isNewNote()) document1.getDocument().setUniversalID(document1.getDocument().getUniversalID()) but it is still changing when saved.
Change document's id on dominoDocument's querySaveDocument event.
if (document1.isNewNote()) {
document1.getDocument().setUniversalID(yourID);
}
Here is a complete XPage example:
<?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.querySaveDocument><![CDATA[#{javascript:
if (document1.isNewNote()) {
document1.getDocument().setUniversalID(document1.getItemValueString("id"));
}
}]]></xp:this.querySaveDocument>
</xp:dominoDocument>
</xp:this.data>
<xp:inputText
value="#{document1.id}"
defaultValue="#{javascript:document1.getDocument().getUniversalID()}">
</xp:inputText>
<xp:button
value="Label"
id="button1">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="complete"
immediate="false"
save="true">
</xp:eventHandler>
</xp:button>
</xp:view>

XPage fileDownload1 control becomes editable on page full refresh?

Why fileDownload1 control becomes editable (delete file attachment icon appears next to fileDownload control) when I just refresh the page. Here is my code where I use a button to refresh the page (just for the example)
<xp:button value="Label" id="button1">
<xp:eventHandler event="onclick" submit="true" refreshMode="complete"></xp:eventHandler>
</xp:button>
<xp:this.data>
<xp:dominoDocument var="document1" formName="myXpage"
databaseName="${javascript:sessionScope.SUPPORT_DB_FILE}"
action="openDocument">
</xp:dominoDocument>
</xp:this.data>
<xp:fileDownload
id="fileDownload1"
rows="30"
displayLastModified="false"
displayType="false"
displayCreated="false"
hideWhen="true"
allowDelete="true"
value="#{document1.MyBodyRTF}">
</xp:fileDownload>
I tried to replicate what you're describing, but am having trouble doing so. I created a new XPage and modeled it after your sample code.
Notable differences:
I never see the trash icon
I had to compute the UNID of some document with an attachment in the appropriate field
I set both the DB name and UNID in beforePageLoad
I noticed an interesting change in the UI behavior (see animated gif below)
I'm not sure what you're running into without having more source code to be able to view, but I can confirm that what I tried doesn't reproduce the result you're describing.
XPage source:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view
xmlns:xp="http://www.ibm.com/xsp/core">
<xp:this.beforePageLoad><![CDATA[#{javascript:sessionScope.myDbName = database.getFilePath();
var vw:NotesView = database.getView("SomeForms");
var first:NotesDocument = vw.getFirstDocument();
sessionScope.myFirstDoc = first.getUniversalID();
first.recycle();
vw.recycle();}]]></xp:this.beforePageLoad>
<xp:this.data>
<xp:dominoDocument
var="document1"
action="openDocument"
formName="SomeForm"
databaseName="${javascript:return sessionScope.myDbName;}"
documentId="${javascript:return sessionScope.myFirstDoc;}" />
</xp:this.data>
<xp:fileDownload
rows="30"
id="fileDownload1"
displayLastModified="false"
value="#{document1.SomeAttachments}" />
<xp:button
value="Refresh"
id="button1">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="complete" />
</xp:button>
</xp:view>

Resources