Developing H5 Client scripts – Part 2

I am learning to develop H5 Client scripts for a customer; see my previous post for the beginning.

To give back

My customer Chris Bullock thought client side scripts are pretty awesome, and said it would be cool if there was a library of what other M3 users have done. I proposed to post the script here with his permission, and he agreed for the love to give back.

Functional requirement

The requirement is to develop a script for M3. Purchase Order. Receive Goods – PPS300/E in H5 Client that pulls values from a related field in M3 and populates it in another field. Basically getting attribute values from purchase order line, concatenating their values, then populate that in the Lot number field on the purchase order receipt screen.

More specifically, when the user is receiving goods for a purchase order in PPS300/E, the script should automatically set the Location (WHSL) and Lot number (BANO) with the correct values to not let the user enter incorrect values by accident even with the F4-Browse:


Here is the preliminary script I developed:

var PPS300E_BANO = new function () {
	var color = 'rgb(250, 255, 189)';
	this.Init = function (scriptArgs) {
		var controller = scriptArgs.controller;
		var content = controller.GetContentElement();
		var IBITNO = content.GetElement('IBITNO')[0]; // Item number
		var IBPUNO = content.GetElement('IBPUNO')[0]; // Purchase order number
		var IBPNLI = content.GetElement('IBPNLI')[0]; // Purchase order line
		var WLWHSL = content.GetElement('WLWHSL')[0]; // Location
		var WRBANO = content.GetElement('WRBANO')[0]; // Lot number
		// ensure the Item group is MAT
		ScriptUtil.ApiRequest('/execute/MMS200MI/Get;returncols=ITGR?ITNO=' + encodeURIComponent(IBITNO.value), response => {
			var ITGR = response.MIRecord[0].NameValue[0].Value.trim();
			if (ITGR !== 'MAT') {
			if (!WLWHSL.readOnly && WLWHSL.value === '') {
				// hard-code the Location to YARD
				WLWHSL.value = 'YARD';
				// color the field = color; = color;
			// get the Attribute number
			ScriptUtil.ApiRequest('/execute/PPS200MI/GetLine;returncols=ATNR?PUNO=' + encodeURIComponent(IBPUNO.value) + '&PNLI=' + encodeURIComponent(IBPNLI.value), response => {
				var ATNR = response.MIRecord[0].NameValue[0].Value.trim();
				// get the attributes
				ScriptUtil.ApiRequest('/execute/ATS101MI/LstAttributes;returncols=AALF?ATNR=' + encodeURIComponent(ATNR), response => {
					// calculate the Lot number
					var BANO = '';
					response.MIRecord.forEach(e => BANO += e.NameValue[0].Value.trim());
					if (!WRBANO.readOnly && WRBANO.value === '') {
						// set the Lot number
						WRBANO.value = BANO;
						// color the field = color; = color;
				}, (error, message) => MainController.Current.ShowDialog([error, message]));
			}, (error, message) => MainController.Current.ShowDialog([error, message]));
		}, (error, message) => MainController.Current.ShowDialog([error, message]));

Development time

When I develop the script, I alternate between pieces of code in Chrome’s JavaScript console and debugger, and the assembled script in a text editor, iteratively until it’s ready, testing along the way with ScriptName.Init({ 'controller': getActiveController() }):


The result is the following, the script sets the Location and Lot number, and highlights them in yellow with the same color as the web browser’s autofill color to indicate that it autofilled the values:

At this point, the user can verify the values and click Next (or press ENTER) to persist the values in M3.


There are several problems with this script:

  1. The script is not able to tell apart whether the user entered the record with Option 1-Create or with Option 2-Change. In the former case, the script should set the values because the values have never been set; but in the latter case, the script should not set the values because they have already been set. I tried controller.Response.ControlData.Bookmark.Opt but it returns "2" for both Options 1-Create and 2-Change which is wrong. We are running M3 UI Adapter version In a thread with Reah, she said if we upgrade M3 UI Adapter to version, I will be able to use controller.GetMode() instead. To be continued.
  2. To make M3 API calls, I use ScriptUtil.ApiRequest. But as of M3 UI Adapter version, that method is deprecated and replaced by MIService. See my thread with Reah. To be continued.


There is this corner case in usability, unrelated to H5 Client scripts:

Initially, the customer wanted me to set the fields and disable them, no matter what. That works if the user creates a new record with Option 1-Create. But if the user enters an existing record with Option 2-Change and there are already values that another user has previously set, what should the script do? Should the script assume the values are correct and let it be? In which case the script could have guessed incorrectly and leave incorrect values behind (false negative). Or should the script assume the values are incorrect and reset them? In which case the script could have guessed incorrectly and contradict the intention of the previous user (false positive). Furthermore, if the script does reset the values, how will the user know those are new values to persist? Will highlighting in yellow be enough? Or will the user incorrectly assume those are the values currently persisted in M3? I have to read more about WebKit’s autofill design decisions and learn from it. For now, I apply the weakest enforcement: if the field is blank, set it; otherwise, do not; and never disable it.


There are several pending issues:

  • Upgrade M3 UI Adapter to the latest version
  • Use controller.GetMode() to tell apart Option 1-Create and 2-Change
  • Replace ScriptUtil.ApiRequest by MIService
  • Usability: disable the two fields while calling the M3 API, indicate activity (spinning wheel), revert when done, cancel if gone (ENTER, F3, F5, F12)
  • Add exception handling: if == null, try/catch, if response.Message, if !response.MIRecord
  • Compose the promises sequentially with request1.then(request2).then(request3) or Promise.all([request1, request2, request3]) instead of nesting them with request1({ request2({ request3() }) })
  • Use JavaScript async/await for ease of source code reading
  • Use Visual Studio and TypeScript as recommended by the M3 H5 Development Guide (H5 Script SDK)


That was my preliminary script for H5 Client while I am learning how to develop them. I still have to learn more about H5 scripts and autofill, solve current problems, and address pending issues.

Special thanks to my customer Chris Bullock.

Developing H5 Client scripts – Part 1

The day came I have to develop a script for Infor M3 H5 Client with M3 API calls for a customer. This post will add to my previous post, to Scott’s three previous posts, and to Karin’s previous post.


Scripts for H5 Client are written in the JavaScript programming language (ECMAScript). Scripts for Infor Smart Office are written in the JScript.NET programming language. Programs for M3 are written in the Java programming language. Despite the similarities, the three languages are different. Smart Office scripts will NOT execute in H5 Client; you will have to re-write most of the code and be familiar with functional programming, jQuery, Deferred, Promises, etc.; it is like back in the days of IBrix.


Here is the M3 H5 Development Guide:


Here are some tips to convert a Smart Office script to an H5 Client script:

Example of a minimal script for Smart Office:

package MForms.JScript {
	class Test {
		public function Init(element: Object, args: Object, controller : Object, debug : Object) {
			debug.WriteLine('Hello, World!');

The equivalent script for H5 Client:

var Test = new function () {
	this.Init = function (scriptArgs) {
		console.log('Hello, World!');

Various ways to get a field and its value compared to norpe’s guide for Smart Office:

var controller = scriptArgs.controller;
var host = controller.ParentWindow;
ScriptUtil.FindChild($, 'WRCUNM')[0]
ScriptUtil.FindChild(host, 'WRCUNM')[0]

UPDATE 2017-06-06: According to the H5 Development Guide, the above is not the recommended API, but controller.GetContentElement().GetElement("WRCUNM") instead.

Example to call an M3 API in H5 script:

ScriptUtil.ApiRequest('/execute/CRS610MI/LstByNumber', result => console.log(result), (error, message) => console.log([error, message]))

UPDATE 2017-06-06: According to the H5 Development Guide, the above is not the recommended API, but MIService.Current.execute|executeRequest instead, but I get error ‘MIService is not defined’; perhaps I do not have the latest version of H5 Client.

Chrome Developer tools

Use the Google Chrome Developer tools:

Use it for the list of global variables, code completion, type reflection, console, dynamic execution, debugger, network monitor, DOM, styles:

Pause execution to introspect global variables:

UPDATE 2017-06-06: In the JavaScript console, you can get the current controller with getActiveController() or MainController.Current.Instances.host_X where X is the controller number.

Administrative tool

Use the administrative tool to import/export H5 scripts:

UPDATE 2017-06-06: To update a script, simply re-import it, click Yes to override, and refresh the M3 panel with Actions > Refresh-F5; there is no cache thus no need to add the version number in the script file name unlike Smart Office.


Attach the script to the panel as usual at Tools > Personalize > Scripts:

Select Actions > Refresh-F5 to load the script:

Use the JavaScript debugger statement in your script to pause execution and invoke the Chrome debugger:

Call M3 API, and refer to my previous post:


That was a quick look at how to develop scripts for Infor M3 H5 Client including calls to M3 API.

Thanks to Scott Campbell of Potato IT for the first look.

Please like, comment, subscribe, share, come author with us, or look at other ways to contribute.

Related documentation

H5 Client and M3 API with jQuery DataTables revisited

Today I will revisit my previous articles on H5 Client and M3 API with jQuery DataTables: I will correct a few mistakes in my code, I will update the code to the latest versions of jQuery and DataTables, and I will introduce new features.

Motivation: IBrix conversion

I’m helping a customer convert their old IBrix to HTML5/JavaScript. The IBrix we’re starting with uses a lot of M3 API and M3 Web Services (MWS), as well as IPM <c:table> components to render resulting data in selectable lists of rows and columns. To replace that, we could write Infor Smart Office Mashups that use the controls m3:MIListPanel, m3:MIPanel, and mashup:DataService, but the customer won’t use Smart Office as their main user interface; they’ll use Infor M3 H5 Client instead. Alternatively, we could convert the Smart Office Mashups to Web Mashups for H5 Client, but Web Mashups currently don’t support the MI controls. Infor says they are adding support for more and more controls, but they haven’t released any specific information on which controls they’ll add nor on what timeline. The customer couldn’t wait for Web Mashups to support those controls, so we decided to rewrite the IBrix from scratch using HTML5/JavaScript, and to re-evaluate Web Mashups later when Infor releases something new in the future. We opted for jQuery as the JavaScript library – as opposed to another JavaScript library like Dojo – because jQuery is already used by H5 Client and Web Mashups, so if we have to learn something new we might as well just learn one. As part of this learning process, I will share with you what I learn.

Files & folders

I haven’t yet found a good place to put my HTML5/JavaScript code. So for now I will continue to use the mne folder in Infor LifeCycle Manager where I have my own sub-folder:

It’s probably not the best idea, but we already trust Smart Office Script files in the jscript sister folder, so I don’t yet see a problem with putting other files in a sibling folder. The only problem could be an upgrade or a migration that wouldn’t account for our piggyback folder and our folder could be lost (yikes!).

URI relative references

Most resources on the Infor Grid can be accessed over an insecure channel with HTTP, or over a secure channel with HTTPS, like many resources on the web. If our code requests resources over a mix of secure and insecure channels, and the user requests the page over a secure channel, the browser (for example Internet Explorer and Google Chrome) will protect the user: it will show a security icon in the address bar, it will show a security popup, and it will not load the resources that are over the unsecure channel unless the user confirms to do so:


The page behaves seemingly normal in one case and broken in another. It’s tricky to troubleshoot for the developer, users, support, etc.

The solution is to use URI relative reference for scheme abstraction, i.e. remove the scheme part of the URI. The browser will then load the resources using the scheme the user chose in the first place, HTTP or HTTPS.

For example, replace this:

with this:


The resulting URI looks strange, yet it’s valid. It’s not a well-known technique, yet it has been in the URI specification for a long time. And it’s important in our case to prevent a potentially broken page that’s hard to troubleshoot.

URI relative references cont’d.

Also, the Infor Grid is a distributed system where the same application can run on any of the deployed nodes and any of the binding ports. Thus the variations in the URI can be on the scheme HTTP/HTTPS, the host A or B, and the port number X or Y. For example the user could legitimately request any of these and should get the same Grid application:

So our code must account for this possibility. If we used absolute URI in our code, H5 Client and our code wouldn’t be within the same document origin, and we could run into same-origin policy restrictions.

The solution is to use relative references again, this time just with the path and query parts of the URI, without the scheme://host:port parts.

For example, use:


instead of:



As we’re developing for the web, we have to be cognizant of caching and cookies. In my case I’m developing static HTML pages that I’m updating frequently (within seconds). I’m using a browser to execute the page. The browser caches the page, and there could be proxies along the way that cache the page. I’m using Fiddler to intercept M3 API requests/responses. MNE uses cookies to maintain the session across requests. To prevent caching, I use Fiddler > Rules > Performance > Disable caching, and I switch with the anonymous mode of the browser, InPrivate Browsing in Internet Explorer, and Incognito window in Chrome to start fresh without cookies. And I check my page versions and cookies in the HTTP Responses in Fiddler.

Security vulnerability

H5 Client is one of the Grid applications that will work over both HTTP-only, an insecure channel, and HTTPS, a secure channel. Unfortunately, when using HTTP, the login phase is done over HTTP-only as well, it’s not redirected over HTTPS and back like M3 Workplace did, and as all sites should do. So the user/password is sent Base64-encoded over the network, i.e. in clear text:

I validated this with H5 Client (Enterprise):

That’s a security vulnerability. So I’m looking into if this was an undetected installation/configuration mishap, and how to force HTTPS-only and prevent HTTP, at least for the login phase, or at default for the entire MNE. I reported this to Infor. To be further investigated.

H5 Client is usually confined to the corporate network behind firewall and NAT, so this is not as big a risk as with a vulnerability that would be exposed on the Internet.

UPDATE 2014-05-07: It appears this was a misconfiguration of the particular Grid I tested. Check your Grid in case you too have an undetected misconfiguration. Go to: Grid Management > M3_H5_Client > Web Components > Web Routers > Configure. The WWW Authentication Methods should only be checked for HTTPS, and unchecked for HTTP. I don’t do installations/configurations of the Grid, so this is to be confirmed by a certified installer.

 H5 Client lifetime

Let’s learn more about the H5 Client lifetime: login, session handling, keep alive, and logout.

  1. When the user requests H5 Client at /mne, /mne/, or /mne/index.jsp, the J2EE Jetty server sets a new cookie JSESSIONID for the session. I don’t yet know where that cookie is used.
  2. At /mne/index.jsp the server will challenge the client for authentication, and the browser will prompt the user for id/password in a popup.
  3. The user enters the M3 userid/password, and the browser sends that Base64-encoded along with the JSESSIONID cookie. The Authorization header will be transmitted throughout the session.
  4. The server authenticates the credentials and responds with a new cookie JUZUSR2SKRJVIOR2. This cookie will be used by the browser to maintain the session across requests, and it’s even validated by M3 API and MWS which will be very useful later.
  5. At this point we can add our HTML5/JavaScript code into an H5 Client Page. The browser will pass along the JUZUSR2SKRJVIOR2 cookie and authenticate our M3 API and MWS requests.
  6. Also, H5 Client will POST a CMDTP=LOGON to the MNE server with the userid UID and the two cookies. The server seems to ignore the cookies here.
  7. The server will respond with the MNE session id SID.
  8. Then the user optionally opens an M3 Program like CRS610.
  9. When the user closes the browser, H5 Client will issue CMDTP=QUIT with the SID, and the server will logout that user from M3 and invalidate that SID. However, the cookies are not invalidated. Is that another potential security vulnerability?
  10. Also, H5 Client does ping with CMDTP=FNC&CMDVAL=PING at about a 25mn frequency to maintain the session alive and not let it timeout.

We can now draw some useful conclusions:

  1. For H5 Client, we need the SID parameter, and either the HTTP Basic Authorization header or the cookie JUZUSR2SKRJVIOR2.
  2. For M3 API and M3 Web Services, we can use either the HTTP Basic Authorization header or the cookie JUZUSR2SKRJVIOR2. This will prove very useful later.

The JSESSIONID cookie doesn’t seem to be used anywhere.

In my observations, I saw both a HTTP Basic Authorization header and the cookie JUZUSR2SKRJVIOR2 throughout the session, and that seems redundant to me as we only need one, not both. I don’t see an impact yet.

I did my tests by analyzing the HTTP Requests/Responses with Fiddler, and with forged HTTP Requests in Fiddler Composer to try the various combinations. And I did my tests in a rush so not everything might be accurate. Please comment if you find a discrepancy. All I wanted to know was how to authenticate the M3 API and MWS requests.

M3 API authentication

We now know we don’t need to authenticate our M3 API requests, i.e. we don’t need to hard-code any userid/password in our code nor prompt the user to login, provided we conform to the following:

  1. Our code must run within the same browser session as H5 Client, i.e. same origin, for example in an H5 Page.
  2. Remove any authentication from our code, i.e. remove any user/password, and don’t authenticate HTTP Requests to M3 API or MWS.
  3. Use only the path & query parts of the URL without the scheme://host:port, so for example use just /m3api-rest, in order to be within the same origin, so the browser will pass the cookie along, regardless of scheme HTTP/HTTPS, host/FQDN, port

Alternatively, we could build our own login page, but we would have to deal with password management (password expired, forgot password), session keep-alive, and logout.

M3 API with jQuery.ajax()

In my previous article, I had use the native XMLHttpRequest object to call the M3 API. This time I will use jQuery.ajax():

	$(document).ready(function () {
			url : "/m3api-rest/execute/CRS610MI/GetBasicData;returncols=CUNM,TOWN,ECAR,PONO,CSCD?CONO=910&CUNO=ACME",
			"dataType": "json"

jQuery DataTables

Now to render the M3 API into a jQuery DataTable, I won’t do a time and memory consuming row and cell creation anymore. I’ll simply use the jQuery DataTables Custom data source property dataSrc:

	var program = 'CRS610MI';
	var transaction = 'LstByNumber';
	var maxrecs = 100;
	var returncols = 'CUNO,CUNM,CUA1,TFNO,STAT';
	var inputFields = 'CONO=910&CUNO=ACME';
	// construct the URL
	var url = '/m3api-rest/execute/' + program + '/' + transaction + ';maxrecs=' + maxrecs + ';returncols=' + returncols + '?' + inputFields;
	// prepare the columns for dataTable
	var arr = returncols.split(',');
	var columns = [];
	for (var i in arr) {
		columns[i] = { "data": arr[i] };
	$(document).ready(function () {
		var table = $('#CustomerList').dataTable( {
			"ajax": {
				"url": url,
				"dataSrc": function (json) {
					var result = [];
					for (var i in json.MIRecord) {
						var record = {};
						json.MIRecord[i]{ record[o.Name] = o.Value; });
						result[i] = record;
					return result;
			"columns": columns

And the HTML fragment with the column headers:

	<table id="CustomerList" class="display" cellspacing="0" width="100%">
				<th>Address line 1</th>

Row selection

To enable row selection:

	$('#CustomerList tbody').on('click', 'tr', function () {
		if ($(this).hasClass('selected')) {
		} else {

Context menu

To get a context menu (right-click), I use jQuery contextMenu, and it looks like this (unfinished code):

			selector: '#CustomerList tbody',
			callback: function(key, options) {
				// PENDING
			items: {
				"Select": {name: "select", icon: "select"},
				"Copy": {name: "copy", icon: "copy"},
				"Change": {name: "change", icon: "edit"},
				"Display": {name: "display", icon: "display"},
				"Delete": {name: "delete", icon: "delete"}

I’m still working on completing this code.


Here’s a screenshot of the result:

Also, jQuery DataTables supports client-side search, pagination, and sorting. It took about 6s to load and render 3,000 rows. That’s about 2ms per row, not bad. But I will look into making this server-side anyway as client-side is not suitable for production.



In this post, we re-visited how to call M3 API with jQuery, how to take care of authentication, how to maintain the session across requests, how to do session keep alive, and logout (in fact H5 Client takes care of it, and the browser passes along the session to our code provide we conform to certain conditions), how to render the result with jQuery DataTables, how to enable row selection, and how to add a context menu.

As future work, I will follow-up on the security vulnerabilities, complete the code for the context menu, I will implement the equivalent of the SelectionChanged event of Mashups, I will render the M3 API Get with jQuery, and I will implement the F4-Browse dialogs of Ken Eric.

With all those simple engineering problems tackled one after the other, I will have a good basis to start converting IBrix to HTML5/JavaScript.

A more tricky engineering problem will be to implement the server-side search, pagination, and sorting.

Related articles


That’s it! This was a sloppy article put together hastily to get it out of my system and anchor it at once. It nonetheless contains useful information. I’ll be posting more. Stay tuned. Subscribe. Comment. Share. Enjoy.

Introduction to Web Mashups

Here’s a quick introduction to Web Mashups for Infor M3 H5 Client; Web Mashups are the cousins of Infor Smart Office Mashups. To do Web Mashups, you’ll need Infor Smart Office, the Mashup Designer, and Infor M3 H5 Client. I’ll show lots of screenshots.

What are Web Mashups

Historically, Smart Office Mashups were just called Mashups and would run in Smart Office. Smart Office is currently the main user interface for M3, built using C# and Microsoft WPF. With the launch of Infor M3 H5 Client released about 2013, M3 can now run in HTML5/JavaScript in any major web browser (for example Microsoft Internet Explorer, Google Chrome, Apple Safari, Mozilla Firefox), in any major operating system (for example Microsoft Windows, Mac OS,  Linux, etc.), in any major device (Mac, PC, iPhone, iPad, Android, etc.). As part of that web enablement, Mashups are now automatically converted from Smart Office Mashups to Web Mashups, and they can run inside H5 Client or standalone in a browser.

To do Web Mashups, the developer creates Mashups as usual in XAML with the Smart Office Mashup Designer, and the server converts the XAML the best it can into HTML5/JavaScript, jQuery, REST/SOAP, and JSON/XML. Currently Web Mashups don’t support all the controls that Smart Office Mashups supports. I successfully tested m3:ListPanel, m3:DetailPanel, as well as Grid, StackPanel, Label, TextBox, and Button. It seems MIListPanel and MIDetailPanel are not supported. It seems Document Archive is supported. And it seems XAML’s ListView may already be supported, to be confirmed. According to the Product Manager and component owner at Infor, they are adding support for more and more controls. I expect loss along the conversion as I don’t think it’s possible to automatically convert all of XAML, Binding, Converters, and other advanced WPF tricks. But it should work fine if we stick to a specific subset of Mashups.


You can read more about Web Mashups on the Infor InfoCenter:

Web Mashup demo

I’m working with Ryan, an IBrix and J2EE developer at a long-time customer, to help him convert IBrix from the obsolete Movex Workplace to the new M3 13.1 as part of their company’s upgrade. We’re evaluating the capabilities of Web Mashups, and the best strategy to do IBrix conversion. Here I’ll illustrate the basics.

First, I’ll create a simple Mashup using the built-in example at Mashup Designer > Help > M3 Transactions > Item list & details:

That simple Mashup is good for illustration purposes as it shows records from M3 Customer. Open – CRS610, and it is made of a m3:ListPanel to show the list CRS610/B, and a m3:DetailPanel to show fields from CRS610 panels EFG:

Then, I’ll put the XAML inside a Mashup Project (*.manifest), and I’ll deploy the Mashup privately as a Web Mashup deployment target:

Then, Smart Office opens a Deployment Result popup confirming the Web Mashup is deployed privately, with two buttons Open and Debug:

Then, I click Open, and Smart Office launches the Web Mashup in my browser at /mashup/web/MyMashup, and the server prompts me for M3 authentication with User Name and Password:

Finally, I can use the Web Mashup, it works great:

I tried the Debug to simulate a Search event with a Query parameter, but it didn’t work for me, nothing happened when I clicked Execute event, and I don’t yet know why:

You can also check the version of your Mashup grid application at /mashup/about/version, in my case it’s

You can also go to the Administration UI and see the deployed Mashups at /mashup/admin:

You can also generate a *.webmashup package to deploy the Web Mashup globally:

You can also see the supported controls, parameters, and events:

You can also check some of the files in LifeCycle Manager:

That’s it!

Also, check out the post on Web Parts for H5 Client.

And don’t forget to subscribe by clicking the Follow button below, leave us your comments, share, and contribute.

Web parts for H5 Client – DRAFT

Here are illustrated steps to add a web part to H5 Client. This post follows-up my previous post where I introduced H5 Client. This is useful for creating personalizations in H5 Client.

As a reminder, H5 Client is a web client for M3 (HTML, CSS, JavaScript, jQuery, and XmlHttpRequests running in a browser) and if it runs as part of H5 Enterprise then we can add Web parts which are custom web development parts that we write to interact between H5 Client and our code. This technique is similar to Movex Next Extension Adaptation Interface (MNEAI) scripts for Movex Explorer around 1999 and Movex Workplace around 2000, and it is similar to Smart Office Scripts of today.


I started this post a long time ago, faced some technical issues, moved on to other assignments, and never managed to finish it nor polish it. So the screenshots don’t match with each other, and the steps are not necessarily all in order. It’s very much like a draft post. But I decided to post it anyway otherwise I’ll never post it. And there are enough steps and screenshots so you get the point. That will give you a head start.

M3 H5 Enterprise

First, you will need M3 H5 Enterprise to be able to use Web Parts; H5 Foundation will not be sufficient.

Create a webpage

Then, create a webpage and publish it on a web server of your choice. It will be run in an iframe in Ming.le and will communicate with H5 Client via message passing.

For example, I created a folder jscriptH5 next to the jscript folder that is used for Smart Office scripts, and I dump my HTML and JavaScript files there (this is probably not the best location as the parent folder is managed by LifeCycle Manager and might be deleted with upgrades…although so will the jscript folder):


You will need to be a bit familiar with jQuery.

Add code like this for message passing:

        <title>Thibauds web part test</title>
        <script src="somewhere/jquery-1.8.2.min.js"></script>
        <script src="somewhere/jquery.json-2.2.js"></script>
            jQuery(function ($) {
                // register message handler
                infor.companyon.client.registerMessageHandler('inforBusinessContext', messageHandler);
                // send a message to Ming.le (for instance resize the Web Part)
                infor.companyon.client.sendMessage(, { height : '235px' });
                // receive a message from Ming.le with the values of the Context
                function messageHandler(o) {
        Hello World!!!

Message definition

Then, follow these steps to add a message definition to the M3_UI_Adapter (mne) properties in LifeCycle Manager:

  1. Go to LifeCycle Manager
  2. In the Applications View, expand your environment (Development, Education, Test, etc.)
  3. Right-click M3_UI_Adapter and select Configure Application
  4. Click Edit Properties
  5. Expand Infor Workspace Settings
  6. Click the Value of the Property Context List to edit it (it will show the current number of Entries)
  7. Click Add New Line and select Append
  8. Enter a message definition as a JSON object, with

    uid, title, type, and data, where data is a JSON object with the key/value pairs you want to pass to your Web Part, for example:

    {“uid”:”M3_TBO”, “title”:”Thibaud Hello”, “type”:”thibaudHello”, “data”:{“hello”: “”}}

  9. Validate your JSON object for example with JSON lint
  10. Here’s a screenshot so far (the JSON in the screenshot was an old test with wrong values):
  11. Click Save to save changes to disk, a popup will appear
  12. Click Save again to confirm
  13. Now let’s verify it’s saved correctly
  14. Close the tab
  15. Back in the Applications View, right-click M3_UI_Adapter and select Monitor Application
  16. Expand the Node for Application M3_UI_Adapter and select M3UIAdapterModule
  17. Select Properties
  18. Double-check the Value of the ContextList Property and ensure your JSON object is correct; an incorrect value will break H5 Client without giving you any error messages and that will be difficult to troubleshoot.

Infor Ming.le administration

Then, follow these steps to become an administrator of Infor Ming.le:

  1. Go to Infor Ming.le at http://yourhost/SitePages/InforSuite.aspx
  2. Login as an administrator
  3. Select Site Actions > Site Permissions:
  4. Select Site Collection Administrators
  5. Enter the userids of those who will be administrators
  6. Click OK

Ming.le Application Viewer Web Part

Then, follow these steps to add your Web Part:

  1. Select Site Actions
  2. Select Ming.le Application Viewer Web Part:
  3. Select the Category, for example Infor
  4. Enter the Title, Description, and URL
  5. Select an Icon
  6. Click Save (the URL when I took the screenshot had an incorrect value):

Context Application Manager

Then, follow these steps to add the Context Application Manager:

  1. Select Context Application Manager in the top right corner of Ming.le
  2. Select your Web Part on the left, and click the right arrow
  3. Click Save (my screenshots don’t match my final test but you get the point):
  4. Now you have a Web Part on the Right Panel Zone:

Context Publisher

Then, configure the Context Publisher:

  1. Go to the desired M3 program/panel, for example CRS610/E
  2. Select Tools > Context Publisher, and select your Web Part
  3. Configure your Web Part by setting values for the keys, for example Hello World! <WRCUNM>:
  4. Click Save:

Start Web Part

Now start the Web Part by clicking on the Panel Zone. You web part code will get the parameters.

Future work

Future work would include:

  • Reduce the number of steps it takes
  • Configure the Web Part per M3 environment (DEV, EDU, TST, etc.). This seems like it’s not native to Ming.le Web Parts.
  • Un-hard-code the Web Part URL
  • Get any field dynamically in source code, ideally with a variable like controller or content, instead of having to configure the Message Definition and Web Part (I’m not an expert on this yet so there might already be some functions or variables available that I’m not seeing).
  • Apply the Web Part only to a desired program/panel.


Thanks to Joakim B. for all the help.

That’s it.

Data conversion techniques

Here below is an old slide I found in my archives where I list my known techniques for data conversion, i.e. how to push data into Infor M3, also known as data entry. This list intends to remind readers there are more solutions than the traditional techniques.

Data conversionTechniques

Traditional entry points

The two traditional entry points are:

  1. API – The traditional entry point is to call M3 API. Advantages: it’s the fastest and most reliable technique, and the most widespread in terms of platforms supported, libraries, tools, and documentation. Disadvantages: there aren’t M3 API available for every program/field/operation in M3, as given by the M3 API Repository – MRS001.
  2. MDP – When there’s no M3 API available, we use the other traditional entry point, Lawson Web Services (LWS) of type M3 Display Program (MDP) to simulate a user going through the screens at the middleware level in M3 Net Extension (MNE). Advantages: with the Lawson Web Services Designer we can create the equivalent of an M3 API, for most M3 Programs, in almost no time. Disadvantage: it’s less efficient to run than M3 API as there are more layers to traverse.

Those are the traditional techniques. And we massively call them with for example M3 Data Import (MDI), Smart Data Tool (SDT), M3 E-Collaborator (MeC), Visual Basic macros in Microsoft Excel, ProcessFlow Integrator (PFI), Infor Process Automation (IPA), Tibco, WebMethods, or custom Java/C#/VB programs, with the data coming from a source like for example a Microsoft Excel spreadsheet, a CSV or plain text file, or a staging database.

Alternate techniques

If the traditional entry points fail, there are two alternate techniques.

  1. Manual entry – We can always do manual data entry. Advantage: it requires almost no skills, no programming, and no tools. Disadvantage: it can become humanly impossible to manually enter large amounts of data.
  2. MAK – Alternatively, we can write an M3 modification with MAK, to create a new API or modify an existing one. Advantages: it’s the ultimate solution. Disadvantages: it requires an MAK developer, it can take time, and M3 mods create a maintenance problem.

Despair techniques

Then, there are the following techniques which are less know and which I use when I’m at a loss of ideas:

  1. MForms Automation – When there are no M3 API available, and when Lawson Web Services of type MDP fail for rare M3 programs, we can try to reproduce the steps with MForms Automation and write a Smart Office Script that loops thru a data source and executes the MForms Automation at each iteration. This is a proven technique and Seth will soon write a post illustrating this solution. Advantage: It’s the last card on the deck when you lost hope. Disadvantage: It’s less efficient because it’s at the user interface level.
  2. Bookmarks – Similarly, we can write a Smart Office Script to execute Bookmarks in a loop of the form mforms://bookmark?program=CRS620&tablename=CIDMAS&keys=IDCONO…
  3. MNEAI – Likewise, we can inject a piece of JavaScript in M3 Workplace to simulate a user’s data entry, and loop through a data source we get with JavaScript.
  4. H5 Client – We can do the same JavaScript injection for H5 Client.
  5. Macro – We can record the mouse movement and click events, and the keyboard keystrokes, and use a Windows program to replay them. Advantages: It’s the last solution available out of desperation. Disadvantage: it will break at the slightest change in window position or popup, and it will be slow.

Forbidden techniques

Finally, as a reminder, we never use SQL INSERT/UPDATE/DELETE to M3, as that would break the integrity of the ERP, it would bypass the cache of the data abstraction layer, and it would void warranty for support.

That’s it! Thanks for reading. Subscribe below.

H5 Client

H5 Client is a new web-based user interface in Infor Workspace that brings Infor M3 to modern web browsers.

H5 Client is built using standard HTML5/JavaScript, and uses jQuery as one of the JavaScript libraries. It can potentially run on any major browser (Microsoft Internet Explorer, Google Chrome, Apple Safari, Mozilla Firefox, etc.), on potentially any operating system (Microsoft Windows, Mac OS, Linux, etc.), on potentially any device (PC, Mac, iPhone, iPad, Android, etc.). It’s a major milestone since the old Movex Workplace.

Movex Workplace had been built solely for Internet Explorer 6 over 10 years ago with non-standard IE-only features to optimize development and maintenance after Internet Explorer had emerged winner of the first browser war. Despite having been mono-browser, Movex Workplace had been praised by IDC as “the most technologically advanced business portal on the market” extensively using XmlHttpRequest long before the term Ajax was coined.

Fast forward to 2013, the web is growing exponentially with demand for multiple devices, multiple platforms, multiple vendors. H5 Client is the response to that demand and is a new player to complement its big sister Infor Smart Office.

H5 Client was announced and made Generally Available (GA) and I want to advertise it further with this post.

First tests with Google Chrome

  1. To determine if you have H5 Client, open the Grid Information page and look for Application Type mne:
  2. Click on the /mne/ Web Application Link and authenticate with your M3 user/password. It will go to this Home page:
  3. Start an M3 program as usual in the QuickStart, for example CRS610:
  4. The M3 program will start like it did in Movex Workplace, but this time in a modern browser, Google Chrome for instance:
  5. From here, use the program as usual, for example Options > Display CTRL+5:
  6. And Options > Change CTRL+2:

On Safari

Here is a screenshot of H5 Client in Safari on my PC:

On the iPhone

I tested H5 Client on Safari on my iPhone, and here is the result:

Start > CRS610 > Options > Display CTRL+5:
IMG_9107 IMG_9110 IMG_9117

In Landscape View:

Batch Customer Order – OIS275/B1:

Customer Orders – OIS300/B:


Also, it’s possible to use Shortcuts like in Smart Office:


Also, I can use the browser’s Developer Tools and the JavaScript Console to integrate with the H5 Client with Classes similar to Smart Office Scripts (UserContext, Configuration, Controller, Content); that reminds me of MNEAI for Movex Workplace and will probably lead to more posts in the future:


H5 Client is supposed to run as part of Infor Workspace. Thus, H5 Client alone won’t provide all the features necessary for the user.

Also, not all devices and browsers are officially supported. I successfully tested it in Internet Explorer, Google Chrome, and Safari on my PC, and Safari on my iPhone. It failed to run on Firefox on my PC (the entries in the Options menu were disabled), and on Chrome on my iPhone (it froze at /mne/index.jsp). And I yet have to test it on iPad, Linux, Mac OS, and Android.

For more information

To learn more about H5 Client, visit the Summit Week or Inforum 2013.

That’s it! I hope this quick overview will spark an interest in Infor Workspace and H5 Client.


UPDATE 2013-03-15: Added link to HTML5.