Useful function for posting custom XPage forms

By: Rocky Bevins, Published: December 10th, 2012

I’ve written before that I use a bare-bones XPage setup (something like this dominoguru post). Working this way comes with it own set of problems — one being how to post a complex form (say if posting to ?CreateDocument and a $$Return field aren’t doing the trick) — the most obvious solution is to get the POST data and do your bidding manually — something like: http://lotusnotesblog.com/overriding-default-xpage-submit-with-a-custom-form/. This can be an annoyance with all the .appendItemValue() calls upon POST — the function below will handle all that stuff for you, along with handling images.

The SSJS

You could put this in a lib.

/*
 Again, I need to move to GIST -- forgive the weird breaks down there
 
 You will see me looping through param
 I had little success getting a full set of query parameters other ways 
*/
var createFromParams = function(doc, fn) {		
	var key,
	pMap = facesContext.getExternalContext().getRequest().getParameterMap(),
	imgFile,
	tempFile,
	correctedFile,
	success;
	
	for (key in param) {
		if (key.indexOf('img_') === -1) {
		        if (key.indexOf('view:') !=  -1) {
				doc.appendItemValue(key.replace(/.*:/, ''), param[key]);
			} else {
				doc.appendItemValue(key, param[key]);
			}
		} else {
			imgFile = pMap.get(key);					
			tempFile = imgFile.getServerFile();				
			correctedFile = new java.io.File( 
				tempFile.getParentFile().getAbsolutePath() + 
				java.io.File.separator + 
				imgFile.getClientFileName() 
			); 
			
			success = tempFile.renameTo(correctedFile); 

			doc.createRichTextItem(key.replace('img_', '')).embedObject(
				lotus.domino.local.EmbeddedObject.EMBED_ATTACHMENT, 
				'',
				correctedFile.getAbsolutePath(),
			null); 

			correctedFile.renameTo(tempFile);
		}
	} 
	
	fn(doc);
};

The XPage beforeRenderResponse event

<xp:this.beforeRenderResponse><![CDATA[#{javascript:
	var req = facesContext.getExternalContext().getRequest(),
        oldDoc,
	doc;

	if (req.getMethod() === 'POST') {
		// Set the Form programmatically
		doc = database.createDocument();
		doc.appendItemValue('Form', 'FormName'); 			
		
		createFromParams(doc, function(newDoc) {
			newDoc.save();

                        doc.recycle();
			newDoc.recycle();
		});
	} else if (req.getMethod() === 'POST' && param.id) {	
             // Something like this could work for saving
		oldDoc = database.getDocumentByUNID(param.id);
					
		doc.appendItemValue('Form', 'FormName');		

		createFromParams(doc, function(newDoc) {
		      newDoc.copyAllItems(oldDoc, true);				
		      newDoc.remove(true);
		      oldDoc.save();	

                      doc.recycle();
		      newDoc.recycle();
                      oldDoc.recycle();
		});
         }
}]]></xp:this.beforeRenderResponse>

Sample HTML Form

Our HTML form with no defined action since we’re posting to the same XPage

<form enctype="multipart/form-data" name="frm" id="frm" method="post" action="">
	<section id="personalinfo">
		<h2>Personal Information</h2>
		<label>First Name:</label>
		<input type="text" value="" name="FirstName" />
                 <label>Profile Pic:</label>
		<input type="text" value="" name="img_ProfilePhoto" />
	</section>
</form>

Note that if you want to use the upload image feature you must prefix the file name with ‘img_’ when writing the HTML (see the above form). This way you can specify where to put the thing (or you could just do img_Body).

Logging into Facebook and sending some data to your Domino server using JavaScript and the FB API

By: Rocky Bevins, Published: September 3rd, 2012

I hope to create a few posts on connecting Domino to various APIs. In this case we’re hooking into the FB App (defined on facebook.com) and getting the users information (to see a list of possible permission parameters click here). I wont cover much of the API specifically — as will be the running theme of this blog i’ve just dumped some code below.

The code below is only the client and is fairly straightforward. I will write a follow up soon with SSJS for posting the user data to a document and url based callbacks for things like unsubscribing.


window.fbAsyncInit = function() {
	var perms = {scope: 'email, user_activities, user_birthday, user_events'};
		  
    FB.init({
    	appId	:	'Your Application ID', // App ID, from facebook.com
        channelUrl:     'fb.xsp', // Domino XPage
        status	:	true, 
        cookie	:	true, 
        xfbml	:	true,
        oauth : true
     });

    var checkUser = function() {
    	FB.api('/me', function (res){
    	     var xhr = { 
    		url: 'User?CreateDocument',
    		content: (function () {
    		     return {
    			fbid: res.id,
    			FirstName: res.first_name,
    			LastName: res.last_name
    		     }
    	         }()),
    		timeout: 3500,
    		headers: {'accept' : 'application/json',
                'Content-type' : 'text/html'}
    	    };    		
    	
    	    dojo.when(dojo.xhrPost(xhr), function (r) {
    		console.log(r);
    	    });
    	});    	
    },
	
    fbLogout = function() {
        dojo.query('.fb-login').forEach(function (item, i) {
           console.log('logging out');
    	   FB.logout(function (response) {
                 var h = dojo.connect(item, 'onclick', function () {
    	      		  dojo.disconnect(h);
    	        	  fbLogin();
    	          });
                  item.innerHTML = 'Facebook Login';
    	   });
        });
    },
    
    fbLogin = function() {
     dojo.query('.fb-login').forEach(function (item, i) {
    	var h = dojo.connect(item, 'onclick', function () {
    		FB.login(function (response) {
    			if (response.authResponse) {
    			     var accessToken = response.access_token;
    			     item.innerHTML = 'Logout of Facebook';
    				
    			     checkUser();
    				
    			     dojo.disconnect(h);
    			     dojo.connect(item, 'onclick', function () { 
                                  fbLogout(accessToken)
                             });
    	  		}
    			else {
    		              console.log('fail');
    			}
    		}, perms);
    	});   	
    });
    };
    	
    // Setup events for login button, could go ahead and make this anonymous
    fbLogin();
    
    // Commented out, but you can force login on load
    /*
    FB.login(function (response) {
    	if (response.authResponse) {
             var accessToken = response.authResponse.accessToken;	
             checkUser();			
  	}
    }, perms);
    */
};

// Async loading of FB
(function (d) { var js, id = 'facebook-jssdk', ref =
	d.getElementsByTagName('script')[0];

	if (d.getElementById(id)) { 
              return;
        }

	js = d.createElement('script'); js.id = id; js.async = true;
	js.src = "//connect.facebook.net/en_US/all.js";
	ref.parentNode.insertBefore(js, ref); 
}(document));


Crawling with a JAVA agent

By: Rocky Bevins, Published: August 24th, 2012

The agent below is an example of the packages and methods you will need to crawl an html page, and make some changes to the structure with RegExp.

import lotus.domino.*;
import java.util.Vector;
import java.io.*;
import java.net.*;

public class JavaAgent extends AgentBase {
  public void NotesMain() {
    try {
     Session session = getSession();
     AgentContext agentContext = session.getAgentContext();
     
     URL url = new URL("http://example.com1/");
     InputStream is = url.openStream();
     int ptr = 0;
     StringBuffer buffer = new StringBuffer();
    	    
     while ((ptr = is.read()) != -1) {
           buffer.append((char)ptr);
     }

     // With the HTML as a string we can run some RegEx operations
     body = buffer.toString();  
     // Pattern matching  
     body.replaceAll("Dogs", "Cats"); 
 } catch(Exception e) {
      e.printStackTrace();
    }
  }
}

The modified HTML will need to be passed in a steam before being placed in a Body field. Something like:

// Assume we have everything between a pages body elements, 
//we can create a new page with that HTML like so
Stream stream = session.createStream();
stream.writeText("<html><body>");
stream.writeText(body);
stream.writeText("</body></html>");

Overriding Default XPage forms and responding to POST requests

By: Rocky Bevins, Published: August 24th, 2012

This post concerns overriding the default onsubmit event of an xpage — more specifically this is a way of posting if you’re planning on developing with a ‘stripped down’ set of native features. This post will over how to submit via POST and XHR POST.

Using a custom form:

  1. You will need to stop the default form from wrapping your content by either adding ‘createForm=”false”‘ to <xp:view> or clicking the properties pane and finding the property within ‘All Properties’.
  2. Now you need to create a simple form, you can omit the action for this example as we want to post to the current XPage. For example:
    <form id="exfrm" action="" method="post">
         <label>Name: </label> <input type="text" name="name" value="foo" />
         <label>Email: </label> <input type="text" name="email" value="bar"/>
         <button type="submit">Submit</button>
    </form>
  3. With the above code we can click submit and post to: example.xsp?fname=foo&email=bar.
  4. To process this post with the same XPage using SSJS we can do something like the following:
    <xp:this.beforeRenderResponse>
    <![CDATA[#{javascript:
         var req = facesContext.getExternalContext().getRequest(),
         doc;
         
         if (req.getMethod() === 'POST') {
              doc = database.createDocument();
              doc.appendItemValue('Form',  'Answer');
              doc.appendItemValue('FirstName', param.fname);
              doc.appendItemValue('InternetAddress', param.email);
              doc.save(); 
    
             // Redirect after posting
             context.redirectToPage('example.xsp?name=' + param.fname +  
             '&email=' + param.email );
        }
    }]]>
    </xp:this.beforeRenderResponse>

Thats it for posting the ‘standard’ way. Now for posting via XHR (we basically extract more information from the request header and if it was called with XHR we can respond with JSON or somethin’). But first we’ll need to override our default onsubmit event.

Note that I an using DOJOs AMD loader — this is because I have overridden the default domino resources. I don’t really care for most of the XPage controls as it concerns web development — I mostly enjoy being able to use SSJS. I really like DOJO 1.8 (and hope to write a nice tutorial about hooking up the new dojox calendar to domino as I’ve done with couchdb soon).

 

Using RedirectTo to dynamically define a redirection on session logout

By: Rocky Bevins, Published: August 24th, 2012

Most of you are probably already aware of this — you can dynamically redirect on login/logout by appending the redirectto parameter within the acted upon URL. This works with the ‘RedirectTo’ field found on $$UserLoginForms — typically outlined in your domcfg.nsf.

For Logging in and routing:
action="?login&redirectto=http://www.example.com"

For Logging out and routing:
action="?logout&redirectto=http://www.example.com"

Checking to see if a User is in a Group with SSJS

By: Rocky Bevins, Published: August 22nd, 2012

This is the simplest way (that I’m aware of) to confirm if a logged in user is part of a User Group in your directory:

if(@IsMember('webuser', session.getUserGroupNameList())) {
     // User was in the 'webuser' group, and now we can do something, like redirect
     context.redirectToPage('index.xsp?group=webuser');
}

Getting the name of an XPage

By: Rocky Bevins, Published: August 21st, 2012

Quick function for getting the name of an Xpage from a passed in URL:

var getXPageName = function(url) { return url.replace(/.*[^\/]\//, ''); }, 

nameOfPage = getXpageName(context.getUrl().toString()); 

Another (anonymous for kicks) for getting all of the parameters after the ending ‘.xsp’:

var paramStr = (function () { 
     return context.getUrl().toString().replace(/.*[^\/]\/*xsp/, ''); 
}());

Using SSJS to create a document/send an email with Xpages

By: Rocky Bevins, Published: January 11th, 2011

A few weeks ago I responded to a question on developerWorks about creating a document with SSJS, and again recently someone asked the same thing — so I figured it warrants a post despite the information already being covered in a few other places.

Put the code below within the onclick event
:

var doc = database.createDocument(); // Create the document
doc.appendItemValue("Form","formName"); // Assign the form
doc.appendItemValue("fieldName","FieldValue"); // Append something to a field
doc.save();

Looking at how this works you can easily see how you could send mail with SSJS – below is the script.

var doc = database.createDocument(),
msg = "Your username and password are below:",
body = doc.createMIMEEntity(),
subject = body.createHeader("Subject"),
stream = session.createStream();

doc.appendItemValue("Form","Memo");
subject.setHeaderVal("Email Subject");
stream.writeText("some text");
body.setContentFromText(stream, "text/html; charset=iso-8859-1", 0);
doc.send("putanemailhere@putanemailhere.com");

Hide View Pagers if they’re not needed

By: Rocky Bevins, Published: January 9th, 2011

Place the snippet below in the visibility attribute of the view pager control, and it will hide when it is unneeded (less than the defined number of documents). See screenshot below.

 testVw.getAllEntries().getCount() > 30; 

Use a scoped varible and pass it into the view’s “Maximum rows per page” and the visibility attribute of the view pager control, if you don’t want to use the static integer.