Xpages - Repeat control / view SSJS - xpages

I have the exact same design of a database on live and DEV, however, when I have a repeat control on a page, it works as expected on DEV, pulling in the status of documents from a view but not on live, where it just lists the number of the document in the view. It also doesn't seem to return any unique values. I'm using a dbcolumn to get the values, and tried wrapping an #unique around it, and then using a script lib to handle the unique entries, but no such luck. Image attached and code below. Any ideas?
<table class="table table-hover">
<thead>
<tr>
<th>Status</th>
<th>Total</th>
<th>%</th>
</tr>
</thead>
<tbody>
<xp:repeat id="repeat1" rows="30" var="row">
<xp:this.value><![CDATA[#{javascript://var result = #DbColumn("", "vwStatusStats", 1);
//var result = session.evaluate("#DbColumn(\"\"; ; \"vwStatusStats\"; 1)").elementAt(0)
//result = session.evaluate("#Unique");
//return result
//return #Unique(#DbColumn("", "vwStatusStats", 1))
return DbColumnArray("", "", "", "unique", "", "vwStatusStats", 1)
}]]></xp:this.value>
<tr>
<td>
<xp:text escape="true"
id="computedField11">
<xp:this.value><![CDATA[#{javascript:row}]]></xp:this.value>
</xp:text>
</td>
<td>
<xp:text escape="true"
id="computedField1">
<xp:this.value><![CDATA[#{javascript:var v:NotesView = database.getView("vwStatusStats");
var vec:NotesViewEntryCollection = v.getAllEntriesByKey(row);
return vec.getCount().toString()}]]></xp:this.value>
</xp:text>
</td>
<td>
<xp:text escape="true"
id="computedField2">
<xp:this.value><![CDATA[#{javascript:var total = getComponent("cmpTotal").getValue();
var amount = getComponent("computedField1").getValue();
var result = (amount / total) * 100;
return result}]]></xp:this.value>
</xp:text>
</td>
</tr>
</xp:repeat>
<tr>
<td>Total</td>
<td>
<xp:text escape="true" id="cmpTotal">
<xp:this.value><![CDATA[#{javascript:var v:NotesView = database.getView("vwStatusStats");
return v.getEntryCount().toString()}]]></xp:this.value>
</xp:text></td>
<td>
<xp:text escape="true"
id="computedField3">
<xp:this.value><![CDATA[#{javascript:var total = getComponent("cmpTotal").getValue();
var amount = getComponent("cmpTotal").getValue();
var result = (amount / total) * 100;
return result}]]></xp:this.value>
</xp:text></td>
</tr>
</tbody>
</table>

Check for multiple views with the same name / alias in the live database. Your live version could be looking to a different view than the one you expect.
Best practice is also to ensure an #DbColumn call passes #DbName() as the first parameter. It's still going to have to do the same amount of work, but it's going to ensure consistent results ("" won't work for XPiNC). Using SSJS / Java to get the View and return getColumnValues() is also likely to be better performing that #DbColumn(), from analysis by Howard Greenberg. As an extra bonus, it might be worth looking at this XSnippet for use of Repeat Control facets for the HTML that wraps your repeat https://openntf.org/XSnippets.nsf/snippet.xsp?id=repeat-with-header-and-footer-facets. It ensure the HTML for the table doesn't get additional HTML injected.

It seems your live view is corrupted. Open it with Notes client to verify it does contain expected values.
One possibility can be corruption of "programmatic name" value in column properties. Sometimes, especially by copy/paste in view design your columns get the same dollar value ($1 for example) for columns with different formula. That causes "strange" values in one of those columns, completely different from its formula. Remove programmatic value, new one will be generated automatically.

Related

Two DynamicView Links

I am using a dynamicViewPanel that displays a user's attachment information from a domino view. There are 4 columns which information is properly being displayed. When a user clicks the first column, another page opens with technical information about that attachment. What I need to happen is when a user clicks the 4th column, which displays [edit], a different page opens up which the user can edit the attachment information that is displaying. I am using UserViewCustomizer code.
Any suggestions on how to get the [edit] link to open a different page that the Attachment link?
My XPage Code:
<xe:dynamicViewPanel id="dynamicViewPanel2"
pageName="#{empty sessionScope.formName ? 'QuickResponseCode.xsp' : sessionScope.formName}"
var="viewEntry" customizerBean="com.cascorp.UserProfileViewCustomizer">
<xp:eventHandler event="onColumnClick" submit="true"
refreshMode="complete" id="Attachment">
<xp:this.action><![CDATA[# {javascript:sessionScope.hideAdd="Yes";
var srlNbr = viewEntry.getColumnValue("serialNbr");
var url="/qr/?"+srlNbr;
context.redirectToPage(url);}]]></xp:this.action>
</xp:eventHandler>
<xp:eventHandler event="onColumnClick" submit="true"refreshMode="complete" id="edit">
<xp:this.action><![CDATA[#{javascript: "I think something goes here" ]]></xp:this.action> </xp:eventHandler>
<xp:this.facets>
<xp:pager layout="Previous Group Next" partialRefresh="true" id="pager2" xp:key="footerPager"> </xp:pager>
<xe:pagerSaveState id="pagerSaveState1" xp:key="viewTitle"</xe:pagerSaveState>
<xp:pager layout="Previous Group Next" partialRefresh="true" id="pager1"
xp:key="headerPager"></xp:pager>
</xp:this.facets>
<xe:this.data>
<xp:dominoView var="view1"
databaseName="#{configBean.UserprofilesDbPath}" viewName="#{empty
sessionScope.viewName ? 'attachmentsbyowner' : sessionScope.viewName}"
keys="#
{javascript:session.getEffectiveUserName();}">
</xp:dominoView>
</xe:this.data>
</xe:dynamicViewPanel>
My ViewCustomize Code:
package com.cascorp;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import org.openntf.xsp.debugtoolbar.beans.DebugToolbarBean;
import com.ibm.xsp.extlib.component.dynamicview.UIDynamicViewPanel;
import com.ibm.xsp.extlib.builder.ControlBuilder.IControl;
import om.ibm.xsp.extlib.component.dynamicview.DominoDynamicColumnBuilder.DominoViewCustomizer;
import
com.ibm.xsp.extlib.component.dynamicview.UIDynamicViewPanel.DynamicColumn;
import com.ibm.xsp.extlib.component.dynamicview.ViewDesign.ColumnDef;
public class UserProfileViewCustomizer extends DominoViewCustomizer {
//this is used to customize the DynamicViewPanels used in the Order
Processing views
//#SuppressWarnings("unchecked")
#Override
public void afterCreateColumn(FacesContext context, int index,
ColumnDef colDef, IControl column) {
//Get a map of the session variables to read the view session scope variable
//Map svals = context.getExternalContext().getSessionMap();
//Create a variable for the current component
UIComponent columnComponent = column.getComponent();
//Create a reference to the column and set the links to open in read mode
DynamicColumn dynamicColumn = (DynamicColumn) columnComponent;
//To have every view open the selected documents in read mode add the following
dynamicColumn.setOpenDocAsReadonly(true);
DebugToolbarBean.get().info("name of column " + dynamicColumn.getColumnName());
/*
* If all you need to do is have the views open in read mode instead of
* edit mode then the above code is all you need.
* If you want to customize the view columns the the follow code can be
* used as an example.
*/
if (dynamicColumn.getColumnName().equalsIgnoreCase("Attachment")){
//make it a link
dynamicColumn.setDisplayAs("link");
DebugToolbarBean.get().info("make Attachment Column a link");
}
if (dynamicColumn.getColumnName().equalsIgnoreCase("edit")){
//make it a link
dynamicColumn.setDisplayAs("link");
DebugToolbarBean.get().info("make edit Column a link");
}
//Set column properties for specific views.
//if (svals.containsValue("processforkkit")) {
//Hide the first column in this view
if(dynamicColumn.getColumnName().equalsIgnoreCase("$2")){
dynamicColumn.setRendered(false);
}
if (colDef.isCategorized() ){
//set the expand and collapse images if the column is categorized
DebugToolbarBean.get().info("column is categorized: " + dynamicColumn.getColumnName());
// ExtendedColumnDef extColDef = (ExtendedColumnDef) colDef;
// DebugToolbarBean.get().info("twistie image: " + extColDef.twistieImage);
UIDynamicViewPanel.DynamicColumn col = (UIDynamicViewPanel.DynamicColumn)column.getComponent();
col.setExpandedImage("expand.png");
col.setCollapsedImage("collapse.png");
DebugToolbarBean.get().info("collapsed image: " + col.getCollapsedImage());
DebugToolbarBean.get().info("expanded image: " + col.getExpandedImage());
DebugToolbarBean.get().info("style class: " + col.getStyleClass());
col.setStyleClass("category_col");
}
super.afterCreateColumn(context, index, colDef, column);
}
}
I used a repeat control. See below:
<tbody>
<xp:repeat id="repeat1" var="rowData" indexVar="ind" value="#{view1}"
rows="10" repeatControls="true">
<tr>
<td>
<xp:text escape="false"> </xp:text>
</td>
<td>
<!--Create a link -->
<xp:link escape="true" text="#{rowData.Attachment}" id="link2"
value="#{configBean.HostURL}qr?#{rowData.serialNbr}">
</xp:link>
</td>
<td>
<xp:text escape="true" id="computedField6" value="#{rowData.serialNbr}">
</xp:text>
</td>
<td>
<xp:text escape="true" id="computedField2" value="#{rowData.modelNbr}">
</xp:text>
</td>
<td>
<!-- CREATE A 2ND LINK -->
<xp:link escape="true" id="link3" text="${langBean.labelEdit}">
<xp:this.value><![CDATA[#{javascript:importPackage(com.cascorp);
hostName = configBean.getValue("HostCommon")
dbName = configBean.getValue("UserprofilesDbPath")
try{ if(rowData.isDocument()){
return "MyProfileAttachment.xsp?databaseName="+hostName+"!!"+dbName+"&documentId="+rowData.getUniversalID()+"&action=editDocument"
}
} catch (e) {
return e.toString()
}}]]>
</xp:this.value>
</xp:link>
</td>
</tr>
</xp:repeat>

Can I display HTML in Error Messages control?

In my XPages application we are using Bootstrap for theming.
If I look at the alert component:
<div class="alert alert-danger" role="alert"> <strong>WARNING</strong> SYSTEM FAILURE. </div>
I see a element is used.
Now in my XPages application I used something as followed:
FacesContext.getCurrentInstance().addMessage(null, new javax.faces.application.FacesMessage(javax.faces.application.FacesMessage.SEVERITY_ERROR, "<strong>WARNING</strong> SYSTEM FAILURE", ""));
but this is displayed as text:
Is it somehow to make the Error Messages control display HTML?
You can write your own message control like this:
<xp:panel styleClass="xspMessage"
rendered="#{javascript:facesContext.getMessages().hasNext()}">
<xp:repeat id="repeat1" rows="30" var="msg">
<xp:this.value><![CDATA[#{javascript:var v = new java.util.Vector();
var itr = facesContext.getMessages()
while(itr.hasNext()) {
var element:javax.faces.application.FacesMessage = itr.next();
v.add(element.getSummary())
}
return v;}]]></xp:this.value>
<xp:text escape="false" id="computedField1" value="#{msg}" tagName="div">
</xp:text>
</xp:repeat>
</xp:panel>
A better way would be to use CSS to format the error messages control output.

XPages view data source and limit to single category

I have a categorized view that I am using as a view data source in an XPage. I have set the categoryFilter of the data source so that only the call history of the selected individual (for a given incident) is displayed. The filter being stored in the sessionScope is a concatenation of the UniqueID of the person and the IncidentID. The lookup view is categorized by that value.
One would expect that to work, however the filtering does not seem to be working and I can see all call histories regardless of the person I choose. I've done this before and I must be missing something obvious.
I have verified the sessionScope.callHistoryID is being updated as I select a new person. My Call History db currently only has 2 history documents for one guest, yet they display for every guest in the Xpage.
sessionScope.callHistoryID from the debugger is obviously not the same (see below):
<xp:panel id="pnlCallHistoryModal" style="padding-bottom:20px">
<xp:this.data>
<xp:dominoView var="callhistoryView"
viewName="luCallHistorybyPaxandIncidentID"
databaseName="blah.nsf"
ignoreRequestParams="true"
categoryFilter="#{javascript:sessionScope.callHistoryID}">
</xp:dominoView>
</xp:this.data>
<div class="col-xs-12 col-md-12 col-lg-12 panel-body">
<xp:repeat id="repeat1" value="#{callhistoryView}"
var="viewRow" indexVar="rowIndex" rows="500"
repeatControls="false">
<xp:this.facets>
<xp:text disableTheme="true" xp:key="header"
escape="false">
<xp:this.value><![CDATA[
<table class="table table-hover">
<tbody>]]></xp:this.value>
</xp:text>
<xp:text disableTheme="true" xp:key="footer"
escape="false">
<xp:this.value><![CDATA[
</tbody>
</table>]]></xp:this.value>
</xp:text>
</xp:this.facets>
<xp:tr>
<xp:this.rendered><![CDATA[# {javascript:if(viewRow.isCategory()){
return false
}else{
return true
}}]]></xp:this.rendered>
<xp:td styleClass="col-md-10 col-lg-10 small">
<xp:text escape="true" id="name">
<xp:this.value><![CDATA[#{javascript:#ProperCase(viewRow.getColumnValue("CallerFullName"))}]]></xp:this.value>
</xp:text>
<br></br>
<xp:text escape="true" id="computedField1"
styleClass="small secondary-text">
<xp:this.value><![CDATA[#{javascript:if(viewRow.getColumnValue("CallerMemo").length > 128){
return viewRow.getColumnValue("CallerMemo").substring(0,128) + " ..."
}else{
return viewRow.getColumnValue("CallerMemo")
}}]]></xp:this.value>
</xp:text>
</xp:td>
<xp:td styleClass="col-md-2 col-lg-2 small">
<xp:text escape="true" id="creationdatetime"
styleClass="small">
<xp:this.value><![CDATA[#{javascript:viewRow.getColumnValue("CallDateTime")}]]></xp:this.value>
<xp:this.converter>
<xp:convertDateTime type="both"
dateStyle="short" timeStyle="short">
</xp:convertDateTime>
</xp:this.converter>
</xp:text>
</xp:td>
</xp:tr>
</xp:repeat>
</div>
</xp:panel>
I suspect this will be doing the equivalent of ViewNavigator.createViewNavFromCategory(). It's worth checking how that acts if the category name doesn't exist. It may fall back to a partial match.
For "restrict to category", you might be better placed using the keys property and settings keysExactMatch to true.

Xpages - Creating Repeat or View from different database with ACL set to No Access

I am trying to create a repeat element from a view in a different database that does not allow Anonymous ACL access.
PubDb.nsf has ACL set for user Anonymous to Author
PrivDb.nsf has ACL set for user Anonymous and Default "no access".
The database, PrivDb has information that I want to secure, but would like to pull a couple of views out for public display.
When I create a repeat in PubDb.nsf using PrivDb.nsf view, It will not display any data. I am able to use sessionAsSigner to get SSJS to see the view and open documents at the server side level, but when I try to display the view or repeat elements there is no data ? If I change ACL in PrivDb for Anonymous to Reader everything works, but now the entire db is open to Anonymous access.
I understand that sessionAsSigner and sessionAsSignerWithFullAccess allows me to use db signers effective rights, and I can use SSJS to access the notesdocuments and publish data using computed fields for individual docs, but I can't find any information that tells me I can or can't display an xpages element (repeat or view) using the sessionAsSigner. Maybe I can create a lotusscript agent that populates the block?
Below is the code I use to create the repeat element in PubDb.nsf. Note, I assigned the DB twice, once in Application and again in View because when I didn't use sessionAsSigner in the view's computed value I would get prompted for authentication, and it's required in the app field.
<xp:this.data>
<xp:dominoView var="view2">
<xp:this.databaseName><![CDATA[${javascript:
var DB:NotesDatabase=sessionAsSigner.getDatabase(database.getServer(),"PrivDb.nsf");
DB;
}]]>
</xp:this.databaseName>
<xp:this.viewName><![CDATA[${javascript:
var dbOther:NotesDatabase = sessionAsSignerWithFullAccess.getDatabase(database.getServer(), "PrivDb.nsf");
var lookupView:NotesView = dbOther.getView( "PrivView" );
lookupView.recycle();
lookupView}]]>
</xp:this.viewName>
</xp:dominoView>
</xp:this.data>
<div class="container">
<div class="page-header">
<h1>This is a test.</h1>
</div>
<xp:br></xp:br>
<xp:br></xp:br>
<div class="row">
<div class="col-md-1"></div>
<div class="col-md-10">
<xp:repeat id="repeat1" rows="30" var="playerData"
value="#{view2}" repeatControls="true">
<div class="panel panel-default">
<xp:text escape="true" id="computedField1"
value="#{playerData.$5}">
</xp:text>
hcp:  
<xp:text escape="true" id="computedField5"
value="#{playerData.$6}">
</xp:text>
<xp:br></xp:br>
<xp:text escape="true" id="computedField2"
value="#{playerData.$7}">
</xp:text>
   
<xp:text escape="true" id="computedField3"
value="#{playerData.$8}">
</xp:text>
   
<xp:text escape="true" id="computedField4"
value="#{playerData.$9}">
</xp:text>
<xp:br></xp:br>
</div>
<div class="col-md-1"></div>
</xp:repeat>
</div>
</div>
</div>
Try changing your repeat to return for instance the view entries directly using sessionAsSigner instead of going through a view data source. So in your case:
<xp:repeat id="repeat1" rows="30" var="playerData">
<xp:this.value><![CDATA[#{javascript:
sessionAsSigner.getDatabase(database.getServer(), "PrivDb.nsf").getView("PrivView").getAllEntries();
}]]></xp:this.value>
...
</xp:repeat>

Teamstudio Unplugged: adding more table rows without partial refresh

I am using Teamstudio Unplugged to try and Extend a table row with inputTexts bound to a document.
The function of this table is for the user to write down a observation and then a description of the observation.
After this the user should be able to press a button and get another row so he can add another observation.
I cannot partially refresh the entire table because it would cause the inputTexts to reload the value bound to them.
I cannot partially refresh the new row and change its styleclass with serverside scripts because IDs does not carry over from the designer.
Meaning that this in the designer:<xp:tr id="row1"> becomes this in the pc simulator: <tr>
So I can't solve this problem with a computed styleclass and a partial refresh. So i have now built a button included below but it only changes works twice. after that the onclick string never changes.
<xp:button value="Flere observationer" id="button4" styleClass="button">
<xp:this.attrs>
<xp:attr name="onclick">
<xp:this.value><![CDATA[#{javascript:
return "$('.row"+sessionScope.rows+"').removeClass('hidden');"
}]]></xp:this.value>
</xp:attr>
</xp:this.attrs>
<xp:eventHandler event="onclick" submit="true" refreshMode="partial"
refreshId="button4">
<xp:this.action>
<xp:executeScript script="#{javascript:sessionScope.rows++;}">
</xp:executeScript>
</xp:this.action>
</xp:eventHandler>
</xp:button>
This can be done in Unplugged using standard(-ish) XPages techniques, which may be easier for anyone not well versed in JQuery.
I would never recommend using a table for layout purposes, use <div> and or <ul/li> tags and use CSS. However for the purpose of answering this specific question I've used a table (climbing off soapbox now)...
So I'm assuming that the data in each row is in the same document. If so and the user is writing one observation at a time why not use a full refresh? Everything is done client-side on Unplugged (even the server parts - if that makes sense) so its fast. This method shows existing observations (if applicable) and allows the users to enter one more row at a time:
Add a hidden field on your form - NoOfObservations - this can be a text field and will be used as an index in the XPage.
Add your datasource on an XPage to the form
Create a beforePageLoad event to set the index from either an existing doc or new one:
<xp:this.beforePageLoad><![CDATA[#{javascript:
var ObservationCount = 0;
if(!document1.isNewNote()){
ObservationCount = document1.getItemValue("NoOfObservations")[0];
}
sessionScope.put("observations", ObservationCount);
}]]>
</xp:this.beforePageLoad>
Create your table with existing rows - using the xp:repeat Control with the index in the sessionScope to produce your rows - adding 2 new lines for new entries:
<table>
<xp:repeat id="repeat1" rows="30" var="rowData"
indexVar="dataRows">
<xp:this.value><![CDATA[#{javascript:sessionScope.get("observations");}]]></xp:this.value>
<tr>
<td>
<xp:label value="Observation" id="label1"
style="color:rgb(255,255,255)">
</xp:label>
</td>
<td>
<xp:text escape="true" id="computedField1">
<xp:this.value><![CDATA[#{javascript:return document1.getItemValueString("ObservationTitle" + dataRows);}]]></xp:this.value>
</xp:text>
</td>
</tr>
<tr>
<td>
<xp:label value="Details" id="label2"
style="color:rgb(255,255,255)">
</xp:label>
</td>
<td>
<xp:text escape="true" id="computedField2">
<xp:this.value><![CDATA[#{javascript:return document1.getItemValueString("ObservationDesc" + dataRows);}]]></xp:this.value>
</xp:text>
</td>
</tr>
</xp:repeat>
<tr>
<td>
<xp:label value="New Observation"
id="newObservationTitle" style="color:rgb(255,255,255)">
</xp:label>
</td>
<td>
<xp:inputText id="inputText1"></xp:inputText>
</td>
</tr>
<tr>
<td>
<xp:label value="New Description"
id="newObservationDesc" style="color:rgb(255,255,255)">
</xp:label>
</td>
<td>
<xp:inputTextarea id="inputTextarea1"></xp:inputTextarea>
</td>
</tr>
</table>
Use an xp:button with the following SSJS in the onClick event:
<xp:button value="Save" id="button1">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action>
<xp:actionGroup>
<xp:executeScript>
<xp:this.script><![CDATA[#{javascript:
var newObservation = parseInt(sessionScope.get("observations")) +1;
document1.replaceItemValue("ObservationTitle" + newObservation, getComponent("inputText1").getValue());
document1.replaceItemValue("ObservationDesc" + newObservation, getComponent("inputTextarea1").getValue());
document1.replaceItemValue("NoOfObservations", newObservation);
document1.save();}]]></xp:this.script>
</xp:executeScript>
<xp:openPage name="/UnpMain.xsp"></xp:openPage>
</xp:actionGroup>
</xp:this.action>
</xp:eventHandler>
</xp:button>
As with your other question, again I would be inclined to do this client side rather than trying to do partial refreshes. Simply add rows to the table using jQuery and then store the values from each row into hidden list fields when saving the document.
Also again, for the IDs that you want to work with, rather than rely on the IDs being generated by the server, add your own attributes and select items using those instead.

Resources