Switch between Edit and Read-Only mode on web form

A question on Stack Overflow made me remember some code I wrote a few years ago. It allows you switch a form between regular edit mode and read mode, without having to reload the page. It works just like you are used to in the Notes client. So I thought I would post it here on my blog as well.

It is not very complicated. I am using jQuery, but you can of course use plain Javascript if you like. What the code does is to locate all INPUT fields you want to make read-only. It then creates a regular DIV element and set the content of it to the value of the INPUT field. The id and a couple of other attributes are also copied over, then the new DIV is inserted in front of the INPUT field. Finally the INPUT field is deleted.

To make the DIV editable again, the same process is done in reverse.

Below is the jQuery code to make all elements with the data-attribute dominofield read-only. I am using this data-attribute to map input fields to fields in a Domino database. It makes it very easy to create HTML forms and submit them to a Domino database, with one generic agent that will process the Ajax call. The field names and values will be provided in the JSON payload, and the Domino document can then be created or updated and the fields populated with the proper values.

  // Get all input fields used for Domino
  var inputs = $('[data-dominofield]');
  // Process each field
  inputs.each(function() {
    // Build new DIV element
    var input = $(this);
    var div = '<div class="fieldReadOnly" ';
    div += 'data-dominofield="' + input.data('dominofield') + '" ';
    div += 'id="' + input.attr('id') + '">';
    div += input.val() + '</div>';
    // Insert ther new div element in front of input field
    input.before(div);
    // Remove input field
    input.remove();
  });

I also created a fiddle where you can test it yourself.

If you are using Bootstrap, you can also use the readonly attribute and the class .form-control-plaintext to get the same result. This is documented here.

 

0 Comments

My MWLUG presentation: Elementary!

Yesterday I presented at MWLUG, and I want to share my presentation with both the ones attending and anyone who was not able to be there. I am posting two version, one with just the slides, and one with speaker notes, where I tried to capture the content, if not the exact verbiage of the session.

I hope to be able to post the demo database with the code later this week or early next week.

 

1 Comment

My presentation at MWLUG

Tomorrow, August 8, you are welcome to attend my presentation “Elementary!” at MWLUG 2017. In about 45 minutes I will show how to easily incorporate Watson functionality in your own applications, both on the web and in your Notes client applications.

I will be using Node-RED and IBM BlueMix to do this, and I think many will be surprised how easy it is, and how little code is needed. For example I will implement translation from English to Spanish with two (2) lines of server side code. To call this from the web you just need another handful of lines.

I hope to see you tomorrow at 5pm!

0 Comments

Load and Modify External File in NetSuite

When building a suitelet in NetSuite you can either inject HTML, CSS and Javascript in a field, or generate a full HTML page and render it into the suitelet. No matter which method you use, you normally have to write line after line of SuiteScript code where you build the HTML using string concatenation. This is not only difficult and tedious to write, making sure you match all the single and double quotes and semi colons, it also makes the code much harder to maintain.

What if you could just create a regular HTML file, put it in the File Cabinet and then render it into a suitelet? And what if you could use one line of code to inject values from NetSuite in the correct place in the HTML? This could be search results from the use of my search function.

That is what the function looks like:

/**
 * Load file from NetSuite File Cabinet and replace placeholders with actual values
 * 
 * Version    Date            Author           Remarks
 * 1.00       07 Nov 2016     kmartinsson      Created class/function
 * 1.01       08 Nov 2016     kmartinsson      Consolidated setValue and setHTML into
 *                                             one method and added noEscape parameter
 */
// ***** Read and process external file, replacing placeholders with proper values *****
function ExternalFile(filename) {
   //Get the file by path/name, can also be internal id
   var fileId = filename;
   // Load file content and store data
   var file = nlapiLoadFile(fileId);
   var data = file.getValue();
   this.content = data;

   this.setValue = function(placeholder, value, noEscape) {
      // Check if noEscape is passed, if it is and if true then don't escape value.
      // This is needed when value contains HTML code.
      if (typeof noEscape == "undefined") {
         this.content = this.content.replace(new RegExp(placeholder, 'g'), nlapiEscapeXML(value));
      } else {
         if (noEscape == true) {
            this.content = this.content.replace(new RegExp(placeholder, 'g'), value);
         } else {
            this.content = this.content.replace(new RegExp(placeholder, 'g'), nlapiEscapeXML(value));
         }
      }
   }

   this.getContent = function() {
      return this.content;
   }
}

Reference this function in your Suitescript 1.0 code like this:

// Load extrenal HTML file
var html = new ExternalFile("SuiteScripts/BinTransfer.html");
// Insert NetSuite URL for CSS files
var cssFileName = nlapiLoadFile("SuiteScripts/css/drop-shadow.css").getURL();
html.setValue("%cssDropShadow%", cssFileName, true);
cssFileName = nlapiLoadFile("SuiteScripts/css/animate.css").getURL();
html.setValue("%cssAnimate%", cssFileName, true);
// Insert array returned from a search
html.setValue("%binarray%", JSON.stringify(binArray), true);
// Replace placeholders with values
html.setValue("%showAll%", "false");
html.setValue("%company%", companyName);

The last (optional) argument “noEscape” decides if the value should be URL encoded (false/omitted) or not (true) using the function nlapiEscapeXML(). In most cases you don’t need to specify this argument, but if you need to pass HTML or other code into the function you need to set it to true to avoid the code being modified.

As you can see in my example above, I get the NetSuite URL for my CSS files as well. Instead of hard coding the NetSuite URL into the HTML page, I calculate it and insert it when the page is loaded. Not only does it make the page easier to read the code, it also makes it much easier to maintain.

This is a snippet from the HTML file:

<!-- Load plugins/drop-shadow.css from File Cabinet -->
<link href="%cssDropShadow%" rel="stylesheet">
<!-- Load bootstrap-notify.js and animate.css from File Cabinet -->
<script src="%jsBootstrapNotify%"></script>
<link href="%cssAnimate%" rel="stylesheet">

Much easier to read!

Thanks to this little function I have built suitelets who does nothing but load a traditional HTML file with Bootstrap, jQuery, even jQuery Mobile for mobile devices. The page contains Javascript/jQuery that call RESTlest to read and write data. Now I can build suitelets with all the power I have in traditional web development at the same time as I get access to the full NetSuite functionality!

This can also be used to generate XML files to convert into PDF.

Happy coding!

 

0 Comments

IBM Connect 2017 – I will be speaking in San Francisco

I will be speaking at IBM Connect in San Francisco now in February. Rob Novak has resurrected “The Great Code Giveaway” and asked me to present it together with him. Who would turn down that opportunity? So some time between February 21 and 23 you can see Rob and me on stage at Moscone West. The exact time and location has not been announced yet.

I hope to see you in San Francisco and that you will find our presentation and code useful!

1 Comment

IBM Notes, Domino and the future

As some may already know I was recently laid off after 14 years as a Notes and Domino developer at my workplace. I suspected for a while that some staff reduction would be coming soon, but I was a bit surprised that I was included since I am the only Notes developer in the company.

I had for a while considered to do consulting and freelance development. My wife as well as several friends have been encouraging me for years. So this was just the push I needed.

Demand Better Solutions Logo

I am starting my own company, Demand Better Solutions, where I will focus on Notes and Domino Development, application modernization and migration as well as building brand new web applications and websites.

I realize that me being laid off is just a business decision. It is not personal. Several of the business critical applications at my former employer are developed using IBM Notes, but the executives have for years been talking about moving away from the platform. Of course they don’t realize the huge amount of work needed to do this, but never the less this was/is their ultimate goal.

The reason is that they feel (based on what they hear from other executives) that Notes is old technology. The fact that IBM has been slow in modernizing the interface, and that many of the templates still look like back in 1999 when version 5.0 was released does not help this perception.

Last fall all our email at my old job was moved to Outlook, and ever since I have heard users complaining about missing Notes and certain functionality they were used to. A lot of integration between Notes applications and Notes mail were also lost, and I had to re-create it in different ways. You often hear stories about people complaining about the Notes client, but most of our users wanted nothing but to get it back…

My old employer also uses Visual FoxPro, a product where the last version was released in 2004. It has officially been discontinued by Microsoft, but we use it for several important applications. So I don’t think that even a product being discontinued is driving a huge number of migrations. It is the perception of how modern the product is that matters. And that perception is almost 100% the way the product looks.

To a user the interface is the product.

Create a modern looking application and nobody will question (or care) what tool was used to build it.

The last 3-4 years I have been learning new web technologies, like jQuery, Bootstrap, Ajax, JSON. I have been able to use much of that at work, as well as in several side projects. I also started learning C# and .net. After the layoff I sat down and started looking at (among others) php and mySQL as well as researched frameworks like AngularJS.

As a developer I have to keep up with new technologies, or I will be left behind. But it is hard when you work full-time, have side work and then have a family and house to take care of. Having some free time the last few weeks enabled me to focus on learning some new things.

I don’t think the Notes client will be developed much more, almost everything is moving towards web applications these days anyway. But IBM Domino is something totally different. It is an very capable and powerful development platform. With some skills in web technologies and a good understanding of the Domino platform one can build some amazing applications.

IBM recently released FixPack 7 and announced that the current version of Notes and Domino will be supported for at least five more years, until September 30, 2021. New functionality will be provided through Feature Packs, not version upgrades.

But Domino is just one tool of many. I am looking at LDC Via as another data store, as it very closely resembles Domino with a MongoDB-based NoSQL backend. Salesforce also has many similarities with Domino. The transition would therefore be fairly easy. AngularJS is another popular technology, with version 2.0 soon to be released. And we of course have IBM’s BlueMix offering, where MongoDB is just one of many technologies offered.

As a developer we need to learn new things constantly, the language or tools we use does really not matter. We should pick the proper tool, whatever fits the project.

Do you want to modernize your Notes and Domino applications?
Let me and Demand Better Solutions help you!

2 Comments

My MWLUG presentation

I have been very busy ever since the MWLUG conference in Austin, but now you can finally view my presentation and download the sample code. Enjoy!

 

MWLUG_2016

I will post the code for my Phonegap Demo next week.

Reminder: you need to sign the database (or at least all the agents) with an ID who has the rights to run agents, or the Ajax calls will not return anything.

If you are interested in having your Notes applications modernized and moved to the web, feel free to contact me at karl-henry@demandbettersolutions.com.

0 Comments

MWLUG in Austin – I will be presenting again

I have been selected to present at MWLUG in Austin on August 17-19. My presentation will be kind of part two of my presentation last year in Atlanta. It will focus less on the basics and go more into the fun and more advanced stuff. Kind of an extended version of my Connect 2016 presentation.

The title is “Think Outside The Box – Part 2”, and I will discuss and show how you can build a modern web front-end using standard techniques like Javascript/jQuery and frameworks like Bootstrap and jQuery Mobile and have it work against a Domino backend. I will demonstrate how to easily read data from and write data to the Domino database, and how to consume data using free plugins like BootstrapTable and FullCalendar.

I will also discuss the difference between JSON and JSONP and why the latter usually is better when building this type of integration. You will leave with a sample database containing the source code all the demos I will be showing as well as Lotusscript script libraries with classes I built to easily build agents that will interact with the website.

The idea is that you should be able to attend my session in Austin even if you haven’t seen any previous presentation. I will assume you have basic web design skills (HTML, CSS and a working understanding of Javascript) but you don’t have to be an expert at all. I also recommend some Lotusscript knowledge, as I will be providing all attendees with plenty of code to bring home and start using yourself.

I hope to see you in Austin in August! If you haven’t registered yet, go ahead and do it now! There are still seats left.

0 Comments

My Connect 2016 presentation & demo database

Connect2016_DemoDesignAs I promised, I would post my IBM Connect 2016 presentation on my blog.

Presentation (PDF): {link} 

Demo database (ZIP): {link}

You can also find the presentation on SlideShare.net.

To the right you see the database design, you want to look in the Agent section for the agents and in the Pages section for the HTML pages.

Note: You need to sign the database with an ID that have the proper rights. Otherwise the code will not work.

Enjoy!

 

6 Comments

IBM Connect 2016 coming up!

Connect2015_People1In the end of January it is once again time to head to Orlando for the yearly conference that for many years was known as Lotusphere. For the last few years it have been renamed IBM Connect (as well as ConnectED in 2014), and last year most people (including me) thought that 2015 was the end of this conference.

But perhaps due to the popularity of the 2015 edition, IBM decided to have the conference again this year, but in a new location as the contract with Swan and Dolphin (where the conference had been held since the first one in 1993) had expired.

The fountain at WDW Dolphin
The fountain at WDW Dolphin

The new venue is Hilton Orlando. It is closer to the airport and there are also more restaurants around than at Swan and Dolphin. It is close to SeaWorld as well as to the Universal Studios theme parks. Personally I am excited about the new venue. “Swolphin” (as Swan and Dolphin often was referred to) started to get old and worn down, despite a refresh of the rooms back in 2003-2005 some time. Yes, after this many years (18 in a row for me) Swolphin started feeling like a home away from home. You know where everything is, you know the staff and the shortcuts between hotels and sections within the hotel.

So a new location makes Connect 2016 more exciting, it will feel like a new conference but hopefully with many of my old friends attending. I have already found several interesting sessions using the session tool. Philippe Riand and Jesse Gallagher will for example talk about the Darwino application development platform, which allows you to integrate your Domino applications with IBM Bluemix an IBM Connections. Another must-attend session is called IBM Domino App.Net and talks about how to utilize Bluemix to build XPages applications in the cloud.

In addition we of course have all the sessions we have come to know and love: UserBlast with Mat Newman, Admin Tips Power Hour presented by Chris Miller, Spark Ideas, and of course the Opening General Session (OGS) with a secret guest speaker as the tradition requires.

HP_GringottsAfter the fiasco last year with the Tuesday evening special event the organizers went back to holding the event in one of the local theme parks. For the second time it will be held in the Wizarding World of Harry Potter – Hogsmead, which is part of Universal’s Islands of Adventure. Last time I had a blast, so much that last year I took a couple of vacation days to visit Hogsmead again as well as the then newly opened Diagon Alley extension over in the Universal Studios park next-door. You need a park-to-park admission pass to visit both parks, but that allows you to take the Hogwarts Express between the two parks.

ConnectSessionLetterFor me personally Connect 2016 is a milestone. It will be my 20th Lotusphere/Connect and for the first time I will present a session!

This is not a full one hour session, but a new format called Lightning Talk. Those are shorter 20 minute sessions, and not all of them are listed in the session tool on the conference site yet. I think this is a very interesting concept, and I believe it will be successful. I can see myself attending a number of shorter sessions like this to get a good overview of a particular subject, then go on and learn more later.

My session is called “Think outside the box” and I will show how you can connect to a Domino backend from a traditional web application and retrieve data in JSON format. This data can then be used to populate fields/values on a page or used in jQuery/Bootstrap plugin like calendars and data tables. This is a version of the presentation I did at MWLUG in Atlanta, but shorter and with some new content added.

I hope to see you at Connect 2016 in Orlando, perhaps even at my session. If you haven’t registered yet, it is time to do it now. Stay tuned for more posts here leading up to the conference.

1 Comment

Calling a Notes web agent from another server using JSONP

In my MWLUG presentation (as well as in a couple of entries on this blog) I talk about how you can access Domino data from a regular webpage using jQuery and a Lotusscript agent returning data as JSON. The issue with this solution is that the web page must be on the same web application path as the Domino agent. You can’t do what’s known as cross-domain Ajax.

For example if the Domino server is domino.texasswede.com but you have the webpage hosted at www.texasswede.com, it will not work. The security in Javascript does not allow calls across servers like that. There is however an easy solution, and it is called JSONP. What you do is to return not JSON but a call-back function with the JSON data as the argument.

So instead of returning this:

{ "firstName":"Karl-Henry", "lastname":"Martinsson","email":"texasswede@gmail.com" }

you would have the Lotuscript agent return this:

personCallBack({ "firstName":"Karl-Henry", "lastname":"Martinsson","email":"texasswede@gmail.com" })

Let’s assume we call the agent GetPersonData.jsonp.  On the calling side (in the jQuery code) you would then use this code:

$.ajax({
    url : "http://domino.texasswede.com/database.nsf/GetPersonData.jsonp?OpenAgent",
    dataType:"jsonp"
});

Finally you write the Javascript call-back function that will accept the data:

function personCallBack(data) {
    $("#firstName").html(data["firstName"]);
    $("#lastName").html(data["lastName"]);
    $("#emailAddress").html(data["email"]);
}

You can of course make this as advanced as you like but this is the basics. I have updated the JSON class I use for stuff like this to include a method to return JSONP. The new function is called SendJSONPToBrowser() and takes a string with the name of the call-back function as argument, for example like this:

Call json.SendJSONPToBrowser("personCallBack")

Below is the updated class (if you downloaded my sample database from MWLUG you have the older version of it). Enjoy!

 

%REM
	Library Class.JSON by Karl-Henry Martinsson
	Created Oct 9, 2014 - Initial version
	Updated Nov 6, 2015 - Added JSONP support
	Description: Class to generate simple JSON from values
%END REM

Option Public
Option Declare

Class JSONdata
	Private p_json List As String
	
	Public Sub New()
		'*** Set default value(s)
		me.p_json("ajaxstatus") = ""
	End Sub
	
	%REM
		Property Set success
		Description: Set success to true or false
	%END REM
	Public Property Set success As Boolean
		If me.success Then 
			Call me.SetValue("ajaxstatus","success")
		Else
			Call me.SetValue("ajaxstatus","error")
		End If
	End Property
	
	%REM
		Property Get success
		Description: Not really used...
	%END REM
	Public Property Get success As Boolean
		If me.p_json("ajaxstatus") = |"success"| Then
			me.success = True
		Else
			me.success = False
		End If
	End Property
	
	%REM
		Sub SetMsg
		Description: Set msg item
	%END REM
	Public Sub SetMsg(message As String)
		Call me.SetValue("msg",message)
	End Sub
	
	Public Sub SetErrorMsg(message As String)
		Call me.SetValue("errormsg",message)
		me.success = False
	End Sub
	
	Public Sub SetValue(itemname As String, value As String)
		Dim tmp As String
		Dim delimiter As String
		'*** Check for quote (double and single) and fix value if needed
		tmp = Replace(value,Chr$(13),"<br>")
		tmp = FullTrim(Replace(tmp,Chr$(10),""))
		If InStr(tmp,|"|)>0 Then
			If InStr(tmp,|'|)>0 Then
				tmp = Replace(tmp,|"|,|"|)
				delimiter = |"|
			Else
				delimiter = |'|
			End If
		Else
			delimiter = |"|
		End If
		'*** Store value with delimiter in list
		me.p_json(itemname) = delimiter & tmp & delimiter
	End Sub
	
	Public Sub SetData(itemname As String, value As String)
		'*** Store value in list
		me.p_json(itemname) = value
	End Sub
	
	%REM
		Function GetJSON
		Description: Return a JSON object as text
	%END REM
	Function GetJSON As String
		Dim json As String
		'*** Opening curly braces + CR
		json = "{" + Chr$(13)
		'*** Loop through all list elements and build JSON
		ForAll j In me.p_json
			json = json + |"| + ListTag(j) + |":| + j + "," + Chr$(13)
		End ForAll
		'*** Remove the comma after the last item
		json = Left$(json,Len(json)-2) + Chr$(13)
		'*** Add closing curly bracket and return JSON
		json = json + "}"
		GetJSON = json 
	End Function
	
	%REM
		Sub SendToBrowser
		Description: Print JSON to browser, with correct MIME type
	%END REM
	Public Sub SendToBrowser()
		'*** MIME Header to tell browser what kind of data we will return (JSON).
		'*** See http://www.ietf.org/rfc/rfc4627.txt
		Print "content-type: application/json"
		Print me.GetJSON
	End Sub
	
	%REM
		Sub SendJSONPToBrowser
		Description: Print JSON to browser, with correct MIME type
	%END REM
	Public Sub SendJSONPToBrowser(callbackFunction As String)
		'*** MIME Header to tell browser what kind of data we will return (Javascript).
		'*** See http://www.rfc-editor.org/rfc/rfc4329.txt
		Print "content-type: application/javascript"
		Print callbackFunction + "(" + me.GetJSON + ")"
	End Sub
	
End Class

 

0 Comments

Microsoft releases Visual Studio 2015

Microsoft today released the latest version of their development environment Visual Studio. There are even free versions, including the complete IDE Visual Studio Community and the code editor Visual Studio Code (available for Widnows, Linux and OSX).

Visual Studio now includes even more tools for cross platform mobile development for iOS  and Android. There is even an Android emulator included. The web development part supports tools and frameworks like Angular, Bootstrap, jQuery, Backbone and Django.

And naturally the IDE also supports Windows, including Windows 10 (expected to be released at the end of the month).

I have been using tools in the Visual Studio family for many years, I started with a beta of Visual Basic 1.0 a long time ago, and used all version up to and including VB 6.0. I also played around some with Visual C++ and even Visual J++. After that I focused mainly on Lotus Notes development, but recently I have started some C#/.NET projects at work using Visual Studio Community 2013.

0 Comments

MWLUG in Atlanta – I will be presenting!

It is less than 7 weeks left until MWLUG, the Midwest Lotus User Group conference. This year the conference takes place in Atlanta, between August 19 and 21. During the three days there will be over 40 technical session and workshops on collaboration, receptions and networking opportunities, as well as access to experts of IBM solutions, both from IBM and other companies. The topics includes application development, system administration, best practices, customer buisness cases and innovation/future plans by IBM. Breakfast and lunch is included for two days as well. And all this for the cost of only $50 per person! The event takes place at Ritz-Carlton in downtown Atlanta. There is a block of rooms reserved at a special conference rate of $149.00 per night.

One of the sessions will also mark my personal debute as a speaker at a conference. I will present “Break out of the box – Integrate existing Domino data with modern websites” where I will talk about how to integrate websites built either within Domino or on other platforms with backend data that resides in a Domino database. I will talk about how you can build a modern looking website using tools like jQuery and Bootstrap and seamlessly integrate them with existing data on your trusty Domino server using JSON and Ajax. I will also provide plenty of example code ready for you to bring home and start playing with.

A number of IBM Champions will be presenting, as well as IBMers and other industry experts. So no matter your interest, I am sure you will find plenty of good sessions. I am sure I will have a hard time picking which sessions to attend!

So what are you waiting for? Go to http://www.mwlug.com and register! See you there!

 

1 Comment

Microsoft and jQuery Ajax calls – not using standards

I recently started using C# and .NET for the first time to build a web application. It is just a proof of concept application where I am trying to implement CRUD (Create, Read, Update and Delete) though jQuery and Ajax calls from a simple webpage. The application should let me retrieve a list of all companies in the database, get information about a specific company (based on a company id), update an existing company (and create a new company in the database if it does not exist) and finally allow a company to be deleted.

I been doing these things for years using IBM Domino as the backend, simple reading the query string and parsing the name-value pairs before performing actions based on the values. So when I started using Visual Studio I naturally thought things would work the same there.

But I found out that ASP.NET is using a different method to address web resources. It uses the concept of routes, so instead of adding information passed to the server using the query string, the data is passed in the actual URL:
To get a list of all companies you would call /api/Company, and to get information about a specific company you add the company id (/api/Company/Q1234). If I want to pass two arguments to the server, for example to get all companies in a specific city and state, you would call /api/Company/TX/Dallas.

In my opinion, this gives you much less flexibility than if  you pass arguments in the query string. You must put the arguments in the correct order, and it is much harder to use optional arguments. One example of where I used optional argumenst is for sort order. In some cases I want the companies to be returned in descending order, instead of the default ascending. Or I want to sort on a specific column/values. In those cases I pass along a special argument, but normally I don’t. Less data to transfer that way, and cleaner code. But it still works.

It is when you want to perform a POST of form data to the server that it get really complicated and annoying. This is the sample code using the ASP.NET Web API generated by Visual Studio 2013:

// POST: api/Company
public void Post([FromBody]string value)
{
    ... do stuff here
}

As you perhaps can tell, this function only take one argument, which is pretty useless in a real application. And you can’t just add additional arguments in the declaration. One way to do it (as described here) is to use a data transfer object containing all the arguments, which then is used in the function:

public class CompanyDTO
{
    public string CompanyID { get; set; }
    public string LegalName { get; set; }
    public string Address { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string ZIP { get; set; }
}

// POST: api/Company
public string Post(CompanyDTO Company)
{
    return Company.LegalName + " in " + Company.City + ", " + Company.State;
}

The issue here is that you need to put the arguments in exactly the same order in the Ajax call (and hence the query string) as they are declared in the code. You also can’t (as far as I understand) send only updated values, you need to always send all the fields, even if just one field has been changed.

So what should one do? The best solution I found this far is to pass the data from the browser as a string, containing JSON. Not as a JSON object, as that will not work.

So do not do this:

$.ajax({
    type: "POST",
    url:  "api/Company/",
    data: {'city':'Dallas','legalname':'Test Company, LLC'},
    contentType: "application/json; charset=utf-8",
    dataType: "json"
});

The browser will just convert the JSON object to name-value pairs, and you end up with null in your code. Instead, change the jQuery code to this:

$.ajax({
    type: "POST",
    url:  "api/Company/",
    data: "{'city':'Dallas','legalname':'Test Company, LLC','Owner':'Karl-Henry Martinsson'}",
    contentType: "application/json; charset=utf-8",
    dataType: "json"
});

Now it will work! Notice the small change, I added quotes around the JSON. An added bonus is that you don’t need to pass along all fields, just the ones you want/need in any order. And if you pass along a field/name that is not defined on the server, it will simply be ignored.

You probably don’t want to build the data string manually. Perhaps you want to loop though certain fields and retrieve the values to pass to the server. You would do something like this:

// Create a new empty object
var Company = { };
// Loop through all elements with the class 'dataField' and
// build an object with the ID of the element as the name.
$('.dataField').each(function() {
  Company[this.id] = this.value;
});

$.ajax({
  type: "POST",
  contentType: "application/json; charset=utf-8",
  url: "/api/Company",
  data: JSON.stringify(Company),
  dataType: "json"
});

I use JSON.stringify to convert the JSON object to a string before sending it to the server.

So this is what I found out. Hopefully it will help someone. I am still a bit frustrated that Micorosft once again decided to do things a different way than the rest of the world, but I guess one should not be surprised at that.

 

 

 

 

3 Comments

File Upload in Classic Domino Web using jQuery and Bootstrap

This week I was asked to create a simple web form where customers could fill out a few fields, attach two files and submit it for review. The document with the information and attachments are saved into a Domino database, so it can be processed thought the Notes client by the internal staff.

These days I mainly use Bootstrap (and jQuery) to design the webpages, since Bootstrap makes it very quick and easy to get a nice clean look of the page. Using jQuery allows me to do some nice manipulations of the DOM, hiding and showing sections as needed for example, or disable the submit button until all required fields have been filled out.

It has been a long time since I worked with the file upload control in Domino, and it was as ugly as I remembered it. But I knew I had seen some nice jQuery/Bootstrap file upload controls, so I located one that I liked in the Jasny plugin library. If you haven’t already, take a look at those components!

So how do I tie this control to the Domino file upload control? As so many times before, Jake Howlett and his excellent site CodeStore comes to the rescue. He wrote an article back in 2005 about how to fake a file upload control, and that code can be used as-is, and combined with the Jasny plugin.

Here is what my code looks like after doing that:

<div class="col-md-6">
  <label>Producer Agreement</label>
  <!-- File Upload -->
  <div class="fileinput fileinput-new input-group" data-provides="fileinput" title="Attach file here">
    <div class="form-control" data-trigger="fileinput">
      <i class="glyphicon glyphicon-file fileinput-exists"></i>&nbsp;
      <span class="fileinput-filename"></span>
    </div>
    <span class="input-group-addon btn btn-default btn-file">
      <span class="fileinput-new">Select file</span>
      <span class="fileinput-exists">Change</span>
      <input type="file" name="%%File.1" class="required">
    </span>
    <a href="#" class="input-group-addon btn btn-default fileinput-exists" data-dismiss="fileinput">Remove</a>
  </div>
</div>

On the second file upload control I just change the name to “%%File.2”. The form tag must have the encoding set to multipart/form-data, so this is what it looks like for me:

<form name="SubmissionForm" id="SubmissionForm" 
action="AgencySubmission?CreateDocument" method="post" 
enctype="multipart/form-data">

It all worked perfectly. I was able to attach the files and submit the form, and the files showed up in the Notes client. What I did not like was the dreaded “Form processed” message. I tried a few different things, using the $$Return field, etc. But nothing worked.

To make a long story short(er), and without diving too deep into details, I had the form setup to render as HTML, not as a Notes form, thus using ?ReadForm to display it. But when I changed it to Notes on the form properties, the Domino server added it’s own Javascript code to submit the form (in addition to extra HTML). I found out a way to trick Domino to “hide” that Javascript code, so only my jQuery/Javascript code was sent to the browser.

Then I wrote my own code to do a HTTP POST submission of the form as multipart/form-data:

$('form#SubmissionForm').submit(function(event){
  // Disable the default form submission
  event.preventDefault();
  // Gat all form data  
  var formData = new FormData($(this)[0]);
  $('input').each( function() {
    formData.append($(this).attr('id'),$(this).val());
  });
  // Submit form to Domino server using specified form
  $.ajax({
    url: 'AgencySubmission?CreateDocument',
    type: 'POST',
    data: formData,
    async: false,
    cache: false,
    contentType: false,   // Important!
    processData: false,   // Important!
    success: function (returndata) {
      window.location = 'SubmissionThankYou.html';
    }
  });
  return false;
});

That’s it! It worked like a charm. And this is what the final result looks like:

FileUploadControl_Bootstrap

Of course, if you are able to use XPages, there are nice file upload controls there that you can use.

6 Comments

Free Software – Password Reset for Notes/Domino

Earlier this year I was asked to research some alternatives for a web-based password reset function at my work. One of the larger support burdens are users who forget the passwords, especially in the first few days after changing it. We have a 90 day password lifespan, then a new password need to be picked. Some users wait until the last minute, which usually is Friday afternoon right before they go home, making it very likely that they will forget the new password over the weekend. Another big group is auditors, who may come in every 6 months or so, and by then their passwords have of course already expired.

I first looked at some COTS solutions from HADSL (FirM) and BCC (AdminSuite). They were both very competent, and in addition have several other functions that I really would like to have in my environment (like synchronization between Domino Directory and Active Directory). However, as my company is in a cost saving phase, I was asked if I could build something myself, so I played around a little, and came up with a small and simple application.

The application contains two web pages. The first page (Setup) is where the user will setup the security questions used for password recovery as well as entering an external email address that they have access to even if locked out from the Domino account at work. This page is protected by regular Notes security, so the users need to set this up before they lose access to their account.

The second page (Request)is where the user can request the password to be reset. After entering their Notes name, the user is presented with one of the security questions. If the question as answered correctly, the user can now enter a new password. If the question is wrong, another of the questions is presented to the user. I am also using regexp to make sure that the password match the requirement our organisation have for password strength.

Both pages are built using Bootstrap (v3.2.0),  jQuery (v1.11.0), and for the icons I use Font Awesome (v4.2.0), all loaded from BootstrapCDN. I also use a few local CSS and Javascript files to handle older versions of Internet Explorer. The process steps were created using code by jamro and you can find the code here. By the way, Bootsnipp is a great resource to avoid having to invent the wheel again. There are hundreds of free snippets of code there to build neat Bootstrap functionality.

When the user fill out and submit the setup page, a document is created in a Notes database. When the user need to reset the password, the security questions and answers are retrieved from that document. To prevent unauthorised access to the Notes documents, they use Readers fields to prevent them from being visible to anyone but the signer of the agents running on the server.

This application can of course be updated with more functionality. Instead of allowing the user to pick a password, one could be generated by the server and sent through email to the address entered during setup. There are probably other things that can be done to adapt this application to the needs of your organization. And you probably want to change the logo on the pages to fit your organisation.

You can download the application here. It is licensed under Apache 2.0. I will try to get it up on OpenNTF.org soon as well.

Read the “About” page for instructions on installation and setup, as well as full license and attribution. Enjoy!

7 Comments

jQuery – A flexible way to show/hide sections

Yesterday Stephen Gainer blogged about a small Javascript problem he had.

Brilliant!  I gave my customer exactly what he wanted!  No muss no fuss!  I’m sure you see where I’m going with this.  As soon as this was done, my customer came back to me and said he needed four more of these.

My solution, which is terrible, was to duplicate the above four more times (me2Show, me2Hide, me3Show, me3Hide and on and on and …..)  Now I realize how stupid this is, but remember how I said above that there are certain simple things that I never really learned because I never had to?  Well this is one, and this is where I would like YOUR help!

I know there has to be some way to loop through all of my element ID’s with a simple piece of JavaScript, but I can’t for the life of me figure out how to do that.  Can anyone help me out here?

I commented on Stephen’s post and suggested that he use jQuery to easily loop though all elements with a specific class and add a listener function to them to detect a click. Since it is hard to get all information into a comment, I decided to post a simple code sample here instead. My code is easy to expand on, e.g by adding more sections.

There are of course many different ways to do this. You can of course use .toggle(), but I prefer to have better control of when to hide and show the sections. You can break out the lines $(“.mySection”).hide(); into a separate function and call it from the two locations. This is of course not saving anything in this particular code sample, but in more complex code it would make sense to break down the code into separate functions if they are called from multiple lines.

Hopefully this code will help someone, or inspire someone to start playing with jQuery. I like jQuery, as it easily integrates with classic Domino web applications, and even can be used with Xpages.

<html>
<head>
    <title>jQuery hide/show</title>
	<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script> 
	<script>
		$(document).ready(function () {
		// Hide all sections when the page is first loaded
		$(".mySection").hide();
		// Setup all elements with class "myButton" to react on click
		$(".myButton").click( function() {
			// Check if the section is already displayed
			if ($(this).html()=="Hide") {
				// Hide the current section
				var sectionID = $(this).attr("data-showsection");
				$("#"+sectionID).hide();
				// Set the button label to "Show"
				$(this).html("Show");
			} else {
				// Hide all sections, using the class mySection
				$(".mySection").hide();
				// Set all button labels to "Show"
				$(".myButton").html("Show");
				// Show the section we want to display
				var sectionID = $(this).attr("data-showsection");
				$("#"+sectionID).show();
				// Set the button label to "hide"
				$(this).html("Show");
			}
		});
	});	
	</script>
</head>
<body>
    <button id="btnOne" class="myButton" data-showsection="sectionOne">Show</button>
    <div id="sectionOne" class="mySection" data-btnID="btnOne">This is the 1st section.</div>
    <br>
    <button id="btnTwo" class="myButton" data-showsection="sectionTwo">Show</button>
    <div id="sectionTwo" class="mySection" data-btnID="btnTwo">You are now seeing the 2nd section.</div>
    <br>
    <button id="btnThree" class="myButton" data-showsection="sectionThree">Show</button>
    <div id="sectionThree" class="mySection" data-btnID="btnThree">This is the 3rd section.</div>
    <br>
    <button id="btnFour" class="myButton" data-showsection="sectionFour">Show</button>
    <div id="sectionFour" class="mySection" data-btnID="btnFour">The 4th and last section.</div>
    <br>
</body>
0 Comments

Code snippet – jQuery

This morning I was working on a web application, and I came up with a pretty neat and simple little solution. So I just wanted to share it, in case anyone else need something similar.

I have a webpage with an HTML form. Each input tag has an attribute called notesfield, matching the name of the field in Notes where the value is stored:

<div class="col-md-3">
    <label>First Name</label>
    <input class="form-control" type="text" notesfield="FirstName" value="" />
</div>
<div class="col-md-2">
    <label>Initial</label>
    <input class="form-control" type="text" notesfield="MiddleInitial" value="" />
</div>
<div class="col-md-3">
    <label>Last Name</label>
    <input class="form-control" type="text" notesfield="LastName" value="" />
</div>

Then I created a simple function that will call an agent on the Domino server, which will return all the fields on the specified document as JSON. This function is called after the HTML page is fully loaded.

function loadNotesFields(docunid) {
	var notesfieldname = "";
	$.ajax({
		url: "/database.nsf/ajax_GetNotesFieldFields?OpenAgent", 
		data: {"NotesUNID":docunid},
		cache: false
	}).done(function(data) {
		$('input[notesfield]').each(function() {
			notesfieldname = $(this).attr("notesfield");
			$(this).val(data[notesfieldname]);
		});
	});
}

The function is actually extremely simple, and here you can see the power of jQuery. What I do is to perform an Ajax call to a Domino URL, passing a UNID to the agent to use in the lookup. I set cache to false, to avoid the browser from reusing previously retrieved data (this is a good thing to do if the data retrieved can be suspected to change frequently).

The jQuery .ajax() functions returns the JSON in the data object, and when the call is done, the callback function loops through each input element with an attribute of notesfield, reads the value of said attribute and then sets the value of the input element to the corresponding Notes value.

The only thing left is to write the agent that will return the JSON. It could look something like this:

Dim urldata List As String

Sub Initialize
	Dim session As New NotesSession
	Dim webform As NotesDocument
	Dim db As NotesDatabase
	Dim doc As NotesDocument
	Dim urlstring As String
	Dim urlarr As Variant
	Dim urlvaluename As Variant
	Dim i As Integer
	Dim json As String

	Set webform = session.DocumentContext
	'*** Remove leading "OpenAgent" from Query_String
	urlstring = StrRight(webform.Query_String_Decoded(0),"&")
	'*** Create list of arguments passed to agent
	urlarr = Split(urlstring,"&")
	For i = LBound(urlarr) To UBound(urlarr)
		urlvaluename = Split(urlarr(i),"=")
		urldata(urlvaluename(0)) = urlvaluename(1)
	Next
	Set thisdb = session.CurrentDatabase
	'*** Create content header for return data
	Print "content-type: application/json"
	'*** Get Notes document baed on NotesUIND argument
	Set doc = db.GetDocumentByUNID(urldata("NotesUNID"))
	'*** Build JSON for all fields in document except $fields
	json = "{" + Chr$(13)
	ForAll item In doc.Items
		If Left$(item.Name,1)<>"$" Then
			json = json + |"| + item.Name + |":"| + item.Text + |",|+ Chr$(13)
		End If
	End ForAll
	'*** Remove trailing comma and line break
	json = Left$(json,Len(json)-2)	
	json = json + "}"
	'*** Return JSON
	Print json	
End Sub

Happy coding!

6 Comments

Excellent Bootstrap select plugin with great support

For a Domino-based web application I am currently working on, I needed a nicer looking select box (drop down) than what Bootstrap offers out of the box. I did some searches and found a handful of free ones, most of them pretty good but not exactly what I wanted. Some did not handle different themes, other had additional functionality I did not want/need, etc. I probably been looking for a good alternative for 3 weeks by now.

Then the other day I found an inexpensive plugin at CodeCanyon. Custom Select for Twitter Bootstrap 3 is just $5 if you use it for a public site, or $25 if you use it on a site where you charge the users for access. It’s well worth it. The control is nice and clean, and very easy to use.

Custom Select for Twitter Bootstrap 3
Custom Select for Twitter Bootstrap 3

What really impressed me was how the author of the plugin, Lisa Stoz, fixed an issue I ran into. It was not a bug in the plugin, but I had a need to use custom tag attributes, and the plugin did not support that originally. The next day Lisa had a new version available with that functionality added. She also helped me with some additional questions that I had. I am very impressed with the quick response and the professional support.

I am new to CodeCanyon, but that site seems very nice. It contains a large number of jQuery and javascript plugins, Bootstrap themes and plugins, and much more. There is also a section with WordPress plugins, as well as a separate site for WordPress themes called ThemeForrest. That site also have other themes and templates. If you build websites, but like me is not a graphics genius, ThemeForrest is a great place to find themes or just inspiration for your site. The themes (as are the plugins) are very modestly priced, between $5 and $10 in most cases.

I have just started to scratch the surface of what’s available there, but I already think this is a great resource. The next time I need a plugin for Bootstrap, jQuery or WordPress, I will probably start at CodeCanyon.

Disclaimer: The links above uses an affiliate code, giving me a small credit at the site if you purchase anything.

1 Comment

End of content

No more pages to load