21 January 2021 Thursday

A little side-work: QRCodes in XPages


Hi there,

it has been a while since I had to create my last xpages project. A customer asked me yesterday after seeing our QRCode integration in our IMPRISIS Web Desktop, if it would be possible to create QRCodes in Xpages as well - and of course it is.

As this might be usefull for others as well, here's what we did:

First of all, we used the NPM Package 'QRCode' to create the QRCodes using JavaScript. The repo can be found here:


https://github.com/soldair/node-qrcode

We then created an XPage, that allows us to type some text into an input field, click a button and receive the result as QRCode. Here's the demo:


Image:A little side-work: QRCodes in XPages

To build this,

we followed the description in the repo above on how to use the pre-compiled modules for qrcode.js from the node-module directory after installing the library locally using yarn or npm.

There's a folder called 'build' in the qrcode subdirectory in node-modules:

Image:A little side-work: QRCodes in XPages

That's what we imported into our Notes App using Designer in the package explorer like so:


Image:A little side-work: QRCodes in XPages

Then, we created an XPage:


xml version="1.0" encoding="UTF-8"?>
<
xp:view
       
xmlns:xp="http://www.ibm.com/xsp/core">
       
<xp:this.resources>
               
<xp:headTag tagName="script">
                       
<xp:this.attributes>
                               
<xp:parameter
                                       
name="type"
                                       
value="text/javascript" />
                               
<xp:parameter
                                       
name="src"
                                       
value="qrcode/qrcode.js" />
                       
xp:this.attributes>
               
xp:headTag>
               
               
       
xp:this.resources>
       
<xp:div styleClass="container">
               
<xp:div styleClass="row">
                       
<canvas id="canvas">canvas>
                       
<xp:inputText id="inputText1"
                               
value="#{sessionScope.MyQrCode}"
                               
defaultValue="https://www.sit.de">

                       
xp:inputText>
                       
<xp:button value="Label" id="button1">
                               
<xp:eventHandler event="onclick" submit="true"
                                       
refreshMode="complete">
                               
xp:eventHandler>xp:button>
                       
<xp:scriptBlock id="scriptBlock1" type="text/javascript">
                               
<xp:this.value>var sampleText = document.getElementById("#{id:inputText1}").value;
QRCode.toCanvas(document.getElementById('canvas'), sampleText, function (error) {

   if (error) console.error(error, sampleText)

   console.log('success!', sampleText);

 })
]]>xp:this.value>
                       
xp:scriptBlock>
               
xp:div>
               
xp:div>
               

xp:view>

The Xpage imports the JavaScript Library before importing dojo. Then, we created a element to draw our QRCode. We added a input field and a button and bound the input field to a session scope variable. Finally, we added a script block that reads the value of the input field using CSJS and passes it on to the qrcode function to create the image. Et voila - QRcodes in XPages.


Hope this helps if someone needs it - happy coding !


Heiko.
Heiko Voigt   |   21 January 2021 13:14:34   |    Domino  XPages    |   Comments [0]

Hello !

Coming out of an interesting session on the OpenNTF slack channel today, I thought it would be a good idea to blog about our findings, before someone else false into the same trap(s).


What's the topic ?


DateTime-Values and their handling by domino-db/proton.


Why should you care ?


Well, if you're a LotusScript or Java Developer with Domino - not so much. If you're using the AppDevPack - well, maybe you should.


What's up ?


Timezones
in one word. Imagine you have this code fragment to save a DateTime value to a Notes Document using domino-db:

(I'm using moment.js in my node code)

Datetime: {
type: 'datetime',
data:  moment(momentTZ().tz("Europe/Berlin")).format('YYYY-MM-DDTHH:mm:ss.SS');
},




You will run into two issues here. One is fairly obvious - domino-db will let you know that the you have a BAD_TIME value here:

Error:
at new DominoDbError (~\app\node_modules\@domino\domino-db\src\domino-db-error.js:6:16)
at wrapError (~\app\node_modules\@domino\domino-db\src\requests\grpc\utils\grpc-helpers.js:157:10)
message: 'Bad time value',
code: 'ERR_BAD_REQUEST',
cause: Error [ProtonError]: Bad time value
 at makeProtonError (~\app\node_modules\@domino\domino-db\src\requests\grpc\utils\grpc-helpers.js:74:17)
 at ~\app\node_modules\@domino\domino-db\src\requests\grpc\utils\convert-from-proto.js:305:35
 at Array.forEach ()
 at commonResponseFromProto (~\app\node_modules\@domino\domino-db\src\requests\grpc\utils\convert-from-proto.js:298:15)
 at createResponseFromProto (~\app\node_modules\@domino\domino-db\src\requests\grpc\utils\convert-from-proto.js:335:3)
 at createSingleDocumentConverter (~\app\node_modules\@domino\domino-db\src\requests\grpc\utils\convert-from-proto.js:363:18)
 at ~\app\node_modules\@domino\domino-db\src\requests\grpc\utils\bulk-document.js:245:21
 at Object.onReceiveStatus (~\app\node_modules\grpc\src\client_interceptors.js:1212:9)
 at InterceptingListener._callNext (~\app\node_modules\grpc\src\client_interceptors.js:568:42)
 at InterceptingListener.onReceiveStatus (~\app\node_modules\grpc\src\client_interceptors.js:618:8)
 at callback (~\app\node_modules\grpc\src\client_interceptors.js:847:24) {
code: 65545,
id: 'BAD_TIME_VALUE'
}
}

Why is that ? Let's check the documentation:


Of course, there may be situations where your application needs to convert between a JavaScript Date and a Domino DATETIME. For example, let's assume you want to write a document item of type DATETIME where the value is equal to myDate (and myDate is an instance of Date). You might be tempted to use toISOString() like this:

  Datetime: {
type: 'datetime',
data: myDate.toISOString()
},






Unfortunately,
toISOString() writes a value with millisecond precision. Therefore, when you use Database.createDocument() to write a document containing the above item, the request will fail. You must format myDate to be no more precise than a hundredth of a second. In other words:

  '2013-03-01T14:09:01.009Z' // Invalid (too precise)
'2013-03-01T14:09:01.01Z' // Valid






Usually, this means you will want to use an existing Node.js package to format and parse date values for use by domino-db. For example,
moment is one good package for handling dates, but ultimately it is up to you to find the package that meets your needs.
HINT: If you are using moment, use the following format string for parsing and writing date values:
'YYYY-MM-DDTHH:mm:ss.SS[Z]'.




So this means, we have to change our code to this:


Datetime: {
type: 'datetime',
data:  moment(momentTZ().tz("Europe/Berlin")).format('YYYY-MM-DDTHH:mm:ss.SS[Z]');
},


This will write our DateTime Value to Notes without the error above.

So far so good. Now, let's look at our Date/Time value in the written document in Notes.


Remember - we created a DateTime value in Node in GMT+1, so e.g. 12pm GMT+1.


What got saved ?


12pm GMT.


See the next problem ?  


Your Date/Time field in Notes is off by one hour.


Why is that ? After talking to development, this results from the C-APIs the proton task is calling when creating the items. The C-API *ALWAYS* stores Date/Time Values with GMT as the time zone, without converting the incoming value to GMT. And domino-db returns a GMT Timestamp as we re-read the document and the item. If we convert this to GMT+1 now, we get 1pm back, which is wrong !


Currently, we can't send any time zone parameter using domino-db/proton so our Node applications have to take care of this themselves, maybe by storing the time zone in an additional item on save and re-creating the appropriate timestamp from there on re-use.

The Notes Calendar btw. converts these dates automatically from GMT to the local time zone . So that's another can of worms to keep in mind. I will have to check the return values after the Notes Client changed the date fields - we will get back the converted time stamp with no time zone definitions whatsoever. Ugly.


In summary, be aware of these problems if you are dealing with multi time zone apps using domino-db/proton or with time zone aware apps of any kind.


Heiko.
Heiko Voigt   |   4 December 2020 16:28:35   |    AppDevPack    |   Comments [2]

Hi,

another quick example on how we can quickly build apps using the HCL Domino AppDevPack. Here goes:


Customer rings.


Customer: "I need to get some statistics on our Domino Applications - as a timeline slide to see trends, how can we do that ?"


Me: "There are analytics tools from other partner that can get this done"


Customer: "No, I want something that I can extend over time myself..."


Me: "Well, what do you need from a KPI Point of view ?"


Customer: "Usage of the App, Diskspace development. read and write access"


Me: "OK, Domino can give you all of this out of the box."


Customer: "Really ?! Can you guys build me a starting web component that I can integrate into our intranet ?"


Me: "Sure..."


So here goes. Domino actually does create all of this data on a daily basis for each application on a specific server. Here's what we will be using:
Check out your server's log and switch to this view:


Image:HCL Domino Database Analytics DIY with domino-db

There's this document called "Activity". Domino writes this on a daily basis for each application. It gives you the following data:


Image:HCL Domino Database Analytics DIY with domino-db
And....

Image:HCL Domino Database Analytics DIY with domino-db

Now, as I said, this documents gets overwritten on a daily basis. So to get a series of data, I created a new database based on log.ntf and a simple agent that copies the activity documents for all the databases I want to get statistic data for into this new database.


This gives me a daily series of usage data like so (added a view to view the data):


Image:HCL Domino Database Analytics DIY with domino-db

Now having this, I just used our IMPRISIS Basic React Client and created a react component to get the data and display it using ReCharts.
The data is automatically read via domino-db and passed as JSON data to the component. The chart itself is defined as JSON Object in our IMPRISIS Design Repository Db where we can define queries and define renderings for them. So basically, I can bring data and representation together dynamically. We can also do data conversion between the domino-db documents and the data structure used in the chart.


Image:HCL Domino Database Analytics DIY with domino-db

With that, we start the react component, which gives us a chart like this:

Image:HCL Domino Database Analytics DIY with domino-db

The chart contains an area chart for the disk space, bar charts for read and write access numbers and a line chart for usage counts.


The cool thing here is that using our IMPRISIS component framework for React, I was able to build a reusable charting component that is completely agnostic of the chart representation and the data it represents, All of that can be passed dynamically to the component and gets automatically read out by our domino-db based REST-API Middleware,


Total development time: 1 day.

What do you think ?


Heiko.
Heiko Voigt   |   13 November 2020 16:06:47   |    AppDevPack    |   Comments [0]

Image:See you at HCL Digital Week next week !

So, next week will be an important week for all HCL customers and Partners.
HCL is opening its doors (virtually) to show the latest & greatest in their software portfolio including several huge announcments, like HCL Sametime Premium and HCL Volt MX.

Personally, I am looking forward to the "Factory Tour" track around the Digital Solutions portfolio.

One session I am REALLY looking forward to is the Lab Session around EWS Services for Domino (Exchange Web Services) for drect access from Outlook/Apple Mail/Thrid Party PIM Clients to a HCL Domino Server.

You can pick and choose your topics from three program tracks: DS Imagine, DX Inspire and Factory Tour.

Not registered yet ? Check out the Agenda & register here:

https://www.hcltechsw.com/events/digital-week

See you there !

Heiko.
Heiko Voigt   |   6 November 2020 16:56:06   |    HCL    |   Comments [0]

Image:Collaboration at its best - simple Integration between HCL Notes and Mattermost
Hi,


almost a year ago, we added a new collaborative tool to our internal and external communication portfolio. We started our own persistent Chat server using Mattermost as an open source soultion of choice for us.

Since day one, this tool has nothing but grown in acceptance internally and externally, as we use it together with our customers to keep track of project based communications and to avoid email overload.

Today, all Employees of SIT and Harbour Light are using it, as well as five large customers are permanently using this channel oriented communication tool to get work done. Mattermost integrates nicely with our Jitsi Meeting Server and soon with HCL Samteime Meetings.

In the past couple of months, a variety of our customers adopted the tool to their own portfolio. And last month, we implemented the first integration with HCL Notes and Mattermost using the new HTTP Classes from LotusScript. But we did not stop there - we also built an integration both ways by adding a recognizer to Mattermost to simplify the back and forth of data integration. So what did we build ?


Image:Collaboration at its best - simple Integration between HCL Notes and Mattermost

First, we built a REST-Service on HCL Domino based on an XAgent, that renders a Notes or HTTPS-Link to a specific Document and opens that document in a browser or the Notes Cleint, depending on the parameter it receives. As a key, we transfer the internal document  number of the Documents in the customers project documentation application. These are unique keys that follow a specific alpha-numeric pattern. Using this REST-Service, we created a recognizer in Mattermost using REGEX and a mapping to our REST-Service, so whenever the users of the customers type a document identifier following their patterns, this automatically turns into a link that links back to the documentation app. Target group are engineers working together on building pressure molding machines,
The other way around works as well - as soon as a new project starts, the responsible Team Member for Documentation can create the relevant artifacts in the documenation app. Now, she can automatically create a corresponding Mattermost channel and auto-invite her Team to the channel to continue communication there by clicking an action button in HCL Notes. We are calling the Mattermost REST API straight from HCL Notes using the new LotusScript HTTP Classes.
To finish things off, we implemented a Slash command on Mattermost called "/persist" in a Channel - this will transfer the communication of the Mattermost Channel to the linked Notes Artifact in the Documentation Database as RichText including date/time Stamp of when the transfer happened and by whom it got initiated. Also, we integrated an action to the mail file that allows the transfer of mail content to a mattermost channel. With these two features we are able to have live communication in Mattermost but are able to take the communication tarnsparently back to the product development documentation. The engeneering teams enjoy the easy way to stay up to date and to still be able to persist these more informal decision making processes if needed.


The whole integration was done in less than a week. Pretty awesome, eh ?!
Of course, we can integrate this in XPages and IMPRISIS DAP as well. Oh yes and in MS solutions as well if needed.

Food for thoughts,


Heiko.


P.S.: Link to Mattermost: https://mattermost.com/
Heiko Voigt   |   30 October 2020 17:06:48   |    Mattermost  HCL Notes    |   Comments [0]

Image:My session from CollabSphere with Graham Acres (C3UG)

Hello,


first of all I have to say a huge thank you to Richard Moy and the Team at CollabSphere 2020 - they pulled of a really great online Conference with almost 500 registered participants.

Everything went perfectly fine, Zoom as the Meeting platform performed really well.


Here are my slides and the video of my session around the AppDevPack - it was a great session, again with Graham Acres. Always fun to be presenting with him. We talked about the state of the union for the AppDevPack and shared our ugly duckling blues on why only so many people make use of this great tool. Enjoy the presentation and the video and sound off in the comments !


Slides:
https://www.slideshare.net/HeikoVoigt/inf104-hcl-domino-appdev-pack-the-future-of-domino-app-dev-nobody-knows-about

Cheers,


Heiko.
Heiko Voigt   |   30 October 2020 13:33:41   |    AppDevPack    |   Comments [0]

21 October 2020 Wednesday

DNUG Dvelopment Day - Part 2

Hello,

this is my Sample RSS Feed Entry from DNUG Development Day Part 2 today !

Image:DNUG Dvelopment Day - Part 2

Nach dem erfolgreichen ersten Teil des #dnug47online Development Tracks hat die Fachgruppe für den zweiten Teil der Onlinekonferenz am 21.10.2020 wieder vier spannende Themen auf die Agenda gesetzt. Diesmal werden Anwendungen und Möglichkeiten präsentiert, die mit Hilfe von aktuellen Technologien im Domino Umfeld erstellt wurden.

Zu Beginn wird Jürgen Kringe (DNUG) vorstellen, wie er mit HCL Volt das Tool zur Agenda Planung für diese Konferenz realisiert hat. In der nachfolgenden Präsentation wird Serdar Basegmez (Developi Information Systems) zeigen, wie mit den in Domino v10 / v11 eingeführten RESTful-APIs, eine Integrationen zwischen Domino und allen anderen Unternehmensanwendungen gelingt. In einem weiteren praxisorientierten Vortrag erläutert Heiko Voigt (SIT GmbH) ein Projekt in dem ein RSS-Feed-Aggregator auf Basis von Node.js und domino-db entwickelt wurde und warum es sinnvoll war, diesen Weg zur Lösung des Problems einzuschlagen. Zum Abschluss den Tages wird John Curtis (HCL) nicht nur die aktuelle Version der Domino Query Language vorstellen, er wird auch verraten, wohin die nächsten Entwicklungsschritte führen werden.

Die Fachgruppe Development freut sich sehr darauf, Euch zahlreich am Mittwoch, 21.10.2020 um 14 Uhr via Zoom-Meeting begrüßen zu dürfen.

Heiko Voigt   |   21 October 2020 13:27:14   |    DNUG  Development     |    [0]

Hello,


Another quarter has passed and once again HCL delivered on its promise to keep the update cadences for the HCL Domino AppDevPack to a release every quarter. Colin, Heiko and Graham sit down with Gordon Hegfield to go through the new features and updates !
Check out this new overview video here:


https://youtu.be/ZHLK6tjc4PU

Cheers,


Heiko.
Heiko Voigt   |   15 October 2020 18:49:11   |    Domino  AppDevPack    |   Comments [0]

Image:Graham Acres and I will be presenting at Collabsphere !

I am more than honored to have the opportunity to present at CollabSphere once again together with
my great colleague from C3UG, Graham Acres. A big thank you to Richard Moy and his team
for setting this event up and for having us as presenter. We are really looking forward to it !


I hope to see all of you there - it's a virtual event so no excuses for travel this year. And - it's free !

Main site: https://collabsphere.org
Registration: 
https://collabsphere.org/ug/cs2020.nsf/register.html

This is our session:


Track:
Infrastructure and Integration


Title
: HCL Domino AppDev Pack - The Future of Domino App Dev Nobody Knows
About


Abstract:

The AppDev Pack was added to Domino in V10 as a way for Domino to
enter into the world of full stack web development. It has only grown since
then but for whatever reason inside and outside of HCL it gets treated like
the ugly duckling of the family. It is easily the most important step
forward for your XPages apps, not to mention being accessible to a new crop
of young JavaScript developers. Graham and Heiko love this little duckling
and want to share with you the power that HCL have given us. We will
demonstrate how it is not one but two ducklings now that we have Java
bindings as well. Be prepared for Concepts, Demos and Code that will help
these ducklings turn into beautiful swans in your Domino environment.
Heiko Voigt   |   8 October 2020 07:54:41   |    Domino  HCL  Usergroup    |   Comments [0]

Hello,

nowadays, most of development work is in JavaScript, some server-side, some client-side. Over the past  several months, I learned a lot about how not to do thing in JavaScript and a couple of things finally sunk in. One of them is how copy Arrays in JavaScript not using the "=" way, which, of course, is wrong.

With this post, I want to write down what I have learned about cloning arrays in JavaScript - especially and most  importantly because copying an array using = only copies a reference to the original array. Let's start with a sample to define to problem this creates:


const users = ['Peter','Paul','Mary'];
const copyOfUsers = users;

if(users === copyOfUsers) {
     console.log('Both are the same');
}

So, now that we have our reference copies, what will happen if we make changes to both objects ? Well - it will  simply change the data of BOTH objects since they both reference the same data in memory !
const users = ['Peter','Paul','Mary'];
const copyOfUsers = users;

users.push('Otto');
copyOfUsers.push('Hans');

console.log(users);
console.log(copyOfUsers);


So this will log ['Peter','Paul','Mary','Otto','Hans'] in both cases !

Imagine deleting something or changing a value....

How can we work around this so we can have a real copy of an array in  case we need it  ? Well, there's the option to use the spread operator when creating the array like so:
const users = ['Peter','Paul','Mary'];
const copyOfUsers = [...users];

users.push('Otto');
copyOfUsers.push('Hans');

console.log(users);
console.log(copyOfUsers);


So this will log ['Peter','Paul','Mary','Otto'] and ['Peter','Paul','Mary','Hans'] in these two cases !

Job done !

We could also concat an empty array and our existing array to get a new array like so:
const users = ['Peter','Paul','Mary'];
const copyOfUsers = [].concat(users);

users.push('Otto');
copyOfUsers.push('Hans');

console.log(users);
console.log(copyOfUsers);


So this will log ['Peter','Paul','Mary','Otto'] and ['Peter','Paul','Mary','Hans'] in these two cases as well  !

Also, the .from derivate is able to do this like so:
const users = ['Peter','Paul','Mary'];
const copyOfUsers = Array.from(users);

users.push('Otto');
copyOfUsers.push('Hans');

console.log(users);
console.log(copyOfUsers);


again, this will log ['Peter','Paul','Mary','Otto'] and ['Peter','Paul','Mary','Hans'] in these two cases.

So problem solved ? Not so fast young padavan. This only works for the so called "Shallow copying" of arrays. This means, we have no nested object  structure inside of the array and all objects are on one, the so called first level.

What does that mean ? Look at this sample:
const users = ['Peter',['Paul'],'Mary'];
const copyOfUsers = [...users];

// modify the nested part in the copy:
copyOfUsers[1].[0] = 'Carl';

console.log(users);
console.log(copyOfUsers);


So this will log ['Peter','Carl','Mary'] in these two cases, so the original users object got affected as well !

Now, to resolve this problem, there are two options to use "deep copying" instead of shallow copying:

Option 1: We can use the Lodash Library and the _.cloneDeep feature:
const users = ['Peter',['Paul'],'Mary'];
const copyOfUsers = _.cloneDeep(users);

// modify the nested part in the copy:
copyOfUsers[1].[0] = 'Carl';

console.log(users);
console.log(copyOfUsers);


So this will log ['Peter','Carl','Mary'] and ['Peter',['Paul'],'Mary'] in these two cases, so the original users object has not been affected. Phew !

This is my preferred method as it is working with all sorts of data types and nesting structures, as well as functions and symbols. Keep in mind that functions and Symbols are copied by reference when using _.cloneDeep .

In case you can't use lodash for whatever reason, you can use option 2, bare in mind that if you need function and symbol support, this won't work..

Option 2: JSON.parse()
const users = ['Peter',['Paul'],'Mary'];
const copyOfUsers = JSON.parse(JSON.stringify(users));

// modify the nested part in the copy:
copyOfUsers[1].[0] = 'Carl';

console.log(users);
console.log(copyOfUsers);


So this will log ['Peter','Carl','Mary'] and ['Peter',['Paul'],'Mary'] in these two cases as well.

So, sometimes things are not that easy in JavaScript. You have to be aware of what you need to achieve, to pick the right way to solve a given problem. Hope this helps a bit in understanding the way to work with arrays and cloning.

Heiko.
Heiko Voigt   |   5 October 2020 13:53:20   |    AppDevPack    |   Comments [0]