Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Break out of The Box - Part 2

387 views

Published on

My presentation at MWLUG 2016 in Austin, TX.

Published in: Internet
  • Be the first to comment

Break out of The Box - Part 2

  1. 1. Break out of The Box – Part 2 Integrate existing Domino Data with modern websites Karl-Henry Martinsson Demand Better Solutions, LLC
  2. 2. I AM...  Swede living in Dallas, TX  Developer at Demand Better Solutions, LLC  Programming since 1982  Web developer since 1994  Notes/Domino developer since 1996  IBM Champion since 2014 http://www.demandbettersolutions.com http://www.texasswede.com http://blog.texasswede.com
  3. 3. My Story  Old infrastructure • Domino 8.5.2 server and Notes 8.5.2 Basic client  Company unwilling to upgrade • Memory requirements for Standard client • Citrix environment – limited memory available • Upper management thinks Notes/Domino is legacy  Management requests • New web applications with modern look-and-feel • Future mobile support (phone/tablet)
  4. 4. Bootstrap and jQuery to the Rescue!
  5. 5. Why Not Xpages?  Infrastructure not supporting XPages (Domino 8.5)  No XPages development skills  Tight deadline – no time to learn  Better control of versions – not having to wait for IBM  Access to frameworks not yet in Domino (e.g. jQuery)  Use time-saving jQuery/Bootstrap plugins
  6. 6. Why Integrate?  Limited or no migration options  Desire to modernize front-end  Code re-use  Take advantage of the power of Domino  Use existing skillset (Domino, Lotusscript)  Learn valuable new skills (Javascript/jQuery/Ajax)  No one will know you are using a Domino backend!
  7. 7. How does it work? .NSF  HTML and CSS  jQuery (Javascript)  Choice of framework • Bootstrap • jQuery UI / jQuery Mobile • and many other...  Process Ajax from browser  Generate and return JSON • Lotusscript agents • XPages agents • ?ReadViewEntries • ExtLib REST Service control Ajax call JSON data
  8. 8. Server – IBM Domino  Lotusscript agents  NSF databases  Existing business logic  Can use existing classes and script libraries Works on Domino version 5.0 and higher Update to recent version highly recommended
  9. 9. Where does everything live?  HTML pages, CSS files and Javascript • Notes page element • Notes resources • CDN (.js and .css) • Server file system – in Data/Domino/HTML • Another web server  Lotusscript agents • .NSF database on Domino server
  10. 10. Development Tools  Domino Designer  Browser with Dev Tools • Firefox with Firebug plugin • Internet Explorer Developer Tools (built-in) • Chrome Developer Tools (built-in)  Online resources • JSONLint • Stack Overflow • Google Search
  11. 11. What is jQuery?  Fast, small and feature rich Javascript library  Simplify using Javascript on a web page  Available as CDN and as downloaded local files  Three code streams: • jQuery 1.x  Supporting all browsers • jQuery 2.x  Removed support for IE 8 and earlier  Smaller and faster • jQuery 3.x  Continuation of jQuery 2.x  Some breaking functionality – e.g. in Ajax calls  Only version to get new functionality  Free to use (MIT license)
  12. 12. What is Bootstrap?  Developed by Twitter  Open source front-end framework  Mainly CSS, with some jQuery  Cross-browser  Responsive (out-of-the-box in version 3.0+)  Supports themes, color schemes and plugins  Available through CDN or as local files  Many 3rd party resources and plugins  Current version 3.3.7, version 4 is in early testing
  13. 13. What does Bootstrap look like?  Attractive look out of the box  Themes used to modify colors and controls  Override with your own CSS for customization  Many free and premium add-ons available Bootstrap 2 using the theme “United”, with additional CSS for page curls. Bootstrap 3 using the default theme.
  14. 14. How do you use jQuery and Bootstrap?  Link to .js and .css files in HTML header • Download files locally • Use CDN for easy linking – my preferred approach  Start writing your code! Tips:  Use // without protocol to use HTTP or HTTPS as needed.  ViewPort meta tag helps with scaling of page.  Never edit the Bootstrap CSS file (even if local), always override the CSS. <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Demo 1 - MWLUG 2016</title> <script src="//ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script> <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> <link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> <link href="demo1.css" rel="stylesheet">
  15. 15. Ajax – Asynchronous Javascript and XML  Asynchronous – call is processed in background  Result is passed back when done, then processed  Data can be returned in different formats: • XML – original format, rarely used anymore • JSON –now more common, easy to parse in Javascript • JSONP – JSON with Padding, when calling other servers • Can also return plain text or HTML  Using only a few lines of jQuery code  Supported as data source directly in many plugins
  16. 16. Ajax call using jQuery  Arguments passed as JSON  cache: false – “cache buster” $.ajax({ url: '/websites/example.nsf/ajax_GetUserDetails?OpenAgent', data: {name: userName}, cache: false }).done(function(data) { // Process returned data here }).fail(function(e) { // Process failed call here }).always(function() { // This code will always execute }); or $.ajax({ url: '/websites/example.nsf/ajax_GetUserDetails?OpenAgent', data: {name: userName}, cache: false }).success(function(data) { // Process returned data here });
  17. 17. JSON – JavaScript Object Notation  Describe data as Javascript objects  Preferred to XML in web applications • Less “chatty” (less bandwidth) • Very easy to access values directly in Javascript  Supports all Javascript data types  Can contain arrays of objects  Can be very complex if needed  Use JSONLint to validate!
  18. 18. JSON Examples
  19. 19. Generate JSON on Domino  ?ReadViewEntries&OutputFormat=JSON • Available in Domino 7.0.2+ • Can be hard to parse  Formula in Notes view • http://www.eknori.de/2011-07-23/formula-magic/  Lotusscript agent • Generate your own JSON • Test at JSONLint.com • Use JSON classes  Class.JSON (available in demo database)  JSON Lotusscript Classes by Troy Reimer (on OpenNTF.org)
  20. 20. Generate JSON on Domino – Xpages Style  XPages agent (SSJS XAgent) • Domino 8.5.2 and XPages knowledge • Better performance than Lotusscript • http://www.wissel.net/blog/d6plinks/shwl-7mgfbn  REST Services control from Extension Library • Domino 8.5.2 and ExtLib on server
  21. 21. Demo 1 Table using bootstrap-table plugin
  22. 22. Bootstrap-table plugin  Plugin by @wenzhixin  Get it at http://bootstrap-table.wenzhixin.net.cn  CDN hosted version at CloudFlare.com  Minimal HTML markup  Javascript mainly to define columns and settings
  23. 23. Bootstrap-table – Add to the webpage HTML <div id="tableToolbar"> <div class="toolbarText">My Contacts</div> </div> <table id="ContactTable"></table> jQuery (partial) $("#ContactTable").bootstrapTable({ url: 'ajax_Demo7_GetAllContacts?OpenAgent', search: true, showRefresh: true, pagination: true, pageSize: 25, classes: "table-condensed table-hover table-striped tableContent", toolbar: "#tableToolbar", columns: [{ field: 'FirstName', title: 'First Name', width: 80, sortable: true }, { field: 'LastName', title: 'Last Name', width: 90, sortable: true }, { … … …
  24. 24. Bootstrap-table – Generate JSON data Lotusscript code (partial) '*** Get all documents in view to process Set db = session.CurrentDatabase Set view = db.GetView("(LookupContactsByLastName)") Set col = view.AllEntries '*** Start of JSON string jsonString = “” '*** Loop through all entries and build JSON to return Set entry = col.GetFirstEntry Do Until entry Is Nothing '*** Build JSON for each entry and add to string Set json = New JSONData() Call json.SetValue("LastName", CStr(entry.ColumnValues(0))) Call json.SetValue("FirstName", CStr(entry.ColumnValues(1))) Call json.SetValue("Company", CStr(entry.ColumnValues(2))) Call json.SetValue("Address", CStr(entry.ColumnValues(3))) Call json.SetValue("City", CStr(entry.ColumnValues(4))) Call json.SetValue("State", CStr(entry.ColumnValues(5))) Call json.SetValue("ZIP", CStr(entry.ColumnValues(6))) Call json.SetValue("DocUNID", CStr(entry.ColumnValues(9))) '*** Add new JSON to existing JSON string jsonString = jsonString + json.GetJSON() + "," + Chr$(13) Set entry = col.GetNextEntry(entry) Loop '*** Remove the trailing comma and line break if we have data If Len(jsonString) > 0 then jsonString = Left$(jsonString,Len(jsonString)-2) End If '*** Add brackets for array jsonString = "[ " + Chr$(13) + jsonString + Chr$(13) + “ ]“ '*** MIME Header to tell browser what kind of data we will send Print "content-type: application/json" '*** Send JSON back to browser Print jsonString
  25. 25. Bootstrap-table – Examine returned JSON
  26. 26. Bootstrap-table – The Result
  27. 27. Demo 2 Contact database using Bootstrap
  28. 28. Contact database - Overview  Display contacts – use Demo 1 as base  Click on user in table to display details  Buttons • Edit • Save • New • Delete  Add refresh/reload of contacts when updated
  29. 29. Contact database – Add elements  Lotusscript agents • ajax_GetAllContacts • ajax_GetContactDetails • ajax_SaveContact  If DocUNID is blank, create new contact  Otherwise update existing contact • ajax_DeleteContact  HTML page changes • Add section for contact details • Detect click on row to display details • Add buttons and jQuery code to call Lotusscript agents
  30. 30. HTML – buttons <button class="btn btn-sm btn-primary" id="btnNewContact">New</button> <button class="btn btn-sm btn-primary" id="btnEditContact">Edit</button> <button class="btn btn-sm btn-success" id="btnSaveContact">Save</button> <button class="btn btn-sm btn-danger pull-right" id="btnDeleteContact">Delete</button> jQuery – Edit and New buttons //*** Button actions $("#btnEditContact").on("click", function(e) { setEditMode(); }); $("#btnNewContact").on("click", function() { setEditMode(); // Empty all input fields $('input[data-notesfield]').each( function() { $(this).val(""); }); // Empty hidden DocUNID field $("#docUNID").attr("data-UNID",""); // Hide ‘Delete’ button $("#btnDeleteContact").hide(); }); Contact database – New buttons
  31. 31. Contact database – New buttons jQuery – Save button $("#btnSaveContact").on("click", function() { $("#btnSaveContact").hide(); var json = new Object(); // Store field values in JSON object var docunid = $("#docUNID").attr("data-UNID"); json["DocUNID"] = docunid; $('input[data-notesfield]').each( function() { var id = $(this).attr("id"); var notesfield = $(this).attr("data-notesfield"); json[notesfield] = $(this).val(); }); // Perform a call to the server to save values $.ajax({ url: "ajax_Demo8_SaveContact?OpenAgent", type: "POST", data: json }).done(function(data) { if (data.status=="error") { alert("Failure: " + data.msg); } else if (data.status=="success") { setReadMode(); // Convert INPUT back to DIV $("#contactTable").bootstrapTable("refresh", {silent: true}); }).fail( function(e) { alert("Failure!","Failed to save contact. Error: " + e.errorThrown); }); $("#btnEditContact").show(); });
  32. 32. Contact database – New buttons jQuery – Delete button $("#btnDeleteContact").on("click", function(e) { var docunid = $("#docUNID").attr("data-UNID"); $.ajax({ url: "ajax_Demo8_DeleteContact?OpenAgent", type: "POST", data: {DocUNID: docunid } }).done(function(data) { if (data.status=="error") { alert("Failure: " + data.msg); } else if (data.status=="success") { $("#contactTable").bootstrapTable("refresh", {silent: true}); // Empty all input fields $('input[data-notesfield]').each( function() { $(this).val(""); }); // Empty all div with Notes data $('div[data-notesfield]').each( function() { $(this).html(""); }); // Empty hidden DocUNID storage $("#docUNID").attr("data-UNID","") $("#btnDeleteContact").hide(); $("#btnEditContact").hide(); } }).fail( function(e) { alert("Failure!","Failed to delete contact. Error: " + e.errorThrown); }); });
  33. 33. Contact database – Detect click on row jQuery – Detect click on table row // Detect click on row in table $("#contactTable").on('click-row.bs.table', function (e, row, $element) { // Convert INPUT fields back to DIV just in case setReadMode(); // Hide save button if visible $("#btnSaveContact").hide(); // Get DocUNID value in table and load corresponding values from server var unid = row.DocUNID; displayDetails(unid); });
  34. 34. Contact database – Load details jQuery – Load contact details from server and display on page // Get contact details from Domino server and populate fields // using the DocUIND value as lookup key function displayDetails(docunid) { $.ajax({ url: 'ajax_Demo8_GetContactDetails?OpenAgent', data: {DocUNID: docunid}, cache: false }).success(function(data) { if(data.status=="success") { // For each element with data-notesfield attribute $('div[data-notesfield]').each( function() { notesfield = $(this).attr("data-notesfield"); if (data[notesfield]!=null) { fieldvalue = data[notesfield]; $(this).html(fieldvalue); } }); // Store DocUNID in enmpty div for later use $("#docUNID").attr("data-UNID",data.DocUNID); // Display previously hidden editand delete buttons $("#btnEditContact").show(); $("#btnDeleteContact").show(); } }); }
  35. 35. Contact database – Fancy stuff jQuery – Switch between DIV (plan text) and INPUT (editable) // Put contact details into edit mode by changing DIV to INPUT function setEditMode() { $("#btnEditContact").hide(); // Change all div with Notes data to input $('div[data-notesfield]').each( function() { var id = $(this).attr("id"); var notesfield = $(this).attr("data-notesfield"); var input = "<input class='jsonData inputNotesField form-control input-sm' id='" + id input = input + "' data-notesfield='" + notesfield + "' value='" + $(this).html() + "'></input>"; $(this).replaceWith(input) }); $("#btnSaveContact").show(); $("#btnEditContact").hide(); } // Put contact details into read mode by changing INPUT to DIV function setReadMode() { $('input[data-notesfield]').each( function() { var id = $(this).attr("id"); var notesfield = $(this).attr("data-notesfield"); var div = "<div class='jsonData displayNotesField' id='" + id div = div + "' data-notesfield='" + notesfield + "'>" + $(this).val() + "</div>"; $(this).replaceWith(div) }); }
  36. 36. Contact database
  37. 37. Demo 3 Contact database using jQuery Mobile
  38. 38. jQuery UI and jQuery Mobile  UI Frameworks  jQuery UI – alternative to Bootstrap • Set of components  Widgets  Effects  Interactions • Built on top of jQuery  jQuery Mobile – emulates native mobile app look • Header/footer • Grid layout • Buttons • Listview widget • Forms • …and much more!
  39. 39. Contact database – jQuery Mobile  Reuse agents from previous demo  New HTML code • Multi page application with two pages  Contact list using Listview widget  Contact details using form elements  New Javascript/jQuery • Some code can be reused!  Notification overlay when saved • Using notifIt! plugin • Can use other plugins, many available!
  40. 40. Contact database – Android, iOS, WinMobile  Create native apps using PhoneGap  One source, multiple targets  Create folder structure on disk • .html, .css and .js • config.xml in root folder  Create ZIP file  Upload to PhoneGap Build  Download finished apps • Android • iOS • Windows Mobile
  41. 41. Demo 4 Calendar using FullCalendar plugin
  42. 42. FullCalendar plugin  Get it at http://fullcalendar.io  Lotusscript agents • ajax_Calendar_GetAllEvents  Returning events between specific days  Calendar automatically sends start and end date • ajax_Calendar_GetEventDetails • ajax_Calendar_UpdateEvent  Triggered when moving event or changing duration  Arguments: DocUNID, start date and end date
  43. 43. FullCalendar plugin  Calendar points to JSON of event data  Automatically sends start and end dates  Agent returns only events within date range jQuery – Display calendar and load JSON of event data from server var eventSource = 'ajax_Calendar_GetAllEvents?OpenAgent'; $("#notesCalendar").fullCalendar({ events: eventSource });
  44. 44. FullCalendar plugin Lotusscript agent ajax_Calendar_GetAllEvents '*** Local variables to hold arguments passed from URL Dim startdate As String Dim enddate As String '*** Other local variables Dim jsontext As String '*** Create new URLData object Set url = New URLData() '*** Create new JSONData object Set json = New JSONData() '*** Check start date and convert from ISO to US date format If url.IsValue("start") Then startdate = ISOtoUS(url.GetValue("start")) Else startdate = "01/01/1980" End If '*** Check end date and convert to US date format If url.IsValue("end") Then enddate = ISOtoUS(url.GetValue("end")) Else enddate = "12/31/2199" End If
  45. 45. FullCalendar plugin Lotusscript agent ajax_Calendar_GetAllEvents (continued) '*** Send MIME header to browser Print "content-type: application/json" jsontext = "" Set db = session.CurrentDatabase Set view = db.GetView("Events") Set col = view.AllEntries Set entry = col.GetFirstEntry() Do Until entry Is Nothing If CDat(entry.ColumnValues(0))>=CDat(startdate) Then If CDat(entry.ColumnValues(0))<=CDat(enddate) Then Call json.SetValue("id", CStr(entry.ColumnValues(5))) Call json.SetValue("title",CStr(entry.ColumnValues(3))) Call json.SetValue("start", Format$(CDat(entry.ColumnValues(0)),"mm/dd/yyyy hh:nn ampm")) Call json.SetValue("end", Format$(entry.ColumnValues(1),"mm/dd/yyyy hh:nn ampm")) '*** Make the entry editable in calendar (allow changing date/time) Call json.SetBoolean("editable", True) End If End If jsontext = jsontext + json.GetJSON() + "," + Chr$(13) Set entry = col.GetNextEntry(entry) Loop If Len(jsontext)>4 Then jsontext = Left$(jsontext,Len(jsontext)-2) End If Print "[ " + jsontext + " ]"
  46. 46. FullCalendar plugin FullCalendar plugin – Events triggered on click, resize and drop/move … eventClick: function(calEvent, jsEvent, view) { var unid = calEvent.id; displayEventDetails(unid); }, eventResize: function(event, delta, revertFunc) { if (!confirm(event.title + " will now end at " + event.end.format("h:mm a") + "nAre you sure?")) { revertFunc(); } else { var unid = event.id; updateEvent(unid,event.start.format("MM/DD/YYYY hh:mm a"),event.end.format("MM/DD/YYYY hh:mm a")); displayEventDetails(unid) } }, eventDrop: function(event, delta, revertFunc) { var prompt = event.title + "<br>was moved to " + event.start.format("MM/DD/YYYY") prompt = prompt + " at " + event.start.format("h:mm a"); bootbox.confirm(prompt + "<br>Are you sure you want to do that?", function(result) { if(result==true) { var unid = event.id; updateEvent(unid,event.start.format("MM/DD/YYYY hh:mm a"),event.end.format("MM/DD/YYYY hh:mm a")); displayEventDetails(unid) } else { revertFunc(); } }); } …
  47. 47. FullCalendar plugin Javascript code – Send event update to server function updateEvent(docunid,startDT,endDT) { var json = new Object(); json["DocUNID"] = docunid; json["EventStart"] = startDT; json["EventEnd"] = endDT; // Perform a call to the server to save new event date/time $.ajax({ url: "ajax_Calendar_UpdateEvent?OpenAgent", type: "POST", data: json }).done(function(data) { if (data.status=="error") { bootstrapAlert(data.msg,"danger"); } else if (data.status=="success") { bootstrapAlert(data.msg,"success"); } }).fail( function(e) { bootstrapAlert("Failed to create progress note. Error: " + e.errorThrown,"danger"); }); }
  48. 48. FullCalendar plugin Lotusscript agent – ajax_Calendar_UpdateEvent '--- Local variables Dim startDate As String Dim endDate As String '*** Get document Set db = session.CurrentDatabase If url.GetValue("DocUNID")<>"" Then Set doc = db.GetDocumentByUNID(url.GetValue("DocUNID")) End If '*** Check that we found a document, otherwise exit If doc Is Nothing Then Set json = New JSONData() json.success = False json.SetErrorMsg("Failed to locate document '" & url.GetValue("DocUNID")) Call json.SendToBrowser() Exit Sub End If Call doc.ReplaceItemValue("EventStart",CDat(url.GetValue("EventStart"))) Call doc.ReplaceItemValue("EventEnd",CDat(url.GetValue("EventEnd"))) Call doc.Save(True,False) Set json = New JSONData() json.success = True json.SetMsg("Updated '" & doc.GetItemValue("EventTitle")(0) & "' with new date/time") Call json.SendToBrowser()
  49. 49. FullCalendar – End Result
  50. 50. Summary
  51. 51. What have we seen?  Ajax/JSON – efficient way to access Domino data  CRUD using server based agents (Lotusscript)  jQuery and Bootstrap to speed up development Why learn this?  Quickly create attractive and modern web applications  Still access existing Domino data with ease  Acquire some new and easy-to-learn skills  Those skills are beneficial on other platforms as well  Many plugins available for free – less work for you!  Your customers and/or boss will love you!
  52. 52. Questions?
  53. 53. Thank you! Karl-Henry Martinsson Email: texasswede@gmail.com Twitter, Skype: @texasswede http://www.linkedin.com/in/texasswede http://blog.texasswede.com

×