Warehouse Mobile Devices Portal (WMDP) comes with a number of predefined work pages, dynamic menus, a logon page, and few other screens. The content and the workflow of the pages that are displayed to the user are fully defined on the AOS tier. The X++ classes that belong to the hierarchy inherited from the WHSWorkExecuteDisplay class contain all of the form logic for WMDP. In particular, they are responsible for creating the structure of the logical form, which contains logical controls. The logical form is packaged into XML that is passed to the WMDP running on IIS, and rendered to the html pages that are displayed to the user. The page rendering on the WMDP tier recognizes the following types of logical controls:
- Text input, rendered as an html “input” element of type “text”
- Password input, rendered as an html “input” element of type “password”
- Label element, rendered as an html “label” element
- Button element, rendered as an html “input” element of type “submit” or “button”
- Combo box, rendered as an html “select” element
- Link control, rendered as an html “a” element
Question: What if I wanted to add a different kind of control, such as an image?
Answer: Well, ideally you should be able to rely on some sort of extensibility model that would allow you to plug in your own logic that is responsible for rendering your custom control. Unfortunately, the WMDP that shipped with Dynamics AX 2012 R3 does not have any extensibility model for rendering page controls.
I have been approached by a number of people who were not exactly satisfied with that answer. In this short blog post I will describe a workaround that will enable you to add another type of html control without having to modify the sealed binaries of WMDP. We will add an image control to the log on screen. I’ll grant that perhaps this example isn’t terribly exciting, but it’s easy to test because it’s the first screen that is displayed when we restart WMDP after making a change.
To add the image control, the first step is to open and edit the X++ method that is responsible for building the logon screen. Use the following path in the AOT to find the method.
\Classes\WHSWorkExecuteDisplayLogin\buildGetUserIdPassword
Let’s add a line of code that creates a label control with a URL to the image that we want to show on the logon screen:
ret += [this.buildControl(#RFLabel, 'IMG_01', 'http://localhost/thumbsup.jpg', 1, '', #WHSRFUndefinedDataType, '', 0)];
The new line of code adds a label control named “IMG_01” with the value of the URL of an image file named thumbsup.jpg. In this case, I want to add this line after the password input control and the logon button. After generating incremental IL and restarting WMDP, the logon screen contains a label with the URL to an image that is available on the localhost.
If we open the logon page in IE and go to Developer Tools (F12), we will see the following element within the html structure:
As we can see, the label control was rendered as html label element with ID=“IMG_01Lbl”. The “Lbl” suffix was added during the construction of the html page on WMDP. It’s important to be able to assign custom identifiers to html elements. If we ensure uniqueness in global naming, we can design a contract between the client and server that requires that every label element prefixed with particular text has a special meaning. In our case, if we assume that the label element identifier is prefixed by “IMG_”, the label text will be treated as the URL of actual image element that should be added to the page. In order to make the magic happen, we need to add a little bit of JavaScript code to the MVC view file, which we are using for rendering our pages. In particular, we can edit Display.aspx (located under <WMDP instance root>\Views\Execute). Let’s add the following JavaScript function.
function init() { var x = document.getElementsByTagName('label'); for (var i = 0; i < x.length; i++) { var control = x.item(i); if (control.id.indexOf('IMG_') == 0) { control.style.display='none'; var picture=document.createElement('img'); picture.setAttribute('src', control.outerText); control.parentNode.appendChild(picture); } } } |
And call it on page load.
<body onload="init();"> |
If we reopen the logon screen now, we will see that the label element is gone in the HTML and the image control is now added:
So, with a very minor change we managed to add an image control to the logon screen. This is fun, but probably not very useful. I believe that this kind of functionality can come in handy when executing actual work, for example, where a user wants to see the image of the item that he or she has been instructed to pick or count in the warehouse.
As for the technical solution, we need to remember that the same MVC view file is used to render logon screen, the menus and all the work pages displayed on the mobile device. With that in mind we need to remember the following:
- Our naming convention needs to be verified in terms of uniqueness, so that we will not end up trying to render pictures using actual message labels
- The JavaScript is present on every page we load. Our little function for rendering images is not that heavy, but if you get carried away by your imagination, you may end up with a performance (and maintainability) problem.
- In this particular case, the URL was originated from a hardcoded string value. You may need to be more careful when dealing with user entered data. Remember to threat model your solutions. WMDP encodes html responses to protect you against XSS attack, but does not perform any validation of the target of the URL that you are passing in the XML.
No comments:
Post a Comment