Posts Tagged With: Views

Showing a database in a panel in #IBMNotes

I’d never understood the whole ‘widgets’ thing that got added to Notes, where you could have additional things in that right-side set of panels. At first, I put Linked In over there, thinking I’d use it to look up people and keep track of discussions. I never did use it and eventually, it would just show me a login box, so when I changed laptops, I didn’t bother putting it back. I tried some RSS feeds, but they were far too chatty for me to ever keep track. However, I found a great use for them this past month: quick access to back-end of XPages databases.

In our environment, we put all the XPages and custom controls into one ‘design’ database and almost all of the data into another. You guessed it, the almost all is the problem. Some of the configuration documents have to reside in the design database, so it knows where the data resides. I want the users to double-click on the icon and simply open the application in XPiNC, rather than accidentally seeing the man behind the curtain. So, I put a link in the home page of our application that opens the views in Notes:

<xp:link escape="true" id="link1" text="Open Notes views" style="position: absolute;z-index:100;top:25px;left:750px;">
	<xp:this.rendered><![CDATA[#{javascript:var roles = context.getUser().getRoles();
	return @IsMember("[WFAdmin]", roles);}]]></xp:this.rendered>
	<xp:this.value><![CDATA[#{javascript:var server:NotesName = session.createName(@Subset ( @DbName(), 1));
		var filepath = database.getFilePath();
		return "Notes://" + server.getCommon() + "/" + filepath + "/TSWFKeywords?OpenView";}]]>

That works fine, but you have to navigate back to the home page to get the the link. I could put it everywhere, but as I was looking at the Drag n Drop sidebar per a request from our newly acquired office in the UK, I figured out that creating the XML files and putting them among your widgets was really easy.

Just create a file with an XML extension — it can have any name, so I tend to create them with meaningful names, like “APPS1 Shared Resources Engage.xml” — with a format similar to this:

<?xml version="1.0" encoding="UTF-8"?>
<webcontextConfiguration version="1.1">
<palleteItem contributeTabOnStartup="false" 
title="ENGAGE SR" 
<data TYPE="DEFAULT"/>

I know five of the values that you can edit….

Line Meaning
contributeTabOnStartup=”false” If you want it to always open as a regular tab when you open Notes, set this to true. Otherwise, set this to false.
contributeToSideshelfOnStartup=”true” If you want it to always open as a panel on the right, set this to true. If you’d prefer to double-click and have it open in a new window, set this to false.
id=”APPS1SharedEngage” The unique ID for this widget. If you drag-and-drop another XML file with the same ID, it will update the existing one. When you create one using the menus, it supplies a random numeric one, but text also works, so I’d recommend that using meaningful, text ones.
title=”ENGAGE SR” This is what displays on the icon in the sidebar and on the panel header if you load it in a panel.
url=”Notes://APPS1/Projects/Philippines/Engage/EngageSharedRes.nsf” The Notes URL that you want to open. You can specify a view, but when I tried to specify an agent it didn’t execute the agent. I also tried to specify the XPage, but it never loaded. So, either the database or a specific view (with /viewname?OpenView after the filepath)

Then, drag and drop that XML file from your file system onto the My Widgets panel. In the one I have above, it always opens in a panel on startup, so I can access the views quickly and easily. However, we’re going to have 70 of these at any one time once we roll out our design to all of our projects, so I’ll probably not have any load on startup. If you don’t load them, on startup, double-clicking opens them as a new Notes window.

As soon as I finish figuring out how we’re going to configure the Drag n Drop widget, I’ll post on how to do that. While there is documentation on OpenNTF, I think I can provide some more insight and may look at doing some enhancements down the road.

Categories: Old Notes | Tags: , , | Leave a comment

Simple view without links in #xpages

One of my co-workers, Neil Enet, asked me the other day if it was possible to set up a view but prohibit the users from opening the documents. I tossed out the idea of using an HTML table with all the HTML for the rows and cells being computed in one column of a view.

"<tr><td>" + approverName + "</td><td>" + approvalType +"</td><td>" + description"</td></tr>"

Unfortunately, that’s pretty clunky and not very satisfying. So, as we talked, I said, “Hey, how about you create an XPage with a view and just don’t make any of the columns clickable?” So, he did.

I asked Neil to comment on it, and he had some great thoughts:

My first XPage. I’ve never competed on a race before, but I imagine the feeling of winning one as being pretty similar to seeing this XPage for the first time. And OK, let’s be honest, it’s a pretty simple XPage. “Simple” might be too much of a word, actually. It’s just a view. ONE view.

The process was extremely easy. I just dragged the View Control, linked it to the view in Notes that I wanted, and voilà. I changed the font size and color of each column, and that felt like an even more awesome achievement.

The funny thing is I’m sure that if I never had the need to create a view like this, where users weren’t able to open documents, this wouldn’t have been so fulfilling. I can see myself saying: “Great, I just created a view, and you can’t open any docs. What’s the big deal about THAT?” But it turns out that that’s exactly what I needed. So simple! And to think that Old Notes didn’t allow me to do this, and that I had to go down the “terrifying” XPages way. Ha!

I can’t think of a better way to start playing with XPages. I now know I can do one, and I know I’m being very naive if I say “XPages is a piece of cake”, but there it is on my system, and I’m sure it will not be the only one.

I’m looking forward to creating more complex XPages, and it’s very very exciting. I might just take a picture of my XPage and put it next to my wife’s here on my desk. And when people walk by and ask me what that is, I’ll answer: “That’s the most simple XPage in the world. But you know what? It’s MY simple XPage.”

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="">

<xp:viewPanel rows="30" id="viewPanel1">
		<xp:pager partialRefresh="true" layout="Previous Group Next" xp:key="headerPager" id="pager1">
	<xp:this data>
		<xp:dominoView var="view1" viewName="COPApprovals"></xp:dominoView>
	<xp:viewColumn columnName="$11" id="viewColumn1" style="font-weight:bold;color:rgb(0,0,160)">
	<xp:viewColumn columnName="$10" id="viewColumn2" style="font-weight:bold;color:rgb(0,64,0)">
	<xp:viewcolumn columnName="$12" id="viewColumn3">


Then, to get users to access it from their Old Notes, using an Outline Entry to open the URL….

targetXpage := "internalApprovals.xsp";

server := @Subset ( @DbName; 1 );
path := @Subset ( @DbName; -1 );

fserver := @Name([CN]; server);
fpath := @ReplaceSubstring(path; "\\"; "/");

url := "notes://" + fserver + "/" + fpath + "/" + targetXpage;


And now, Neil has developed his first XPage, users will be able to see status on their documents and everyone will be happy. (Well, I’ll be happier once he goes back in and names the columns in the old view, gives better IDs to the XPage viewColumns, puts it all onto an application layout control and makes everything pretty, but, it works!)

So, if you’re still in fear of XPages, you needn’t be. Go forth and be LOST IN XPAGES with the rest of us!


Categories: Old Notes, Xpages | Tags: , , | 2 Comments

Preventing users from opening a document in Notes

As we prepare our pilot of our XPage application, I was reminded that sometimes users might find a way to open a document in the Notes client when we don’t really want them to. While the odds are against them getting to a view or to a Notes document, no system is idiot-proof (idiots are absolutely genius sometimes!) So, I dug out an old script that I’d written back during my time at FAA to prevent users from accidentally opening a Notes database when they should be opening it only via the browser.

So, the users in question need to be able to update the documents, but I want them to do it in XPiNC or in the browser. That means, I can’t go using readernames fields to hide the documents from them and I know that hiding views (either via not including them in an outline or by naming convention) isn’t necessarily going to prevent them from opening my views.

Basically, all we do is check for their roles and if they have the right one, we let them in. Otherwise, they get warned off. I could add some script to this to open the document in the proper XPage, but this is a bare-bones version to help you get started if you have this kind of need.

Sub Queryopen(Source As Notesuidocument, Mode As Integer, Isnewdoc As Variant, Continue As Variant)
Dim session As New NotesSession
Dim db As NotesDatabase
Dim doc As NotesDocument
Dim formName As Variant
Dim OKtoOpen As Boolean
Dim roles As Variant

Set db = session.CurrentDatabase
roles = db.QueryAccessRoles(session.UserName)

Set doc = Source.Document
formName = doc.GetItemValue ( "Form" )

OKtoOpen = False

Forall URoles In roles
	If Ucase(URoles) = "[ADMIN]" Then
		OKtoOpen = True
	End If
End Forall

If OKtoOpen = False Then
	Continue = False
	Messagebox "You are not authorized to access " & formName(0) & " documents via the Notes Client!",48, "Access Error"
End If
End Sub

While I was at it, I also wrote another version to be used to keep users out if they accidentally opened it on the backup or development servers. (Yes, I know production databases don’t belong on development servers, but it has happened here and, I am sure, other places).

Sub Queryopen(Source As Notesuidocument, Mode As Integer, Isnewdoc As Variant, Continue As Variant)
	Dim ws As New NotesUIWorkspace
	Dim session As New NotesSession
	Dim db As NotesDatabase
	Dim appsdb As NotesDatabase
	Dim doc As NotesDocument
	Dim appsdoc As NotesDocument
	Dim serverName As New NotesName ( "" )
	Dim dontOpenServers (1) As String

	dontOpenServers (0) = "DominoDev"
	dontOpenServers (1) = "Backup"

	Set db = session.CurrentDatabase
	Set serverName = New NotesName ( db.Server )

	Forall badServer In dontOpenServers
		If serverName.Common = badServer Then
			Continue = False
			Messagebox "You are attempting to open this document on " & serverName.Common & Chr$(10) & "Trying to open the document on MAIN",48, "Wrong Server"
			Set doc = Source.Document
			Set appsdb = New NotesDatabase ( "MAIN/COMPANY", db.FilePath )
			' if that didn't open it, try again
			If Not ( appsdb.IsOpen ) Then
				Call appsdb.Open ( "MAIN/COMPANY", db.FilePath )
			End If
			' if that didn't open it, try by replicaID
			If Not ( appsdb.IsOpen ) Then
				Call appsdb.OpenByReplicaID ( "MAIN/COMPANY", db.ReplicaID )
			End If
			If Not ( appsdb.IsOpen ) Then
				Messagebox "Could not open the MAIN/COMPANY replica of the database, trying local replica",48, "Failed"
				Call appsdb.OpenByReplicaID ( "", db.ReplicaID )
			End If
			If ( apps1db.IsOpen ) Then
				Set appsdoc = apps1db.GetDocumentByUNID ( doc.UniversalID )
				If Not ( appsdoc Is Nothing ) Then
					Call ws.EditDocument ( False, appsdoc )
					Messagebox "Opened the database, but could not open the document",48, "Failed"
				End If
				Messagebox "Could not open the local replica of the database either",48, "Failed"
			End If
		End If
	End Forall
End Sub

These QueryOpens could be placed on individual forms or on subforms that are on those forms. You could put a version in the PostOpen event of the database script, though you have to keep in mind that the PostOpen doesn’t run if the user opens a document via a document link instead of opening a view or the database itself. Come to think of it, I will be putting a version in the PostOpen to prevent unauthorized users from opening the database, but have it quietly open the XPiNC page I want them to open.

I thought about putting this in my Security category, but it’s not really about security. It’s mostly about making sure the user gets the proper experience, by using production replicas or the correct client.

Hope you found something interesting here!

Categories: Old Notes, Utilities | Tags: , , , , , , | 5 Comments

Nesting repeats to provide links to attachments in #XPages

Another of the challenges in my assessment XPage problem is that the evaluators will need to open the attachments that were submitted with each grant request. There are two issues here – first, that the attachments are uploaded to multiple separate documents from the grant request and, second, that there might be multiple attachments on each of those documents.

We chose to have the attachments on separate documents to reduce the size of the request document and lower the likelihood of truncation or replication issues. The users requesting these grants are all located in Africa and may well be in remote locations. So, bandwidth issues, even for the browser side, are of paramount concern for us. We’ve had some issues in the past on the Notes side with some truncations or with replication issues and found it better to keep documents smaller as a precaution.

So, for each request, there can be up to 4 documents with an unknown number of attachments associated with each request. As such, I stumbled upon the idea of using a repeat-within-a-repeat to display the links for each attachment.

First, I made sure to get only the attachment documents that I want by filtering my source view (as discussed previously):

<xp:dominoView viewName="AnnexLinks" var="linksView">

Then, I needed to build my repeat control. The code itself is actually very brief. My AnnexLinks view is very simple, with only three columns: AppUniqueID (my index, which is not the document unique ID, by the way), the AttachmentType and the URLs for the attachments.

The URLs column builds a unique URL for each attachment:

unid := @Text ( @DocumentUniqueID );
base := "0/" + unid + "/$file/";
base + @AttachmentNames

Since there can be multiple values in the URLs column for a single document, I have the link embedded inside a repeat control, using that value as an array to populate my link. I’m computing the filename back from the URL to use as the link label, but using the AttachmentType for the document in the outer repeat to display a label for the attachments.

<xp:repeat id="repeat3" rows="30" value="#{linksView}" var="rowData" indexVar="rowIndex">
    <xp:text escape="true" id="computedField1" value="#{rowData.AttachmentType}" style="font-weight:bold">
    <xp:label value=" files:" id="label2" style="font-weight:bold">
    <xp:repeat id="repeat4" rows="30" value="#{rowData.URLs}" var="url">
        <xp:link escape="true" id="link1" value="#{url}">
            <xp:this.text><![CDATA[#{javascript:@RightBack(url,"/") + " ";}]]></xp:this.text>
        <br />

It’s shocking to me that something that ends up so small can accomplish what seemed like a huge tasks initially. On my notepad, I’ve got all kinds of SSJS written to get a handle to the attachment documents, then to try building an array of URLs, synchronized with a list of filenames to be used as labels. Fortunately, by poking around for URL info (h/t to Stephan Wissel – this will have to be updated for XPiNC and upgradeability) and referring back to my copy of Mastering XPages, I was able to sort out how to do it in just a few lines of simple, elegant code.

Categories: Xpages | Tags: , , , , | 1 Comment

Filtering source views for use in #xpages

In our continuing example of purchase orders, we move from our examination of dynamic field binding in repeats to the filter used on the source view to select the competition questions we want to appear in that repeat control.

Depending on the type of competition, the source selection method and the dollar amount of the purchase order, there are different questions that the person preparing the purchase order needs to answer about the competition. There are several categories of competition questions, but the layout for the questions and answers remains the same, so I created a custom control that is used by all categories and the custom control appears on the XPage multiple times.

Each time, I pass the category as one of the properties of the custom control, as well as the criteria (type, method and amount). My view is set up with four sorted columns to filter my competition questions, so with the help of Per Henrik Lausten on StackOverflow, I learned that all I have to do is create an array to be used as the key, just as I would in LotusScript for a GetAllDocumentsByKey.

On our Purchase Order XPage, the syntax for one of the custom control insertions would appear as follows:

		sectionTitle="Competitive Process" competitionType="#{javascript:competitionType}"

When referring to the custom properties of a custom control within that control, you use compositeData followed by the property name. It makes it rather easy to pass parameters to the control.

In Java, arrays are handled by using a Vector. Simply create the Vector, then addElement to add each of my four filters (category, type, method and amount). When this is assigned to the Keys element of a dominoView used as a source, it filters the documents returned to provide only those that match our filter criteria. Here’s our filter in action:

    <xp:dominoView var="competitionQuestionView"
     viewName="CompetitionQuestions" keysExactMatch="true"
       <xp:this.keys><![CDATA[#{javascript:var vArray = new java.util.Vector();

        return vArray;

*Updated to us vArray.add instead of vArray.addElement on Jesse Gallagher’s advice. Thanks, Jesse!

Categories: Server-Side Javascript, Xpages | Tags: , , , , | 3 Comments

Repeats and dynamic field bindings #xpages

In our purchase order module, we need to ask certain questions depending on the source selection method, competition type and total cost of the PO. These questions are likely to change over time and in between different instances of the database. So I have a view containing the questions, so that I can add questions dynamically to my XPage without needing to change the code.

To be honest, doing this in a regular Notes form would have required either putting every question on the form or putting a bunch of fields on the form for all the answer fields. Either method would be sloppy and wasteful. There could have been dozens of unnecessary answer fields hidden by hide-whens and constant revisions between various instances of the database leading to a configuration management/version control nightmare.

Doing it in a repeat control, with the source view filtered by values passed in an array, I can get dynamically selected questions with only the necessary fields being stored on the document.

In order to do this, I had to use all of my minimal XPages knowledge, ask several questions on Stack Overflow, re-read portions of Mastering Xpages and scour the help documentation. You, however, can simply follow my guidance and have this solution for your own.

The Question FormCompetition Question Form

I created a relatively simple Notes form, based on the ones supplied to us for keywords by Teamwork Solutions. The layout is clean and very readable, plus re-using it allows more consistency within our application. A number of the fields are used to determine when and where the question will display (Category, Competition type, Source selection method and Dollar ranges). The checkbox values are derived from keyword documents, to allow maximum flexibility. I’m allowing five field types for answers: Yes/No, Dialog Box, Text Box, Date and Date Range. I had tried Rich Text, but was having problems with losing the handle to the document when saving, and since it wasn’t really required in this implementation, dropped that for later review. Sometimes, the question has a related question to extract more details, so I allowed two questions to be placed on one question document to ensure they display together. Down at the bottom, I’ve got the fields to which the answer fields will be bound on the Notes document. One hazard to allowing the field to be named in this configuration document is that there might be duplicate use of a field name, by the risk level strikes me as low.

Setting up the Repeat

The way that I started to understand repeats was actually when I set up jQuery mobile access to some data after viewing Richard Sharpe’s webinar for TLCC on Building Your First Mobile Application Using XPages I was able to take the knowledge I acquired doing that and use it to link from one Requisition to many Purchase Orders as demonstrated on this blog.

The repeat itself is not that interesting, but, one of the technical tricks in the setup is. As I’ve started to learn XPages, I’ve gotten comfortable with variables stored in applicationScope (available to everyone), sessionScope (available throughout the current user’s session) and viewScope (available on the current page), but had no idea when one would ever use requestScope variables. It turns out that they can be very useful to us here, though we’re going to access them using a dataContext rather than labelling them as requestScope variables.

Since I want to take a value from each Question document to determine the field to bind to on the Purchase Order document, the syntax and re-use will be far easier if I can establish a variable for that field name used within each row of the repeat. This way, we can use Expression Language to refer to the field on the Purchase Order document, binding to it dynamically.

<xp:repeat id="repeat2" rows="30" var="rowData" style="width:700px" value="#{competitionQuestionView}">
            <xp:dataContext var="fieldName">
                <xp:this.value><![CDATA[#{javascript:rowData.getColumnValue ("FieldName");}]]></xp:this.value>
        <--- insert code here --->

First, we want to display our question, with the layout handled by a table. We just grab the column value and display it.

       <xp:td style="width:200.0px;">
          <xp:label id="label1">

Then, in the right-hand cell, we have the various possible answer fields. Note that since only one is rendered (the rendering formulas being mutually exclusive), it doesn’t matter that we bind to the field multiple times, since only one binding actually appears on the page. Tim Tripcony guided me to this solution in his answer on Stack Overflow. By using array syntax, we bind to the field on the poDoc using the value of our requestScope variable, fieldName. Remember, as a requestScope variable, it is only “alive” for the duration of this data retrieval to create the field binding. The binding formula ends up being very simple: #{poDoc[fieldName]}

Here’s a portion of the answer fields to give you the flavor…

   <xp:inputText id="inputText1" style="padding-top:2px;width:400px">
      <xp:this.rendered><![CDATA[#{javascript:rowData.getColumnValue("FieldType") == "Text Box"; }]]></xp:this.rendered>
   <xp:comboBox id="comboBox1" style="padding-top:2px;">
      <xp:this.rendered><![CDATA[#{javascript:rowData.getColumnValue("FieldType") == "Dialog Box"; }]]></xp:this.rendered>
   <xp:inputText id="inputText2" style="padding-top:2px;text-align:left">
      <xp:this.rendered><![CDATA[#{javascript:rowData.getColumnValue("FieldType") == "Date"; }]]></xp:this.rendered>
      <xp:dateTimeHelper id="dateTimeHelper1"></xp:dateTimeHelper>
         <xp:convertDateTime type="date"></xp:convertDateTime>
   <xp:radioGroup id="radioGroup1" style="padding-top:2px;width:86.0px">
      <xp:this.rendered><![CDATA[#{javascript:rowData.getColumnValue("FieldType") == "Yes/No"; }]]></xp:this.rendered>
      <xp:selectItem itemLabel="Yes" itemValue="Yes"></xp:selectItem>
      <xp:selectItem itemLabel="No" itemValue="No"></xp:selectItem>

Since my Question document also allows for the addition of a comment question after the non-TextBox answers, I have another line on the XPage to display those (Note that the dataContext for the commentFieldName requestScope variable has to be added to that panel inside the repeat control as well).

<xp:tr id="commentRow">
   <xp:this.rendered><![CDATA[#{javascript:rowData.getColumnValue("DisplayComment") == "Yes"; }]]></xp:this.rendered>
   <xp:td style="border-bottom:grey solid 0px;padding-top:2px;">
      <xp:label id="label3">
   <xp:td id="commentCell" style="border-bottom:grey solid 0px;padding-top:2px;">
      <xp:inputText id="inputText3">

In my next blog post, we’ll look at how we filtered the view data source in order to only display the questions we want on a particular purchase order.

If you don’t already look at XPages questions on Stack Overflow, you really ought to, and if you haven’t been keeping up with TLCC’s webinars, check out the series.

Categories: Server-Side Javascript, Xpages | Tags: , , , , , , , | 1 Comment

Copying views from one database to many

Recently, one of our internal clients asked if we could change who could see a button on one view that is in many of our databases. That sounds pretty easy, right? All you have to do is change the template and those changes will be pushed by the Designer task into every database.

Well, unfortunately, while almost all of the 65 databases in question contain the same view, some of those views don’t link back to any template.

I’d learned a while ago that the old Notes.Net forum was no longer particularly active and that the community had decided to move questions to Stack Overflow.

To digress for a moment, Stack Overflow is part of the Stack Exchange “network of communities” and it’s a great place to both seek and provide answers with experts and budding experts. It was founded in 2010 by one of my favorites in the industry, Joel Spolsky, and Jeff Atwood. Joel’s blog, Joel on Software, provided me with a lot of insights into programming, running a business, dealing with people and, oddly enough, with the first news I had of trouble on the morning of 9/11.

So, if you have a Notes, XPages or other problem, go there and ask. I asked around on Stack Overflow (link) and got a recommendation on how to deal with it. Actually, I got a couple of ideas to choose from and Panu Haaramo’s idea was the best. Basically, get a handle to the view by it’s UNID and copy it to the destination database using XPages.

I put a combobox (fileChoices) on the page to allow the user to select which database to push to, hardcoding in the view name, but it could easily be generalized and looped.

// get a handle to this database
var thisdb:NotesDatabase = session.getCurrentDatabase();
// get server
var server = "MyServer/MyCompany";
// get filepath
var filepath = getComponent("fileChoices").getValue();
getComponent("status").setValue("Searching for "+filepath);
// get a handle to the destination database 
var dbTarget:NotesDatabase = session.getDatabase(server,filepath);
// delete the Manuals view
var oldView:NotesView = dbTarget.getView("Manuals");
if ( @IsNull (oldView) ) { getComponent("status").setValue("Null"); 
} else {
    getComponent("status").setValue(getComponent("status").getValue()+", Trying "+ dbTarget.getTitle());
    try { oldView.remove();
        // get a handle to this Manuals view
        // copy the view to the destination database
        getComponent("status").setValue(getComponent("status").getValue()+", Updated on "+filepath);
    catch (ex) {
        getComponent("status").setValue(getComponent("status").getValue()+", No update for "+filepath);

Any thoughts on how to improve the code (especially the null-checking, which I cannot tell whether it does any good or if the try-catch ends up doing everything) will be greatly appreciated, with credit provided and the code updated.

Somehow, when I posted this internally, it won “Most Popular Newsfeed of the Month” at our office in January. When I tell you I work for an international development company, that seems to make sense… until I point out that the development the rest of the company does is the other kind of development. That is projects designed to help people in developing countries. So, rather than show how well-received my post was internally, it shows that we’re still early on in our efforts at Social Business.

Categories: Utilities | Tags: , , | 3 Comments

Create a free website or blog at

%d bloggers like this: