There is a viewPanel having a column with showCheckbox="true".
Is it possible to restrict the users to select only one row/document ( and not multiple rows/docs. ) listed by the viewPanel?
Not in a View Panel. The View Panel is designed to offer a quick, simple approach with restricted functionality.
An alternative (possibly better) approach may be to have another column with a link or image that triggers whatever functionality you need. That will allow users to trigger functionality with a single click rather than two. The View Panel allows you to place controls in columns instead of just mapping to a column in the underlying view.
Alternatively, you could add a checkbox manually to a column, map to a scoped variable, and check/uncheck programmatically.
Paul's probably right on. My alternative for you is to use a repeat control. You can make it look however you want. Including a view control.
I have an example of this in this NotesIn9 show: http://notesin9.com/index.php/2011/07/11/notesin9-ee-009-using-java-hashmaps-and-treemaps-with-xpages/
Now in my example I did multiple values. But if instead of a HashMap or ArrayList if you kept your "selected" document id in a single value field like just a scoped variable.. then you'd get what you want. One document at a time.
I agree with Paul Stephans (also upvoted his answer because I think it would be the Nest solution) but if you insist on adding such a functionality to your viewPanel you can do this by adding a client side script to prevent the user from selecting more then one element:
First add the styleClass="rowCB" to your checkbox row and insert this code to your xpage:
<xp:scriptBlock>
<xp:this.value><![CDATA[dojo.ready(function(){
dojo.query('.rowCB>input').connect("onclick", function(evt){
var target = evt.target.id;
if(!window.crrCheckedElement){
window.crrCheckedElement = evt.target.id;
}else if(window.crrCheckedElement != target){
alert("You can select only one item!");
evt.target.checked = false;
}else if(window.crrCheckedElement == target){
window.crrCheckedElement = "";
}
})
});]]></xp:this.value>
</xp:scriptBlock>
Maby the code needs some improvement but this should be your way to go.
though maybe not the best solution, here is one possibility.
function deselectOtherDocs(viewName, currentDocId) {
var viewPanel:com.ibm.xsp.component.xp.XspViewPanel = getComponent(viewName);
var selectedIds = viewPanel.getSelectedIds();
for(var i=0; i<selectedIds.length; ++i){
if(selectedIds[i]!=currentDocId){viewPanel._xspSetIdUnchecked(selectedIds[i])}
return;
}
fire this off when a doc is checked and pass the view control name and the current doc's unid.
pardon any typos. I am writing from my phone.
edit: if you don't have to use a view control, then David Leedy's suggestion is the way to go. store the selected unid in a scope variable and let that determine which repeat row's box is selected. you might also consider using a radio button instead of a checkbox, since the former is understood as a single selector by users.
Related
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.
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.
Does anyone have a suggestion on how to print selected documents in an simple xPages view. I'm converting a legacy application. Which used the following Lotus script code to print. Thanks
Set db = session.CurrentDatabase
Set collection = db.UnprocessedDocuments
count = collection.count
If count = 0 Then
Goto errSelectDocs
End If
Stop
For i = 1 To count
'
Set note = collection.GetnthDocument (i)
Set Source2 = w.EditDocument( False, note )
Set Source3 = w.ComposeDocument("","","mRecensement imp")
Call Source3.print(1)
Call Source3.close
Call Source2.close
'----------------------------------
nextdocument:
Next
I'm going to answer here rather the following up in the comments of the Simon answer.
so ok. We're saying build a new page with a repeat control of the select documents. and the question asker is saying, I THINK, that it seems wrong to do:
doc:NotesDocument=database.getDocumentByID(rowData);
return doc.getItemValue("xxxx") for 30 + items
right. You don't want to do that. should work. But icky to do.
Probably what I would do is create a SSJS function to pass rowData into. In that function build an array. Load the document once... put all the items into the array and pass them back to the page with the repeat control.
Probably what you do then is have a panel and use either a dataContext or objectData that's bound to the panel. Inside the panel is your page and fields. Those fields just read from the dataContext or objectData. so you're only getting the document once. I guess you could even use just a scoped variable though I don't think there's an event to call code on each row. So you'd need to hack it into the first field maybe or something.
But that's what you want. I previously asked a question on StackOver flow about returning multiple parameters like this: How to pass variable parameters to an XPages SSJS function?
Maybe that's helpful.
Someone might come up with a better solution but one option is this.
First have your viewPanel on your first XPage. Select your documents and click a button. The code would do this.
var viewPanel = getComponent("viewPanel1");
sessionScope.documentIDs = viewPanel.getSelectedIds();
You then hand it off to another XPage which has a repeat control of the print structure for a document. It reads the document ID's and creates the page. Then just use the normal print command after loading.
window.print();
I have a repeat control using a view as the datasource with a custom control within the repeat. The custom control is made up of a panel with two tables. One table has computed fields with an Edit button and the other has editable fields with a Save and Cancel button. The Edit and Cancel buttons work as needed, but the Save button gives a NotesDocument.save() is null error. I have already narrowed the issue down to the error occurring on the edoc.save() line by commenting out all prior lines. I even tried to do an edoc.lock(), but got the same error.
var edoc:NotesDocument = database.getDocumentByUNID(viewScope.get('docid'));
edoc.replaceItemValue('Ext_1',viewScope.get('ext_1'));
edoc.replaceItemValue('DID',viewScope.get('did'));
edoc.replaceItemValue('Mobile',viewScope.get('mobile'));
try {
edoc.save();
} catch(e) {
print(e.toString());
}
The storage of a DocID in the viewScope and a repeat control doesn't seem right. You want to add a custom property to your custom control called DocID and then instead of
database.getDocumentByUNID(viewScope.get("docid"));
You do:
database.getDocumentByUNID(compositeData.DocID);
This was you can be sure that you get the document that was in that view for that row.
What you also might consider, instead of all the manual steps (the ones you commented out) have a panel with a DocumentDataSource and then simply bind your input fields to that one. Handover of id via custom property and "IgnoreRequestParameter = true
Then you simply do a rowDoc.save() (presuming you named the datasource rowDoc) and you don't need to recycle anything. Let us know how it goes.
I'm sure I'm not the first one to try to address this, but Google isn't doing me any good.
If you use the Advanced Search in Drupal to filter on taxonomy terms, the search form comes back with the term IDs in the keyword textbox like so:
search phrase category:32,33
The chosen values are NOT selected again in the taxonomy select box.
Instead of showing them in the keyword textbox, I would like them to be selected in the taxonomy select box - the way that any user would expect a form like this to act. I have been looking for a module that will add this functionality, to no avail. I have tried implementing hook_form_alter() to set the #default_value on that form element based on the previous form submission (available in the $form_state arg), but a) this seems kludgy and b) it seems that this function is called once to validate the form and again to re-display it, and the submitted value isn't available the second time (which is when it's needed).
Any suggestions?
It took much longer than it should have, but I finally figured out how to do this. I may release a module on the Drupal site later on, but in case anyone else has this same problem, this was the solution I came to.
Create a module and use hook_form_alter() to modify the form. I already had a module I was using to customize the advanced search, so I put it in there. I won't get into the detail of building your own module - you can find a simple tutorial for that, and you only need to define this one function.
/**
* Implementation of hook_form_alter().
* Remove 'category:x,y,z' from the keyword textbox and select them in the taxonomy terms list
*/
function modulename_form_alter(&$form, $form_state, $form_id) {
// Advanced node search form
if ($form_id == 'search_form' && $form['module']['#value'] == 'node' && user_access('use advanced search')) {
// Remove category:x,y,z from the keyword box
$searchPhrase = $form['basic']['inline']['keys']['#default_value'];
if(!empty($searchPhrase) && strpos($searchPhrase, 'category:') !== false) {
$searchWords = explode(' ', $form['basic']['inline']['keys']['#default_value']);
foreach($searchWords as $index=>$word) {
if(strpos($word, 'category:') === 0) {
// Use the value to set the default on the taxonomy search
$word = substr($word, strlen('category:'));
$form['advanced']['category']['#default_value'] = explode(',', $word);
// Remove it from the keyword textbox
unset($searchWords[$index]);
}
}
// Re-set the default value for the text box without the category: part
$form['basic']['inline']['keys']['#default_value'] = implode(' ', $searchWords);
}
}
}
Thanks for sharing this. After several drupal installations I am facing this problem now, too. However, this time I had to use some search extensions like search_config and search_files to meet the clients requirements. Furthermore, I'm using better_select which turns select lists into lists of checkboxes (easier to select mulitple values on long lists). Thus, the checkboxes always returned some value (either the id if selected or 0 (zero) if not selected) and I ended up with something like "search phrase category:0,0,0,0,...,0".
Your solution above indeed removes the "category:0,0,0,0,0,0,...,0" string from keyword search field and checks all taxonomy terms correctly. The same can be done for content types, you'll just have to replace "category:" by "type:" throughout the whole script.
Paul