Se ha denunciado esta presentación.
Utilizamos tu perfil de LinkedIn y tus datos de actividad para personalizar los anuncios y mostrarte publicidad más relevante. Puedes cambiar tus preferencias de publicidad en cualquier momento.

Break out of The Box - Part 2

449 visualizaciones

Publicado el

My presentation at MWLUG 2016 in Austin, TX.

Publicado en: Internet
  • Sé el primero en comentar

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

×