Old Notes

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="http://www.ibm.com/xsp/core">

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

</xp:view>

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;

url

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 )
				Else
					Messagebox "Opened the database, but could not open the document",48, "Failed"
				End If
			Else
				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

Copying design elements via script in #XPages

One of my first posts was about copying a view from one database to many using XPages. Well, as I was surfing the help documents to learn more about FTSearch in order to extend the capabilities of my Excel exporting capabilty, I found createNoteCollection.

createNoteCollection can be used to create a collection of all notes in a Notes database. That is, not just Notes documents, but all Notes design elements. Not only that, but you can quickly choose what kind of notes you want in your note collection. While there are boolean parameters for each type of note you might want, I am most intrigued by the combination ones, which allow you to select by category.

selectAllAdminNotes (ACL and replication formulas)
selectAllCodeElements (agents, database script, outlines, script libaries and misc code elemnts)
selectAllDataNotes (documents AND profile documents)
selectAllDesignElements (code, format, index, help, icons and shared fields)
selectAllFormatElements (actions, forms, framesets, image resources, java resources, misc format elements, pages, style sheets and subforms)
selectAllIndexElements (folders, misc index elements, navigators and views)
selectAllNotes (everything)

So, once you create  your note collection, then you can walk the collection using getFirstNoteID and getNextNoteID, accessing each design element (or document). Then, using NotesDatabase.getDocumentByID to get a handle to it as a NotesDocument, so you can use NotesDocument.CopyToDatabase to copy the design element across to the new database.

var db:NotesDatabase=database;
var notecollection:NotesNoteCollection=db.createNoteCollection();
notecollection.selectAllDesignElements();
var noteID:String=notecollection.getFirstNoteID();
var note:NotesDocument;
var nextNote:NotesDocument;
while ( noteID != null ) {
	note=db.getDocumentByID(noteID);
	if ( note != null ) {
		note.copyToDatabase();
	}
	noteID=notecollection.getNextNoteID();
	nextNote=db.getDocumentByID(noteID);
	note.recycle();
	nextNote=note;
}

Now, it is a little blunt to grab all design elements and copy them from one database to another. One problem is that you are certain to have some duplication of design elements and I’m sure that will create problems. I know that I was able to have two identically named views in my original post, so I didn’t copy the view if it already existed in the destination database. So, you might want to create collections in both databases and compare the design notes t0o make sure not to create duplicates (either by deleting the one in the destination database first or simply not copying the new one in).

Categories: Old Notes, Server-Side Javascript, Utilities, Xpages | Tags: , , | Leave a comment

Finding user roles in #XPages

I’ve written a piece before on roles in XPages, but since that dealt with using the ACL to limit access to a page and not about the programmatic use of roles, I wanted to return to the issue.

Back in old Notes, if we wanted to hide something based if the user did not have a certain role in the ACL, we could use a pretty simple formula:

!@IsMember(“[roleNameHere]”; @UserRoles)

Remarkably, it’s not that much harder in XPages, but there are some important wrinkles to be concerned about. As noted previously and by Russ Maher on his blog, you must remember to use your brackets [] and also keep in mind the result you want (true or false). Remember that the XPages formulas are ‘rendered’ formulas, meaning you want to ‘true’ to display the result and ‘false’ to hide it, so you’d use:

@IsMember(“[roleNameHere]”, context.getUser().getRoles());

Here’s the more complete source code for the rendered formula:

<xp:this.rendered><![CDATA[${javascript:@IsMember("[Testing]", context.getUser().getRoles());}]]></xp:this.rendered>

Now, I’m not sure what impact of referencing the user roles that way has on performance, but since I now I’m using them in many rendered formulas all over my application, I decided to compute it once and then reuse it many times. I put a few extra lines into a control that’s on my main application layout control to drop it into a sessionscope variable. I suppose I might shave a millisecond off if I only computed that once per session, but I didn’t go that far.

<xp:this.beforePageLoad>
<![CDATA[${javascript:var roles = context.getUser().getRoles();
sessionScope.userRoles = roles;}]]>
</xp:this.beforePageLoad>

Then, in order to check to see if my user has one of three roles when rendering an item, I could use this code:

<xp:this.rendered><![CDATA[${javascript:var manager = @IsMember("[InventoryMgr]", sessionScope.userRoles);
var viewer = @IsMember("[InventoryViewer]", sessionScope.userRoles);
var grantsManager = @IsMember("[InvMgrGrants]", sessionScope.userRoles);
if ( manager || viewer || grantsManager ) { return true };
return false; }]]>
</xp:this.rendered>

Now, I also found that sometimes I need to determine the user’s role in my Java code. That’s also not that hard, except that vectors are not quite arrays. If there is a single value, it’s not the same as a multiple value vector. I’m not sure if this is a Notes implementation issue or if it’s the way Java always handles vectors. That is, if it’s a single value, it puts our brackets [] around the value, but it does NOT for multiple values. So, when I was using the code written for us, it wasn’t always picking up the roles correctly. Once I simply told it to check both ways, our code worked more cleanly. (The reference to ExtLibUtil comes from the original code, so I didn’t modify it.)

public Vector getCurUserRoles() {
	try {
		curUserRoles = ExtLibUtil.getCurrentDatabase().queryAccessRoles(ExtLibUtil.getCurrentSession().getEffectiveUserName());
	} catch (NotesException e) {
		this.debug("getCurUserRoles ERROR: " + e.getMessage(), "error");
		curUserRoles = new Vector();
	}
	return curUserRoles;
}

public boolean hasRole(String role, String uname) {
	try {
		Vector roles = this.getCurUserRoles();
		if (roles.contains(role))
			return true;
		if (roles.contains("["+role+"]"))
			return true;
		return false;
	} catch (Exception e) {
		this.debug("hasRole ERROR: " + e.getMessage(), "error");
		return false;
	}
 }

Note that the debugging uses Mark Leusink‘s DebugToolbar, which I highly recommend to everyone.

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

Shared Action Confusion in Notes

My co-worker, Virginia Tauss, just ran across the most annoying thing in an “Old Notes” database. The database has a number of Shared Actions, some of which are used on forms and views that are inherited explicitly from one of our templates. In our system, each of the projects we support gets a database to manage their project, built off a base design, then modified to meet their needs. Thus, the database does not inherit it’s entire design from the template. In fact, there are some design elements that are drawn from a second template (our Field Operations Manual has several forms and views that are used in all databases).

In my recent design changes, I started using Shared Actions in the Field Operations Manual design, for all the reasons everyone likes Shared Actions – code reusability, simplicity and space savings.

Unfortunately, when a form or view inherited from the Field Operations Manual uses a Shared Action, the inheriting database would not display the same action as had been designated in the template.

Shared Actions rely upon something called a ‘share-id’, which has nothing to do with the name of the action, the order in which it appears in the list of actions in the database or the Note ID of the design element. That is to say, unique to each implementation, rendering Shared Actions far less useful in our multi-template inheritance environment.

From the IBM TechNote

This issue is related to how Shared Actions are referenced. They are not referenced by Note ID, by name, or by their order in the Shared Actions list. They are referenced by a property called ‘share-id’. If a Shared Action is copied to another database the share-id for the action is not necessarily preserved. The issue typically occurs after a design element is updated and the design of the database is refreshed.

Needless to say, this was an eye-opener. I’d never had databases using multiple templates before and hadn’t a lot of experience with Shared Actions in templates. One of my friends, Greg Ehrig, was a ski instructor at one point and he always said, “If you aren’t falling down, you aren’t learning.” Notes and XPages give us all plenty of chances to fall down, so plenty of chances to learn!

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

Blog at WordPress.com.

%d bloggers like this: