Xpages- Create New copy of saved document and open it without saving it - xpages

I have a document that I want to create a new version/copy of, so I am trying to do server-side javascript to
Create a new document
copy all items from the current document
open the new document that I have created, without saving it
I am not able to open the newly created document, is this possible?
code I am using is:
var viewPanel=getComponent("viewPanel1");get the componet of viewPanel
var docIDArray=viewPanel.getSelectedIds(); get the array of document ids
for(i=0;i < docIDArray.length; i++){
var docId=docIDArray[i];
var doc=database.getDocumentByID(docId);
var newDoc = database.CreateDocument
doc.CopyAllItems (newDoc)
var docUNID = newDoc.getUniversalID ()
// need something here to open copied document
}

You need to store the IDs in the session scope and then open the page and do the copy inside in one of the data source events:
var viewPanel=getComponent("viewPanel1");get the componet of viewPanel
var docIDArray=viewPanel.getSelectedIds(); get the array of document ids
sessionScope.alltheDocs = docIDArray;
then open the page where you want to have the new document. Inside that page you need to have a repeat control that matches the element count of alltheDocs. I probably would design it using a DojoTab container (one tab per document). Inside the repeat place a panel with a data source (or a custom control). Then in the queryNewDocument event you copy the fields using the variable name of your datasource.

You could save these docs, then show them and add them to a deletion queue by marking a field on those docs. On save remove them from deletion queue as a work around possibly.

1) Do not store Notes object to scope lasting longer than request.
2) If XPage has to inherit some values, it needs to read them from some source.
3) You can not inherit data from Notes document - according to (1) in memory object must not be stored in sessionScope (simplest way to pass objects between two pages), and you can not retrieve it by UNID/key (it is not saved, as requested).
So, (possibly) the only option is:
Make copy of source document - copy every field you are interested in into Map[String,Object]. Fields must be converted into "raw" objects String, Double, Date (java, not Notes) or their vectors for multivalues. You must not copy special (Notes objects) fields - names, dates, rich text! Names can be converted to strings, Dates can be retrieved as Java dates, rich text might be treated as MIME (String) (but with possible loss of formatting). I think you don't want to pass attachments.
In target XPage, define queryNewDocument event to lookup and initialize fields from this Map object. Delete the sessionScope object to prevent duplicates.

I needed very similar thing in my application. I have XPage with a source document opened in read mode. There is a button that creates a new document and set few values (using the source document). I wanted the XPage to open this new document in Edit mode after it was created.
Note: I could not use redirect with URL parametr action as I needed to open it in a same XPage and preserve a view scope variables and beans.
Solution:
Add View Scope variables NewDocAction and documentId (on button click),
Partial refresh of XPage (on button)
Compute document data source using viewScope variable documentId
Check presence of View Scope variable NewDocAction (in onClienLoad event)
javascript code in the button:
var travelDoc = xpBean.createTravelDoc(requestDoc);
if (travelDoc != null){
viewScope.put("content","travelForm"); //to render proper CC on XPage
viewScope.put("documentId", travelDoc.getUniversalID());
viewScope.put("NewDocAction", "ToEditMode");
}
javascript code in a onClientLoad event of a travelForm custom control:
if (viewScope.containsKey("NewDocAction") && viewScope.get("NewDocAction").equals("ToEditMode")){
context.setDocumentMode("travelDoc","edit");
viewScope.remove("NewDocAction");
}

Related

Xpages - How to access the newly generated UniversalID and send it on submit in Xpages

I want to be able to send the UNID of the newly created document to the user, so that the user can access the document. In the execution script;
var unid = param.documentId;
var vUnid = newDocId.getDocument().getUniversalID;
Both were picking up the parent UNID, which already existed in the browser.
This image will show you how the document looks like and will be grateful to know how to pass the newly generated UNID when document is submitted.
You need to add your code into the postSave event of your data source. The UNID for a new document only gets set when saved for the first time. Before that it's a temp value only
Write your newly generated UniversalID into a sessionScope variable
sessionScope.newUnid = newDocId.getDocument().getUniversalID;
and use it in next XPage called after submit
var useTheNewUnid = sessionScope.newUnid;

View data being cached/failing to refresh

I have a tabbed panel containing different sections of a form. In one section, users are given the ability to add a child document to the currently open document. In a second section, they are given a listbox, where the options are dynamically generated (via #DbLookup) by looking at a view of all the child documents, filtered by the current document's ID. This functionality all works correctly, however, there is a problem with the dynamic listbox options.
When a user adds a new child document, then switches to the next tab, the listbox is not updated with this new document. If they save/re-edit the main document or refresh the page, it makes no different, the user must load another XPage before going back to the original in order for the listbox to update. I have tried to resolve this by doing full updates of the page, using session.evaluate and "NoCache" in the #DBLookup call, or calling database.getView("My view").refresh(), but without luck.
There is also a similar problem where I have a repeat control which uses a view of child documents (again filtered by main document ID) as a datasource. When a user adds a child document using a button, it partially refreshes the repeat - but doesn't show the new child document until the page is refreshed again (or you leave the page and return).
Is there something crucial I am missing with regards to the JSF lifecycle/the way that view data is cached?
As first measure I would add another formula item to the listbox which simply returns the current time (#Now() should work). That way you can check if the listbox options are refreshed on the update in the first place.
If the options are refreshed fine it's indeed clear that the #DbLookup does some caching, although I'm not aware of any default caching logic.
At least for a test I would change the code to use a NotesView object instead of the #DbLookup, something like this:
var nview = database.getView("someview");
var nc = nview.getAllEntriesByKey(currentDocument.getDocument().getUniversalID(), true);
var a = [];
var ve = nc.getFirstEntry();
while (ve) {
a.push(ve.getColumnValues().elementAt(0)); // value of first column
ve = nc.getNextEntry(ve);
}
return a;
(I wrote the code from memory, there may be syntax errors).
Since the code only works with view entries it should be equally fast than the #DbLookup. And you could even do a nview.refresh() if needed.

Get list of all ids in an xPage container control

I am using this code to copy some documents by a button click source. I would like to prevent the end user from having to select columns and would prefer simply get all the documents ids from the view panel. Not exactly sure how to do that, or if a dataview might be a better choice for me.
var viewPanel=getComponent("viewPanel1"); //get the componet of viewPanel
var docIDArray=viewPanel.getSelectedIds(); //get the array of document ids
for(i=0;i < docIDArray.length;i++){
var docId=docIDArray[i];
var doc=database.getDocumentByID(docId);
var db=session.getCurrentDatabase();
var newDoc:NotesDocument=doc.copyToDatabase(db);
newDoc.replaceItemValue("approved","No");
var id=newDoc.getUniversalID();
newDoc.save(true);
}
Leave the view panel out of the equation: a view panel is a component, and components are for users to interact with; if the user's interaction with the view panel (i.e. "selecting" documents) doesn't alter which documents you wish to duplicate, ignore the view panel (at least, for the purposes of this specific event).
If you simply want to duplicate all documents that display in the view to which the view panel is bound, talk to the same data source the view panel is associated with. So, assuming your data source declaration looks something like the following:
<xp:panel>
<xp:this.data>
<xp:dominoView var="allDocuments" viewName="($All)" />
</xp:this.data>
<xp:viewPanel value="#{allDocuments}">
...
...then just iterate through that same view:
allDocuments.setAutoUpdate(false);
var eachDoc = allDocuments.getFirstDocument();
while(eachDoc) {
var newDoc = eachDoc.copyToDatabase(database);
newDoc.replaceItemValue("approved", "No");
newDoc.save();
newDoc.recycle();
var nextDoc = allDocuments.getNextDocument(eachDoc);
eachDoc.recycle();
eachDoc = nextDoc;
}
allDocuments.setAutoUpdate(true);
Since you're duplicating the documents within the same database, when the event finishes, the view panel will simply show twice as many documents, since you duplicated all of them. Unless, of course, the item value you're replacing disqualifies them from the view you're displaying.
NOTE 1: The reason the code above toggles the autoUpdate property is because, unless you toggle that to false prior to the iteration, when you duplicate each document, if the new document does display in the view you're iterating, the indexer will become aware of it, and you might end up in an infinite loop, because each time you try to get the next document, it's actually returning a handle on the duplicate you just created... so you would essentially be infinitely duplicating the same document until some exception is thrown (i.e. stack overflow, out of memory, etc.). Disabling autoUpdate prevents that by only allowing iteration of entries the index was aware of when your routine began.
NOTE 2: If the data source is only defined inside the view panel, move it to a parent (panel, Custom Control, or XPage) that also contains whatever component will trigger the duplication (i.e. button, link) and reference the data source within the view panel. That way both the view panel and the button can talk to the same data; otherwise, only the view panel is aware that the data source exists.

Multiple data sources in XPages

I have 2 xpages that interact together. The first acts as a homepage and allows the user to create a new document and fill out some basic header information. The button to create the new document, sets a sessionScope var with the NoteID. It then saves document1 and opens the same document using another xpage.
The second xpages is bound to document1, and uses the sessionScope to edit the document. This xpages contains and editPanel bound to document2. I want to create a child document to document2. This works but what happens is document1 is also saved as conflict. It is also saved as a second main document.
I DO NOT want to save document1 at all, but can't seem to prevent it. The button that creates document2 uses the simple action save document, and specfies document2.
HELP, I have tried everything, and have been stuck on this for 2 days.
Thanks!
Steve
To bind to multiple datasources, on your XPage, under Properties expand Data and set "Ignore Request Params" to TRUE
If you change the type of your buttons, to a simple button instead of a submit button, and handle the doc.save() yourself should solve your problem.
The Save Document simple action accepts the name of the data source to save as a String argument. If supplied, it will only save that specific data source.

Dialog Control Cannot See Second Data Source on the XPage

I have a custom control with a field where the user will enter a document ID (not a note ID or UNID, just a unique number). This data source is named document1. When they exit the field, I perform a lookup and display either the document with that ID or a new document in an extlib Dialog control (data source bundleDoc). When bundleDoc is saved, I want to update a log field on document1 to indicate that a bundle document was added.
I can save bundleDoc and close (hide) the dialog but the code in my Save & Close button in the dialog can't "see" document1. I can't refer to it using document1.getItemValueString or by getComponent. The getItemValueString returns and empty string and getComponent throws an error because the return value is null.
I would have posted an image to help visually but I don't have enough reputation yet. :(
What am I missing? Shouldn't I be able to get to document1 from the dialog control since it is on the same XPage?
UPDATE: Two fields on the dialog form have computed default values that use getComponent to get their values from document1. So, at least when the dialog is loaded, it can see document1. Also, bundleDoc is not defined as a data source for the dialog control. I will try that tomorrow to see if it makes a difference.
UPDATE 2: Still not enough reputation to post a picture, but here is some code. This is one custom control that contains a dialog control. document1 is defined as the data source for the custom control and bundleDoc is defined as the data source for the panel in the dialog control that contains the table of fields for the bundle document.
The BundleID field in the dialog control has a computed default value using this:
if (bundleDoc.isNewNote()) {
getComponent("inputBundleID").getValue();
} else {
bundleDoc.getItemValueString("BundleID");
}
The formula for StorageLocationID is similar except that the component is inputStorageTrayID.
This is the code in the Save & Close button:
bundleDoc.save();
var newArr = new Array(document1.getItemValue("WorkLog"));
newArr.push("Added bundle " + document1.getItemValueString("BundleID") + " - " + session.getCommonUserName());
document1.setValue("WorkLog",newArr);
document1.save();
getComponent("dialog1").hide();
The error happens on the document1.save line but it does not get the BundleID from document1 (I set a sessionScope variable to the value of newArr and it showed 'Added bundle - Anonymous'.
It depends on where you have added the datasources. If document1 is set as the datasource of custom control 1 and bundleDoc is the datasource of custom control 2 you can't access them outside of the custom control they're defined in.
If you add document1 as the datasource of your custom control and create the xe:dialog control (containing a panel with the bunleDoc datasource) in the same custom control you should be able to access document1 (and update/ save it) from a button on the dialog.
I think the problem was initially caused by caching issues because it started working the day after I posted the question.
However, I had to do one more thing to get the page to work the way I wanted to. This is the XPage in Designer:
To write a value from the Save & Close button back to the WorkLog field, I had to save the document1 data source before opening the dialog. Then document1 was recognized throughout the Save & Close code and it was saved properly at all times.

Resources