XPages sufficient for line of business type of applications?

Hi there, currently I am following another Angular course since it seems to have become the leading development framework at work. So back to learning all the rules within Angular.

At the moment I am modernizing a Domino application with the help of XPages which:

  1. Implements Model-View-Controller architecture, mostly inspired by the guys at Pipelia since IBM never told us to do so.
  2. Is written in Java to support the MVC architecture and to have close integration with XPages runtime.
  3. Is using Expression Language wherever possible to avoid usage of SSJS.
  4. Uses the lifecycle of JSF in XPages at the max.
  5. To cover support for different devices I am using Bootstrap as front-end framework. So I miss some native behavior which I do not tend to cover-up.

So far so good and I think I have come quiet long in my project so I still dare to call it rapid application development.

The code-base has been reduced dramatically and all exotic upcoming JavaScript libraries from the early 2000 I have been able to replace with just XPages. With my latent UX skills and extending the out of the box Bootstrap I might now even call this application ‘sexy’ 🙂

I know I haven’t touched many areas discussed in the XPages community such as:

  • Websockets (I do not see a use-case yet).
  • Writing Java servlets (please pass me a demo NSF).
  • Watson services (cloud is still a sensitive topic).
  • set up micro-services with smartNSF and consume them in my Java code with an mapper library (requires changes in the environment).
  • Integration with IBM Connections.
  • Redefining my data with the help of a Graph DB.

Either I see little usage, it is not possible or there is no-one to guide me (the information is certainly not provided by the vendor).

So now back to Angular. Learning all these rules, technologies and new tools setup I was wondering what new technical options this framework will bring me at work. Reflecting on the type of customer-orders I receive I am wondering:

Is XPages not sufficient for most of your line of business apps?

Perhaps you have a though about this?

Happy development & enjoy your summer 🙂

Display JSON pretty in a textarea control

In an application I am using heavily JSON default as the data-format. To make debugging a bit more easy I am working on a toolbar to be able to check the content of the JSON objects.

Normally I use JSONLint most of the time to prettify (and validate) the JSON so it becomes a bit easier to read and check. But this copy and paste habit becomes more and more obnoxious so there I wanted to have a toolbar (like the debug toolbar) to have a good overview of the JSON objects.

However if you write the JSON object to a computed field or text area field (inputTextarea control in the XPages world) it is just plain, unformatted text. Not pretty for the eye and not much helpful for the brain.

So I tested a simple approach which turns out to run just fine. I display the json object in a textarea input control and then with CSJS I format the content. Here is some sample code:

<xp:inputTextarea id=”input-fieldx” defaultValue=”#{javascript:App.currCollection}”>
</xp:inputTextarea>
<xp:button value=”Make Pretty” id=”button1″>
<xp:eventHandler event=”onclick” submit=”false”>
<xp:this.script>
<![CDATA[var name = x$(‘#{id:input-fieldx}’).attr(“name”);
prettyPrint(name);]]>
</xp:this.script>
</xp:eventHandler>
</xp:button>
<xp:scriptBlock id=”scriptBlock1″>
<xp:this.value>
<![CDATA[function prettyPrint(id) {
var obj = dojo.byId(id);
var ugly = obj.value;
var json = dojo.fromJson(ugly);
var pretty = JSON.stringify(json, undefined, 4);
obj.innerHTML = pretty;
dojo.setAttr(id,”rows”,10);
}]]>
</xp:this.value>
</xp:scriptBlock>

Here is what it looks like:

json-before

The content how it looks initially.

json-after

The content how it looks like after running the code.

Note I had to include the [ ] signs in my code to make the JSON valid due to circumstances in my code, I removed it from the sample code above.

At least now I understand my JSON objects again and I can avoid some copy and paste to JSONLint 🙂

This is probably my last blog of this year so I would like to wish you some wonderful Christmas days. Take care and best wishes for a healthy 2017!

 

 

Adventurer

I have always known that I am an adventurer. My Lotus/IBM Notes career started as a adventure into an international professional working-experience and the people who know me will probably say that it will be very hard to make an ordinary “9 to 5” office guy out of me.

I could never resist the call of the trail – Buffalo Bill

So far about me as person. The occasion of this post are several calls in the community to take a look at Salesforce as an application development platform.

So I took the time to take a look at it to find out if it would be of interest of me. As suggested my journey started with Trailhead. My journey, so far has lead me to finish the developer beginner trail.

I will write about my first experience and will not do any comparison, simply because my knowledge on Salesforce is limited to do so.

If you read this trail description it tells you it will take you up to 15 hrs and 45 minutes. Well I can tell you: it took me much more time. There is a LOT of text to read, video’s are  included and there are challenges to complete modules and to earn points for badges. A challenge can be a list of questions or an exercise.

So let’s assume all the effort and time-investment does contribute to your understanding of the platform.

Before starting, a couple of developers told me about the similarities with Notes and XPages and I have to say it is nice to recognize that building XPages applications have brought Notes developers closer to genuine application development (as far as it compared with Salesforce).

Salesforce biggest ugliness at start is of course it is not NonSQL as Notes is.

A nice feature is Salesforce web enabled development interface. I have not come to the Eclipse IDE yet. I have used the classic web interface, since in Trailhead the examples and screenshots are from the classic interface. I can not say I am in love with a web interface since I noticed some genuine lags compared to the XPages IDE (autocomplete for example). Also getting into read mode after performing a save action I found annoying.

I admit there are similarities programming in Salesforce compared to Notes and XPages, but I guess this comparison is similar with other programming languages. When it regards to building custom user interfaces Visualforce reminds me a lot about XPages.

So after completing this first trail I find the learning curve not that steep. Using Trailhead is just very time-consuming. An instructor or video-learning would have been much more time efficient. But if you want to gain some deep knowledge it doesn’t hurt to spend time on the approach.

A quote I heard is that Salesforce is the platform Notes should have been (if IBM would have prioritized it more) and that there are many more goodies to be found in the trail Develop for Lightning Experience so I am looking forward to take another hike. Ofcourse with my newly earned Adventurer badge on my backpack 🙂

Setting up a boilerplate for Angular 2

Introduction

I guess we all say amen to the following quote:

“The Only Thing That Is Constant Is Change” – Heraclitus

Working with XPages / IBM Notes you get scare-mongered a couple times a year thatthe whole thing is dead. With the Java vs JavaScript deathmatch not decided yet as ICS developer you might wonder on what train to jump next.

In the past I have been looking at Angular but never took it to the next level. With Angular 2 recently official released the cards might be different this time. Just look at the job vacancy sites and Angular specialists are searched after everywhere.

So I took a course Angular 2 and now I am migrating an XPages app to Angular 2 to see if the love is both directions.

Angular 2 boilerplate

A big difference with the previous version of Angular is the preparation. Angular 2 requires some sort of boilerplate, which I will describe how to build in this post.

WebContent/ODP

I will be using Domino as the HTTP server, since my data still resides in NSF. So for now my Angular app will live in the WebContent folder, so that is the working directory of my application. For ease of working I have setup a Github project. Added an ODP folder and synched my NSF with that.

In the WebContent folder in my ODP I will create the following documents:

  • tsconfig.json
  • typings.json
  • package.json
  • app/app.component.ts
  • app/main.ts
  • index.html

If you want you can just download a quickstart somewhere.

tsconfig.json

Create this file in the root of your project. In my course Typescript is used, but you could also script in Angular with JavaScript of course.

tsconfig defines the compiler configuration, here we say that our target code is JavaScript.

{
“compilerOptions”: {
“target” : “es5”,
“module” : “system”,
“moduleResolution” : “node”,
“sourceMap” : true,
“emitDecoratorMetadata” : true,
“experimentalDecorators”: true,
“removeComments” : false,
“noImplicitAny” : false
},
“exclude” : [
“node_modules”,
“typings/main”,
“typings/main.d.ts”
]
}

typings.json

Also place this file in the root of your project. In this file you define the Typescript declarations. It turns out that the Typescript compiler does not recognize several libraries…

{
“ambientDependencies”: {
“es6-shim”: “github:DefinitelyTyped/DefinitelyTyped/es6-shim/es6-shim.d.ts#7de6c3dd94feaeb21f20054b9f30d5dabc5efabd”,
“jasmine” : “github:DefinitelyTyped/DefinitelyTyped/jasmine/jasmine.d.ts#7de6c3dd94feaeb21f20054b9f30d5dabc5efabd”
}
}

package.json

This file will also be stored in the root of the project. In this file all dependencies and it’s versions are registered, which we will install with NPM.

packagejson

app.component.ts

An Angular 2 app needs at minimum 1 component, the root component. Components are the basic building blocks of Angular applications. A component controls a portion of the screen—a view—through its associated template.

import {Component} from ‘angular2/core’;

@Component({
selector: ‘hello-world’,
template: ‘<h1>Hello World</h1>’
})
export class AppComponent {
// … here we will add our logic later
}

So first you define a component, and then you make it available for export so you can import it into the application.

main.ts

This file is the bootstrapper in our app. Since our boilerplate is simple it does not contain so much yet:

import {bootstrap} from ‘angular2/platform/browser’
import {AppComponent} from ‘./app.component’

bootstrap(AppComponent);

Note: At this moment we do not have declared to use any module so we do not have to create an app/app.module.ts file.

index.html

The index file contains out of 2 sections, the first is the header, the other one the body. The header can be divided into 3 steps:

  • add styling
  • load the libraries
  • configure our main.js

index01

The second part is the body content of the HTML file:

index02

 

We’re done!

With this in place we have our boilerplate in place. Run “NPM install” and thereafter “NPM start” or open the index.html file on your domino server (after you have synced the files back to the NSF (this might take a while).

Also notice how the Typescript files are compiled to JavaScript files:

typescript-conversion

And finally “surprise-surprise” the result:

result-angular

Summary

Starting developing with Angular 2 is a bit different in comparison with version. Backward compatibility has always been strong for IBM Notes/Domino.

TypeScript is another opportunity for developers. I intend to blog more about my Angular journey and we’ll see how long the love will last 🙂

Here are some links of interest:

Typescript – Quick start

Angular – Quickstart

 

Bootstrap Greenhouse

I collected some code examples for plugins to Bootstrap in XPages in a GitHub project so I have them for convenience stored in one place.

bootstrap-greenhouse

Add 20 years of experience to your workforce

You can 20 years of experience within IBM Notes and Web development to your workforce by hiring me.

Interested? Read my curriculum vitae on LinkedIn: http://www.linkedin.com/in/patrickkwinten and get in contact.

I am happy to work WITH you !

URL references in XPages

Aaaah URL references, I guess everyone with Domino and XPages have struggled with them. At first you think you have tackled them, but then you open your app from another perspective (e.g. web browser launch – open designated xpage) and you discover the references did no work like you thought they would.

So how do they work? I made an xpage to demonstrate their working:

url-references

I made 6 types of references:

/.ibmxspres/domino -> this brings me back to the root of the server (or data directory)

/.ibmxspres/global -> this brings me to the folder domjava/xsp/ on the server. This is where for example server-side themes are installed

/.ibmxspres/dojoroot -> this brings me back to the folder /xsp/.ibmxspres/dojoroot-1.9.7/ on the server. This is the installation folder of dojo.

/ -> this brings me to the root of the nsf (when standing on an xpage ofcourse).

./ -> this brings me also to the root of my nsf.

../ -> a step to much, the filename is cut off, so I am standing in the installation folder of my nsf.

Can you fix the broken ones? Ofcourse:

/.ibmxspres/domino -> compute: return “/.ibmxspres/domino/” + database.getFilePath() + “/” + the resource where you want to point to.

/.ibmxspres/global -> compute: “/.ibmxspres/global/” + “../../” + database.getFilePath() + “/” + the resource where you want to point to.

/.ibmxspres/dojoroot -> compute: “/.ibmxspres/dojoroot/” + “../../../” + database.getFilePath() + “/” + the resource where you want to point to.

../ -> “../” + database.getFileName() + “/” + the resource where you want to point to.

If you have a custom stylesheet called custom.css in your nsf you can refer to it in a Theme design element as followed:

<resource>
<content-type>text/css</content-type>
<href>/.ibmxspres/domino/css/custom.css</href>
</resource>

I made the sample available on Github. Hopefully next time I do not make the same mistakes with URL references. Happy development =)

Add 20 years of experience to your workforce

You can 20 years of experience within IBM Notes and Web development to your workforce by hiring me.

Interested? Read my curriculum vitae on LinkedIn: http://www.linkedin.com/in/patrickkwinten and get in contact.

I am happy to work WITH you !

 

 

Reading files stored in the NSF webcontent folder

In an XPages application I am creating presentations (openxml presentations is perhaps a more correct defintion I guess).

Since I would like to use the template for house-styling I have to read it in from somewhere and use it in my code. I do not want to go for the option to read it from a server location, so after a while I ran into Sven Hasselbach’s NAPIUtils class available on GitHub.

If you search a bit further I found this handy description on Notesin9 how to add the lwpd.domino.napi.jar file to the build path of your application, which is required.

What the NAPIUtils class makes possible is to read files that reside inside your NSF e.g. the webcontent folder.

Just call something like

var fileIn:java.io.FileInputStream = NAPIUtils.loadBinaryFile(database.getServer(),database.getFilePath(),”your_file_in_webcontent_folder.correctextension”)

And you have a your file in a FileInputStream!

In my case I am using

var inputstream = NAPIUtils.loadBinaryFile(database.getServer(),database.getFilePath(),”Duffbeers.pptx”);
var ppt: XMLSlideShow = new XMLSlideShow(inputstream);

And I have my presentation template residing in my NSF in my XMLSlideshow object =)

So no more hustling and asking your administrators for a folder on your Domino server to read/write to.

Ofcourse, they probably have to change the java security settings, but that is another discussion. 😕

In some cases you do not want users to have access directly to an URL of a resource. In such cases you could read the file in and write it out in the browser. E.g.:

<?xml version=”1.0″ encoding=”UTF-8″?>
<xp:view xmlns:xp=”http://www.ibm.com/xsp/core”&gt;
<xp:this.beforeRenderResponse><![CDATA[#{javascript:importPackage( ch.hasselba.napi );

var exCon = facesContext.getExternalContext();
var response = exCon.getResponse();
var out = response.getOutputStream();
try {
response.setContentType(“application/octet-stream”);
response.setHeader(“Content-Disposition”,”attachment;filename=cv_patrick_kwinten.pdf”);
response.setHeader(“Cache-Control”, “no-cache”);
var fileIn:java.io.FileInputStream = NAPIUtils.loadBinaryFile(database.getServer(),database.getFilePath(),”Patrick_Kwinten_visualcv_resume.pdf”)
var buffer = new byte[10000];
var len;
while ((len = fileIn.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
} catch (e) {
// some basic error logging here…
} finally {
if (fileIn != null) {
fileIn.close();
}
if (out != null) {
out.close();
}
facesContext.responseComplete();
}}]]></xp:this.beforeRenderResponse>
</xp:view>

Happy development =)

Add 20 years of experience to your workforce

You can 20 years of experience within IBM Notes and Web development to your workforce by hiring me.

Interested? Read my curriculum vitae on LinkedIn: http://www.linkedin.com/in/patrickkwinten and get in contact.

I am happy to work WITH you !

Build JSON from NotesView, support for multiple value fields

The problem

An old-fashioned NotesView can be a fast way to define JSON objects. Here is a xsnippet that give you some inpsiration.

If you have fields with multiple values then the formula does not work anymore. You want to store an array in the JSON.

The fix

The following formula checks if the desired output fields contains more than 1 value  and if so transforms it to an array if not, then you get a normal key:value pair returned.

_fld:=”refName”:”refSub”:”refStrength”:”refWeakness”;

json:=””;

@For(n :=1; n<=@Elements(_fld); n:= n + 1;

@If(
@Elements(
@GetField(_fld[n])
)>1;
json := json + @Transform ( _fld[n]; “_fn” ; “\”” + _fn + “\”:”) + “[” + @Implode(@Transform( @GetField(_fld[n]) ; “_fn” ; “\”” + _fn + “\”” ) ; “,”) + “],” ;
json := json +
@Transform (
_fld[n]; “_fn” ; “\”” + _fn + “\”:\”” + @Text ( @GetField ( _fn) ) + “\”” + “,”)
)
);
“{ \”unid\”:\””
+@Text(@DocumentUniqueID)+”\”,” + @LeftBack(json;1) + + “}”

Test

I added my formula to a column and calling several single and multiple text fields. Here is my output in a browser:

notesview-as-json

If I validate the json it says it is valid:

valid-json

I added my code to the XSnippets database.

Add 20 years of experience to your workforce

You can 20 years of experience within IBM Notes and Web development to your workforce by hiring me.

Interested? Read my curriculum vitae on LinkedIn: http://www.linkedin.com/in/patrickkwinten and get in contact.

I am happy to work WITH you !

A modern commenting view in XPages

Introduction

In the old days you a typical commenting feature was something with a form and a view. Typically you could read a list of comments in a historical (flat) order and on the bottom you had the form to enter your comment. Something like in the IBM Domino blog template.

domino-blog

With the rise of social and mobile it became more important to be able to comment-only so the form moved to the top and the list of comments became sorted in the opposite order (Youtube).

Discussions are still displayed in a thread but since threads are less friendly on a mobile device a more what’app like chat display. But these are more instant messaging apps, similar to Slack or IBM’s upcoming Toscana.

slack

My idea: welcome to the bubble

So what can/should we do to modernize the display of the comment feature in our Notes/XPages application?

Well mainly we still have a top level object (TLO) where people comment on. To stimulate people to comment the form should be close to this TLO. We can promote the interaction between a user and others and provide some form of “bubble speech” experience and (ofcourse) the option to “like” a comment.

That would bring us to something more like this:

social_comment

 

Notice on the left you see the comments I have made and on the right comments made by others. The latest comment is displayed first and mines are a bit more outstanding using a background-color.

Now how it’s made

To come to this solutions I am using the following technologies:

  • XPages
  • Java
  • SSJS
  • Bootstrap
  • PrettyTime
  • CSS
  • JavaScript / mention JS

Note that in a previous post I described how to add @mention autocomplete like Facebook, Twitter & Google+ to your XPages app. So I will not discuss in this post how to build the form to comment.

Here is a general outline of what we are going to build:

outline

Step 1 – basic structure; a repeater with a panel

step01

First I have a repeat control which is bound to an ArrayList containing JsonObjects. This approach you can find in my Bildr project on OpenNTF (shameless plug).

I also added an infinite scroll function. You can read about how to set that up in this post.

The panel has a computed styleclass. The style defintion “right” does nothing more than a  text-align: right. This makes sure the text in the content is … aligned right.

Step 2 – adding a user icon

Humans are visually oriented and idle so nothing wrong making our display attractive with a user icon.

step02

In this example I am just displaying some random icons, but if you have a corporate directory with user profiles containing icons you should embed them here.

The pull-left and pull-right classes are from Bootstrap. My comments are pulled to the left, comments from others to the right.

Step 3 – the media body

Next item that we add is a div with a media-body class. It contains out of 2 sections: the content of the comment (text written) and some meta-data (date, author). I also added a “like” link which is inspired by Thomas Adrian’s Intrapages app also available on OpenNTF.

step03

(Guess I don’t need the attributes here…). Here is the part of the meta-data:

step03b

First I display the Author name, only for comments written by others. In order to make the date more human friendly to read I am using PrettyTime. In the past I have blogged about how to use it.

For the remove link I am using the userbean to check if I can delete documents in the database and if the comment is mine: (@UserName() == obj.From) && (userBean.canDeleteDocs == true)

For the event I check and ask if I am sure that I want to remove the entry and then I simply remove it by UNID:

step03c

For the “like” button I would like to point to Adrian’s example in his Intrapages project. I made however the exeption to store the likes in a separate document, so not in the comment. By this way I can tighten my security model. Everybody has the role of [liker] and author access. So everybody can update a like document and not the original comment document.

The like function uses the cookie management principles described here. It stores the username:

userCookie = new javax.servlet.http.Cookie(“userid”, @UserName());

I have set the duration of the cookie as long as the session is active:

//Cookie.setMaxAge(-1) will make the cookie expire after the browser is closed
userCookie.setMaxAge(-1);

But beware that the cookie still exists when users logout and login with a different username-password combination !!

So I have a counter for the number of likes, and depending on my activity I have a like or unlike link.

Ready! Some CSS whistles

After we have added some more (basic) CSS we are done! As a result we have created something much more modern that the old school comments display as we know from the IBM Domino Blog template.

If you are interested and want to receive the details of this setup I welcome you to send me a message and we can exchange code.

Add 20 years of experience to your workforce

You can 20 years of experience within IBM Notes and Web development to your workforce by hiring me.

Interested? Read my curriculum vitae on LinkedIn: http://www.linkedin.com/in/patrickkwinten and get in contact.

I am happy to work WITH you !

 

 

 

 

DevTip: using the latest font-awesome CSS toolkit in your XPages application

Tired off or you feel restricted by the Bootstrap Glyphicons? I bet you do. So you fancy to use Font-awesome as addition? Excellent choice. But after you have added them to your web-content folder and place them on an XPage you will notice the desired font does not appear.

Luckily for you other people have bumped into this before and allocated the problem and come with a fix.

So now you started to load the libraries your XPage application is depending on into ODP/NSF by using Bower or another package manager. If you do not specify a specific version but go for “latest” you will notice that in the suggested fix a version is specified for the font, so which each new release (which you are not aware of due to the “latest” option) your fix is out of sync.

CDN to the resque you might think. Since they handle the URL complication you are good to go as a consumer. Try to add a style sheet resource to an XPage without the http/https protocol is not allowed. If you modified it by hand in the source code like:

href=”//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/css/font-awesome.css”

in the browser something as

link rel=”stylesheet” type=”text/css” href=”/apps/customerx/fontawesomecdn.nsf//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/css/font-awesome.css”

will be produced which won’t work. You can add the protocol only once and you can not make the protocol conditional here.

Themes to the rescue! In a Theme design element you can check against what scheme the application runs and if so go for the corresponding protocol:

<theme>
<!– FontAwesome –>
<resource rendered=”#{javascript:context.getUrl().getScheme().equals(‘http’)}”>
<content-type>text/css</content-type>
<href>http://maxcdn.bootstrapcdn.com/font-awesome/latest/css/font-awesome.min.css</href&gt;
</resource>
<resource rendered=”#{javascript:context.getUrl().getScheme().equals(‘https’)}”>
<content-type>text/css</content-type>
<href>https://maxcdn.bootstrapcdn.com/font-awesome/latest/css/font-awesome.min.css</href&gt;
</resource>
</theme>

Here I have choosen to go against maxcdn which provides a “latest” option. If I check in the browser if delivers me

/*!
* Font Awesome 4.6.3 by @davegandy – http://fontawesome.io – @fontawesome
* License – http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
*/

Summary: problem solved.

  • I have the latest version of font-awesome
  • I have not an URL problem
  • I respect the protocol my application runs under

Unfortunately, my library is gone from my package manager file.

If you have a better suggestion then please drop a comment here below.

Update – Use Grunt

After writing this post I thought that their might be customers who block CDN resources. What to do then?

In Brad Ballasaitis example he created an additional CSS to overwrite the default font-family specification, one without the URL ?v= parameter.

In case you use a tool like Bower to download and install 3rd party libraries or plugins the original CSS with the URL ?=v parameter will be installed. You could write a simple Grunt task (I am new to this) that picks up your custom font-family CSS file from a temporary (fixes?) directory and copy it into the font-awesome CSS directory. A simple example would be:

/*
Copy font-awesome fix
Author Patrick Kwinten
All rights reserved
*/
‘use strict’;

module.exports = function (grunt) {

    require(‘load-grunt-tasks’)(grunt);
grunt.initConfig({
copy: {
main: {
src: ‘fixes/font-awesome-fontFamily.css’,
dest: ‘WebContent/libs/font-awesome/css/font-awesome-fontFamily.css’,
options: {
process: function (content, srcpath) {
return content;
},
},
},
}
});
};

If you run this task you are back to a state where you can now overwrite the default font-family selector again.

Load the CSS files via a Theme design element e.g.:

<!–
Not the following is not working in conjuction with the
XSP Property ‘runtime optimized resources’.

Therefor the following workaround is applied:
– Move the @font-face part into 2 new CSS e.g.
./font-awesome/css/font-awesome-fontFamily.css
./font-awesome/css/font-awesome-fontFamily.min.css

Note the paths to the fonts differ in these files.

– Load 1 of the css via the Theme based upon the context.getProperty(‘xsp.resources.aggregate’) property
–>
<resource rendered=”#{javascript:context.getProperty(‘xsp.resources.aggregate’).equals(‘false’)}”>
<content-type>text/css</content-type>
<href>font-awesome/css/font-awesome-fontFamily.css</href>
</resource>
<resource rendered=”#{javascript:context.getProperty(‘xsp.resources.aggregate’).equals(‘true’)}”>
<content-type>text/css</content-type>
<href>font-awesome/css/font-awesome-fontFamily.min.css</href>
</resource>
<resource>
<content-type>text/css</content-type>
<href>font-awesome/css/font-awesome.min.css</href>
</resource>

Add 20 years of experience to your workforce

You can 20 years of experience within IBM Notes and Web development to your workforce by hiring me.

Interested? Read my curriculum vitae on LinkedIn: http://www.linkedin.com/in/patrickkwinten and get in contact.

I am happy to work WITH you !