Mashup quality control #5

Today I will verify if the MForms Bookmarks of the Infor Smart Office Mashups have any leftover hard-coded values – such as company (CONO), division (DIVI), facility (FACI), or warehouse (WHLO) – that developers may have forgotten to un-hard-code; in this fifth episode of Mashup quality control.

MForms Bookmark URI

I am looking for MForms Bookmark URI in Mashups. They look like this:

<mashup:Event SourceEventName="Click" LinkUri="mforms://bookmark/?program=OIS300&amp;tablename=OOHEAD&amp;startpanel=B&amp;includestartpanel=True&amp;requirepanel=True&amp;suppressconfirm=False&amp;source=MForms&amp;sortingorder=1&amp;view=E01&amp;name=Customer+Order.+Open+Toolbox+-+OIS300%2fB&amp;keys=OACONO%2c100%2cOAORNO%2c%2b&amp;fields=WWFACI%2cABC%2cWWDEL%2c0%2cWFORSL%2c05%2cWTORSL%2c99%2cWFORST%2c05%2cWTORST%2c99%2cW1OBKV%2c2" />

In my case, they are in <mashup:Event LinkUri=…> and <mashup:Link Uri=…>

Un-hard-coded values → OK

Un-hard-coded parameters are of the form CONO,{CONO},DIVI,{DIVI} where Smart Office will replace the names in curly braces with their runtime values. That’s the correct form.

Hard-coded values → error ⚠

If a URI has hard-coded values – such as CONO,200,DIVI,AAA – the user may get error messages such as “You must log on to company X before using the bookmark”:

Can you spot the hard-coded values in the example above? There are nine. It’s difficult for me to find them visually. I need a tool.

Bookmark Class

To parse a Bookmark URI, I could use the MForms.Mashup.Bookmark class:

import MForms.Mashup;
var uri: String = "mforms://bookmark/?...";
var bookmark: Bookmark = new Bookmark(new Uri(uri));


The result shows the keys. But the fields and parameters are missing or not accessible from this scope. Instead, I will parse the URI query myself.

Source code

Here is the final source code that will scan all Mashups, all XAML files, all MForms Bookmarks URI, in all keys, fields, and parameters, for hard-coded values; I use the built-in Regex to find the values not in curly braces:

import System;
import System.Collections.Specialized;
import System.IO;
import System.Text;
import System.Text.RegularExpressions;
import System.Web;
import System.Xml;
import Mango.Core.Util;
import Mango.UI.Services.Mashup;
import Mango.UI.Services.Mashup.Internal;

package MForms.JScript {
    class Test {
        public function Init(element: Object, args: Object, controller : Object, debug : Object) {
            var regex: Regex = StringUtil.GetRegex(ParameterBracket.Curly); // {(?<param>[\u0000-\uFFFF-[}]]*)}
            var mashups /*IList<FileInfo>*/ = PackageHelper.GetSharedMashupList();
            for (var mashup: FileInfo in mashups) {
                var baseUri: Uri = UriHelper.CreateBaseUri(new Uri(mashup.Name, UriKind.RelativeOrAbsolute));
                var manifest: Manifest = PackageHelper.GetManifest(mashup);
                var list /*IList<FileInformation>*/ = manifest.CreateFileInformationList();
                for (var information: FileInformation in list) {
                    if (information.MimeType == Defines.MimeTypeXAML) {
                        var relativeUri: String = information.Path;
                        var stream: Stream = PackageHelper.GetStream(baseUri, new Uri(relativeUri, UriKind.Relative));
                        var document: XmlDocument = new XmlDocument();
                        var nodes: XmlNodeList = document.SelectNodes(" //@*[name()='Uri' or name()='LinkUri'] ");
                        for (var attribute: XmlAttribute in nodes) {
                            if (!attribute.Value.StartsWith("{Binding") && !attribute.Value.EndsWith(".xaml")) {
                                try {
                                    var uri: Uri = new Uri(attribute.Value);
                                    if (uri.Scheme == "mforms" && uri.Host == "bookmark") {
                                        var collection: NameValueCollection = HttpUtility.ParseQueryString(new Uri(uri).Query);
                                        for (var name: String in collection) {
                                            if ("keys,fields,parameters".Contains(name, StringComparison.InvariantCultureIgnoreCase)) {
                                                var pairs: String[] = collection[name].Split(",");
                                                for (var j: int = 0; j < pairs.Length; j = j + 2) {
                                                    var key: String = pairs[j];
                                                    var value: String = HttpUtility.UrlDecode(pairs[j + 1], Encoding.UTF8).Trim();
                                                    if (!String.IsNullOrEmpty(value)) {
                                                        if (!regex.IsMatch(value)) {
                                                            debug.WriteLine([mashup.Name, relativeUri, attribute.OwnerElement.Name, attribute.Name, key, value]);
                                } catch (ex: UriFormatException) {
                                    debug.WriteLine([ex, attribute.Value]); 

I made it based on:


Note 1: I originally had the XPath expression searching too widely as //@*[starts-with(., ‘mforms://’)] but that unnecessarily caught MForms Automation URIs like mforms://_automation?data= and URIs that start M3 programs like mforms://CRS610 . Then I made the XPath expression too precise as //@*[starts-with(., ‘mforms://bookmark/?’)] but that failed to catch several valid URIs: those that start with a white space, those that have backslashes such as mforms:\\ , those that are mixed case such as MFORMS:// , and those that do not have the slash in the query such as mforms://bookmark? , all of which are valid. I could have used the XPath function normalize-space() for trimming, but then XPath 1.0 does not have the lower-case() function, only XPath 2.0 does. So I eventually decided to change the XPath expression to look for attributes Uri and LinkUri, and search for scheme mforms and host bookmark in script once the URI is parsed.

Note 2: This code assumes the MForms Bookmark URIs are in either an attribute named Uri or LinkUri, with attribute value not ending in .xaml .


The result is the following: a list of Mashups and XAML files, with MForms Bookmark URI that contain hard-coded values:

The most worrisome hard-coded values are: company (CONO), division (DIVI), and warehouse (WHYA); I will review those first. The hard-coded facility (FACI) are probably OK as they define a range from/to. There are also hard-coded dates (FVDT/LVDT), filters and positioners; I will review those next. The hard-coded inquiry type (QTTP) is probably OK.

The metrics in this case are:

  • 13 Mashups
  • 178 XAML files
  • 339 URIs
  • 61 of them are MForms Bookmark
  • 774 name/values pairs in the URIs
  • 284 of them are keys, fields, and parameters
  • 43 of them have hard-coded values (15% of above)
  • 10 of them to review (23% of above)

In other words, I was able to quickly scan about 800 values and identify 10 hard-coded-values to review. I would not have been able to pinpoint it as fast and as accurately manually.

Future work

Also, I want to:

That’s it!

Please comment, like, subscribe, share, author. Thanks for your support.

Related posts

Published by


ex- M3 Technical Consultant

15 thoughts on “Mashup quality control #5”

  1. UPDATE: I corrected the XPath expression to search for attribute names Uri and LinkUri instead of searching for attribute value mforms://bookmark/? ; see the detailed notes of the source code. I also corrected the results screenshot and metrics.


    1. Hi kiran. What exactly are you trying to do? I don’t know what you mean by Mashup and JScript SDK. Do you mean the Smart Office SDK? It’s in the Infor Xtreme Product Downloads. Or do you mean something else? I think there is a Web Mashup SDK for H5 but I’m not sure. As for JScript SDK, I haven’t heard of it. If you mean the Smart Office DLLs, they are in the local app folder in your computer as part of ClickOnce deployment, also on the Grid and LCM server folders.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s