Lawson Global and Role Defaults

A long-requested feature, the ability to set global default values for Infor Lawson applications within Smart Office, was recently made available with HF 47, version 10.2.1.0.408. As an added feature, default values can also be set at the Role level, which would take precedence over global values.

Users with Portal admin access can define global values by launching the ‘Lawson Global Values’ link in the Navigator widget under Infor Lawson => Lawson Administration. Role level defaults are set in the Role Manager, accessed from a new ‘Defaults’ button on the General tab.

GlobalDefaultsLawson Global Defaults window

Default values depend on associating values with Lawson key numbers, which are in fact 2 or 3-character alphanumeric values. Key numbers are associated with form fields, with the most common key number being “01” which is generally associated with company fields. If a field has a key number, it can be seen in the field info popup (Ctrl+Alt+O) as ‘Knb’.

Due to the large number of applications developed over many years, key numbers are not guaranteed to be unique across all screens and special handling may be defined. While testing new defaults, it may be necessary to list one or more tokens to be excluded or included when evaluating a form’s default values. If no tokens are listed, the value will be applied where ever the key number is encountered.

When defining defaults, a few of the more common key numbers are listed and can be selected from a popup menu by clicking the ‘…’ button to the right of the key number field.

KeyValueNumber
Key number value displayed in field info popup

CommonKeyNumber
Common Key number popup

One additional consideration when defining defaults is whether to allow role overrides. For example, if your organization has setup different roles to handle different groups of customers or vendors, this may be desirable. However, if a override is not allowed for a key number, you will see the message below when entering it in Role maintenance.

NotAllowed
An attempt to override a key number not allowed

 

Thanks Vince for this information.

ClickOnce Uninstall Script

There’s a previous post Sound of a Silent Install how to perform a silent install in virtual desktop environments using the Windows installer (MSI) version.

The conventional way to install Infor Smart Office on client machines is to use Microsoft ClickOnce network deployment to get an isolated, secure, self-updating application that can be installed and run with minimal user interaction.

To Uninstall a ClickOnce application require the launch of the uninstall dialog and some user interaction. To perform uninstall on many client machines can become tedious so here is a Powershell script example that performs the task.

The script use the application display name from Windows Start menu to find the ClickOnce uninstall string in the Windows Registry. It launches the ClickOnce uninstall dialog, selects the “Remove the application from this computer” option and press the OK button. This example has been tested on Windows v10.0.17763 and there’s no guarantee it will work on every Windows version.

<# Set $ISODisplayName to the visible name of the application in the Windows Start menu #>
$ISODisplayName = "Infor Smart Office - DEV"

<# Get uninstallable ClickOnce applications #>
$InstalledApplicationNotMSI = Get-ChildItem HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall | foreach-object {Get-ItemProperty $_.PsPath}

<# Find uninstall string for named ClickOnce application #>
<# The name is the display name visible in the Windows Start Menu #>
$UninstallString = $InstalledApplicationNotMSI | ? { $_.displayname -match $ISODisplayName } | select UninstallString 
$selectedUninstallString = $UninstallString.UninstallString

<# Launch ClickOnce uninstall command #>
$wshell.run("cmd /c $selectedUninstallString")

<# Wait 7 seconds for uninstall dialog to appear #>
Start-Sleep 7

<# Move focus and Select option "Remove the Application from this computer" #>
<# Key specifications: https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.sendkeys?view=netframework-4.7.2 #>
$wshell.sendkeys("{TAB}")
$wshell.sendkeys("{DOWN}")

<# Press the OK button #>
$wshell.sendkeys("`"OK`"~")

 

 

 

 

Microsoft Edge Chromium and ClickOnce

The new Microsoft Edge Chromium browser doesn’t provide native support for ClickOnce as of version 81.0.416.6.

It will by default NOT work to launch a Smart Office ClickOnce install point link in Edge Chromium.

To enable ClickOnce support in Edge Chromium:

  1. Enter edge://flags link in Edge Chromium browser.
  2. Scroll down to ClickOnce Support setting and select ‘Enable’ from the dropdown list.
  3. Restart the browser.
Note: This setting will be overridden if your organization configures the 
"Allow users to open files using the ClickOnce protocol" policy. – Windows

The Edge Chromium will always prompt the user before launch of a 
ClickOnce link because Chromium doesn't rely on the Windows Security Zones.

ScriptUtil.LoadAssemblyFromUrl() is Obsolete

The ScriptUtil.LoadAssemblyFromUrl method was marked obsolete in v10.2.1.0 HF32. A workaround for M3 scripts using the method is to replace with the following code:

var assembly = Assembly.Load(WebReader.GetRequestBinary(url));

In between version 10.2.1.0.333 (HF32) and 10.2.1.0.385 (HF42) the obsolete method will
fail in silence and just return null.

This has been changed in 10.2.1.0.389 (HF43) and an InvalidOperationException will be thrown when calling the method.

JSON support in REST data service – Part III

Create, Update or Delete a User

In the third post we continue to use the fake online REST service to add operations to create, update and delete a user.

Mashup_REST_JSON_DELPATCHPUT

We add three DataOperation declarations to create, update and delete a user and set the OutputType to be JSON to easily show the request response in a textbox as a text string. The online service will return a correct response but no changes will actually be done in the service database, this is just for testing purposes.

<mashup:DataOperation Name="CreateUserPost">
    <mashup:DataParameter Key="REST.BaseAddress" Value="https://jsonplaceholder.typicode.com/users" />
    <mashup:DataParameter Key="REST.InputJsonTemplate" Value="{StaticResource PostTemplate}" />
    <mashup:DataParameter Key="REST.AcceptHeader" Value="application/json" />
    <mashup:DataParameter Key="REST.HttpMethod" Value="POST" />
    <mashup:DataParameter Key="REST.InputType" Value="json" />
    <mashup:DataParameter Key="REST.OutputType" Value="application/json" />
</mashup:DataOperation>
<mashup:DataOperation Name="UpdateUserPost">
    <mashup:DataParameter Key="REST.BaseAddress" Value="https://jsonplaceholder.typicode.com/users/{id}" />    
    <mashup:DataParameter Key="REST.AcceptHeader" Value="application/json" />
    <mashup:DataParameter Key="REST.HttpMethod" Value="PUT" />
    <mashup:DataParameter Key="REST.InputType" Value="json" />
    <mashup:DataParameter Key="REST.OutputType" Value="application/json" />
</mashup:DataOperation>
<mashup:DataOperation Name="DeleteUser">
    <mashup:DataParameter Key="REST.BaseAddress" Value="https://jsonplaceholder.typicode.com/posts/{id}" />
    <mashup:DataParameter Key="REST.AcceptHeader" Value="application/json" />
    <mashup:DataParameter Key="REST.HttpMethod" Value="DELETE" />
    <mashup:DataParameter Key="REST.OutputType" Value="application/json" />
</mashup:DataOperation>

We also add a button for each operation with a trigger event. All the property values for the current item the DataPanel will be added in the request body, so just declare the Parameter bindings for values that may change.

<Button DockPanel.Dock="Right" HorizontalAlignment="Right" Margin="5" Content="Update User">
   <Button.CommandParameter>
      <mashup:Events>
         <mashup:Event TargetName="UserDetails" SourceEventName="Click" TargetEventName="UpdateUserPost">
         <mashup:Parameter TargetKey="email" Value="{Binding ElementName=CEmail, Path=Text}" Type="String" />
         <mashup:Parameter TargetKey="website" Value="{Binding ElementName=CWebsite, Path=Text}" Type="String" />
         <mashup:Parameter TargetKey="phone" Value="{Binding ElementName=CPhone, Path=Text}" Type="String" />
         </mashup:Event>
      </mashup:Events>
   </Button.CommandParameter>
</Button>

 

Here is the Mashup code if you would like to try it out.

<Grid Margin="15,10,15,15" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ui="clr-namespace:Mango.UI.Controls;assembly=Mango.UI" xmlns:mashup="clr-namespace:Mango.UI.Services.Mashup;assembly=Mango.UI" xmlns:m3="clr-namespace:MForms.Mashup;assembly=MForms" xmlns:sys="clr-namespace:System;assembly=mscorlib">
   <Grid.RowDefinitions>
      <RowDefinition Height="1*" />
      <RowDefinition Height="Auto" />
      <RowDefinition Height="1*" />
   </Grid.RowDefinitions>
   <Grid.ColumnDefinitions>
      <ColumnDefinition Width="1*" />
      <ColumnDefinition Width="10" />
      <ColumnDefinition Width="1*" />
   </Grid.ColumnDefinitions>
   <Grid.Resources>
      <x:String x:Key="PostTemplate"><![CDATA[      
               { 
                 "name": "{name}",
                 "username": "{username}",
                 "email": "{email}",
                 "address": {
                    "street": "{street}",
                    "suite": "{suite}",
                    "city": "{city}",
                    "zipcode": "{zipcode}",
                    "geo": {
                       "lat": "0.0",
                       "lng": "0.0"
                    }
                 },
                 "phone": "{phone}",
                 "website": "{website}",
                 "company": {
                    "name": "{companyName}",
                    "catchPhrase": "{companyCatchPhrase}",
                    "bs": "{companyBs}"
                 }                 
               }
            ]]></x:String>
   </Grid.Resources>
   <mashup:DataListPanel DockPanel.Dock="Top" Name="UsersList" Grid.Column="0" Grid.Row="0" Grid.RowSpan="3">
      <mashup:DataListPanel.DataService>
         <mashup:DataService Type="REST">
            <mashup:DataService.Operations>
               <mashup:DataOperation Name="List">
                  <mashup:DataParameter Key="REST.BaseAddress" Value="https://jsonplaceholder.typicode.com/users/" />
                  <mashup:DataParameter Key="REST.OutputType" Value="DataItem" />
                  <mashup:DataParameter Key="REST.AcceptHeader" Value="application/json" />
               </mashup:DataOperation>
            </mashup:DataService.Operations>
         </mashup:DataService>
      </mashup:DataListPanel.DataService>
      <mashup:DataListPanel.Events>
         <mashup:Events>
            <mashup:Event SourceEventName="Startup" TargetEventName="List" />
            <mashup:Event SourceName="UsersList" TargetName="UserDetails" SourceEventName="CurrentItemChanged" TargetEventName="Get">
               <mashup:Parameter SourceKey="id" TargetKey="userId" Type="Numeric" />
            </mashup:Event>
         </mashup:Events>
      </mashup:DataListPanel.Events>
      <DockPanel LastChildFill="False">
         <ListView DockPanel.Dock="Top" Name="ListViewCompany" ItemsSource="{Binding Items}" Style="{DynamicResource styleListView}" ItemContainerStyle="{DynamicResource styleListViewItem}">
            <ListView.View>
               <GridView ColumnHeaderContainerStyle="{DynamicResource styleGridViewColumnHeader}">
                  <GridView.Columns>
                     <GridViewColumn Header="Company" DisplayMemberBinding="{Binding [company].[name]}" />
                     <GridViewColumn Header="City" DisplayMemberBinding="{Binding [address].[city]}" />
                     <GridViewColumn Header="Name" DisplayMemberBinding="{Binding [name]}" />
                     <GridViewColumn Header="Email" DisplayMemberBinding="{Binding [email]}" />
                     <GridViewColumn Header="Longitude" DisplayMemberBinding="{Binding [address].[geo].[lng]}" />
                     <GridViewColumn Header="Longitude" DisplayMemberBinding="{Binding [address].[geo].[lng]}" />
                  </GridView.Columns>
               </GridView>
            </ListView.View>
         </ListView>
      </DockPanel>
   </mashup:DataListPanel>
   <mashup:DataPanel Grid.Row="0" Grid.Column="2" Grid.RowSpan="1" Name="UserDetails">
      <mashup:DataPanel.DataService>
         <mashup:DataService Type="REST">
            <mashup:DataService.Operations>
               <mashup:DataOperation Name="Get">
                  <mashup:DataParameter Key="REST.BaseAddress" Value="https://jsonplaceholder.typicode.com/users/{userId}" />
                  <mashup:DataParameter Key="REST.OutputType" Value="DataItem" />
                  <mashup:DataParameter Key="REST.AcceptHeader" Value="application/json" />
               </mashup:DataOperation>
               <mashup:DataOperation Name="CreateUserPost">
                  <mashup:DataParameter Key="REST.BaseAddress" Value="https://jsonplaceholder.typicode.com/users" />
                  <mashup:DataParameter Key="REST.InputJsonTemplate" Value="{StaticResource PostTemplate}" />
                  <mashup:DataParameter Key="REST.AcceptHeader" Value="application/json" />
                  <mashup:DataParameter Key="REST.HttpMethod" Value="POST" />
                  <mashup:DataParameter Key="REST.InputType" Value="json" />
                  <mashup:DataParameter Key="REST.OutputType" Value="application/json" />
               </mashup:DataOperation>
               <mashup:DataOperation Name="UpdateUserPost">
                  <mashup:DataParameter Key="REST.BaseAddress" Value="https://jsonplaceholder.typicode.com/users/{id}" />
                  <mashup:DataParameter Key="REST.AcceptHeader" Value="application/json" />
                  <mashup:DataParameter Key="REST.HttpMethod" Value="PUT" />
                  <mashup:DataParameter Key="REST.InputType" Value="json" />
                  <mashup:DataParameter Key="REST.OutputType" Value="application/json" />
               </mashup:DataOperation>
               <mashup:DataOperation Name="DeleteUser">
                  <mashup:DataParameter Key="REST.BaseAddress" Value="https://jsonplaceholder.typicode.com/posts/{id}" />
                  <mashup:DataParameter Key="REST.AcceptHeader" Value="application/json" />
                  <mashup:DataParameter Key="REST.HttpMethod" Value="DELETE" />
                  <mashup:DataParameter Key="REST.OutputType" Value="application/json" />
               </mashup:DataOperation>
            </mashup:DataService.Operations>
         </mashup:DataService>
      </mashup:DataPanel.DataService>
      <mashup:DataPanel.Events>
         <mashup:Events></mashup:Events>
      </mashup:DataPanel.Events>
      <Grid Margin="10,0,10,0">
         <Grid.RowDefinitions>
            <RowDefinition Height="50" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="0" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="0" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="0" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="0" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="24" />
            <RowDefinition Height="Auto" />
         </Grid.RowDefinitions>
         <DockPanel Grid.Row="1" LastChildFill="False">
            <TextBlock DockPanel.Dock="Left" Text="Company" Width="55" Margin="5" VerticalAlignment="Center" />
            <TextBox DockPanel.Dock="Left" Name="CCompany" Width="220" Margin="5" Text="{Binding [company].[name]}" />
         </DockPanel>
         <DockPanel Grid.Row="3">
            <TextBlock DockPanel.Dock="Left" Text="Name" Width="55" Margin="5" VerticalAlignment="Center" />
            <TextBox DockPanel.Dock="Left" Name="CName" Width="220" Margin="5" Text="{Binding [name]}" />
            <TextBlock DockPanel.Dock="Left" Text="UserName" Width="60" Margin="0,0,10,0" VerticalAlignment="Center" />
            <TextBox DockPanel.Dock="Left" Name="CUserName" Margin="5" Text="{Binding [username]}" />
         </DockPanel>
         <DockPanel Grid.Row="5">
            <TextBlock DockPanel.Dock="Left" Text="Email" Width="55" Margin="5" VerticalAlignment="Center" />
            <TextBox DockPanel.Dock="Left" Name="CEmail" Width="220" Margin="5" Text="{Binding [email]}" />
            <TextBlock DockPanel.Dock="Left" Text="Website" Width="60" Margin="0,0,10,0" VerticalAlignment="Center" />
            <TextBox DockPanel.Dock="Left" Name="CWebsite" Margin="5" Text="{Binding [website]}" />
         </DockPanel>
         <DockPanel Grid.Row="7" LastChildFill="False">
            <TextBlock DockPanel.Dock="Left" Text="Phone" Width="55" Margin="5" VerticalAlignment="Center" />
            <TextBox DockPanel.Dock="Left" Name="CPhone" Width="220" Margin="5" Text="{Binding [phone]}" />
         </DockPanel>
         <DockPanel Grid.Row="9">
            <TextBlock DockPanel.Dock="Left" Text="Catch" Width="55" Margin="5" VerticalAlignment="Center" />
            <TextBox DockPanel.Dock="Left" Name="CCatch" Width="220" Margin="5" Text="{Binding [company].[catchPhrase]}" />
            <TextBlock DockPanel.Dock="Left" Text="BS" Width="60" Margin="5" VerticalAlignment="Center" />
            <TextBox DockPanel.Dock="Left" Name="CBS" Margin="5" Text="{Binding [company].[bs]}" />
         </DockPanel>
         <DockPanel Grid.Row="11">
            <Button DockPanel.Dock="Right" HorizontalAlignment="Right" Margin="5" Content="Add User">
               <Button.CommandParameter>
                  <mashup:Events>
                     <mashup:Event TargetName="UserDetails" SourceEventName="Click" TargetEventName="CreateUserPost">
                        <mashup:Parameter TargetKey="name" Value="{Binding ElementName=CName, Path=Text}" Type="String" />
                        <mashup:Parameter TargetKey="companyName" Value="{Binding ElementName=CCompany, Path=Text}" Type="String" />
                        <mashup:Parameter TargetKey="userName" Value="{Binding ElementName=CUserName, Path=Text}" Type="String" />
                        <mashup:Parameter TargetKey="email" Value="{Binding ElementName=CEmail, Path=Text}" Type="String" />
                        <mashup:Parameter TargetKey="website" Value="{Binding ElementName=CWebsite, Path=Text}" Type="String" />
                        <mashup:Parameter TargetKey="phone" Value="{Binding ElementName=CPhone, Path=Text}" Type="String" />
                        <mashup:Parameter TargetKey="companyCatchPhrase" Value="{Binding ElementName=CCatch, Path=Text}" Type="String" />
                        <mashup:Parameter TargetKey="companyBs" Value="{Bindng ElementName=CBS, Path=Text}" Type="String" />
                     </mashup:Event>
                  </mashup:Events>
               </Button.CommandParameter>
            </Button>
            <Button DockPanel.Dock="Right" HorizontalAlignment="Right" Margin="5" Content="Update User">
               <Button.CommandParameter>
                  <mashup:Events>
                     <mashup:Event TargetName="UserDetails" SourceEventName="Click" TargetEventName="UpdateUserPost">
                        <mashup:Parameter TargetKey="email" Value="{Binding ElementName=CEmail, Path=Text}" Type="String" />
                        <mashup:Parameter TargetKey="website" Value="{Binding ElementName=CWebsite, Path=Text}" Type="String" />
                        <mashup:Parameter TargetKey="phone" Value="{Binding ElementName=CPhone, Path=Text}" Type="String" />
                     </mashup:Event>
                  </mashup:Events>
               </Button.CommandParameter>
            </Button>
            <Button DockPanel.Dock="Top" HorizontalAlignment="Right" Margin="5" Content="Delete User">
               <Button.CommandParameter>
                  <mashup:Events>
                     <mashup:Event TargetName="UserDetails" SourceEventName="Click" TargetEventName="DeleteUser">
                        <mashup:Parameter TargetKey="id" Value="{Binding ElementName=UsersList, Path=CurrentItem[id]}" />
                     </mashup:Event>
                  </mashup:Events>
               </Button.CommandParameter>
            </Button>
         </DockPanel>
      </Grid>
   </mashup:DataPanel>
   <DockPanel Grid.Row="1" Grid.Column="2" Grid.RowSpan="2" VerticalAlignment="Stretch">
      <TextBlock DockPanel.Dock="Top" Text="Server Response:" Width="140" Margin="5" VerticalAlignment="Top" HorizontalAlignment="Left" />
      <TextBox Name="ServerResponse" DockPanel.Dock="Top" Text="{Binding ElementName=UserDetails, Path=DataContext[json]}" Margin="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" VerticalContentAlignment="Top" TextWrapping="Wrap" AcceptsReturn="True" />
   </DockPanel>
</Grid>

 

JSON support in REST data service – Part II

Post the User Post with a JSON Template

In the previous part we used the REST data service to fetch a set of users in JSON format and present them in a list view and as a JSON text string. In this example we call the same service to get related user information and send a request to add new information. The fake online service we use returns a correct JSON response but will not create anything in the database of the service. Smart Office 10.2.1.0.367 HF38 is required.

 

Mashup_REST_JSON_POST

First we define another data operation to create a user post by sending a JSON request,

<mashup:DataOperation Name="CreateUserPost">
    <mashup:DataParameter Key="REST.BaseAddress" Value="https://jsonplaceholder.typicode.com/posts" />
    <mashup:DataParameter Key="REST.InputJsonTemplate" Value="{StaticResource PostTemplate}" />
    <mashup:DataParameter Key="REST.AcceptHeader" Value="application/json" />
    <mashup:DataParameter Key="REST.HttpMethod" Value="POST" />
    <mashup:DataParameter Key="REST.InputType" Value="json" />
    <mashup:DataParameter Key="REST.OutputType" Value="application/json" />
</mashup:DataOperation>

and we also add a Send button containing an event that collects required values and trigger the CreateUserPost operation.

<Button DockPanel.Dock="Top" HorizontalAlignment="Right" Margin="5" Content="Send">
   <Button.CommandParameter>
            <mashup:Events>
                 <mashup:Event TargetName="PostComment" SourceEventName="Click" TargetEventName="CreateUserPost">
                 <mashup:Parameter TargetKey="title" Value="{Binding ElementName=PostTitle, Path=Text}" />
                 <mashup:Parameter TargetKey="body" Value="{Binding ElementName=PostBody, Path=Text}" />
                 <mashup:Parameter TargetKey="userId" Value="{Binding ElementName=UsersList, Path=CurrentItem[id]}" />
             </mashup:Event>
         </mashup:Events>
    </Button.CommandParameter>
</Button>

A press of the Send button collects the parameters and creates a JSON parameter body to be part of the server request. The body will look like this.

{
   "title": "test",
   "body": "test",
   "userId": "2",
}

Notice that the userId is a string but for the service we call, assume it must be a numeric value. Imagine that we need to customize the body for the service request since the default one will not do the trick.

Let’s create a template for the body that will result in the following JSON.

{
   "title": "test",
   "body": "test",
   "userId": 2,
}

First we add the template as a resource in the Mashup code within a CDATA block.

<Grid.Resources>   
      <x:String x:Key="PostTemplate"><![CDATA[      
               { 
                 "title": "{title}",
                 "body": "{body}",
                 "userId": {userId}
               }
            ]]></x:String>
   </Grid.Resources>

Next step is to add a template reference in the data operation declaration using the InputJsonTemplate parameter .

<mashup:DataOperation Name="CreateUserPost">
    <mashup:DataParameter Key="REST.BaseAddress" Value="https://jsonplaceholder.typicode.com/posts" />
    <mashup:DataParameter Key="REST.InputJsonTemplate" Value="{StaticResource PostTemplate}" />

Done! This is all that is needed to customize the server request body instead of getting the default one generated.

The full Mashup code if you would like to try it out.

<Grid Margin="15,10,15,15" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ui="clr-namespace:Mango.UI.Controls;assembly=Mango.UI" xmlns:mashup="clr-namespace:Mango.UI.Services.Mashup;assembly=Mango.UI" xmlns:m3="clr-namespace:MForms.Mashup;assembly=MForms" xmlns:sys="clr-namespace:System;assembly=mscorlib">
	<Grid.RowDefinitions>
		<RowDefinition Height="1*" />
		<RowDefinition Height="10" />
		<RowDefinition Height="1*" />
		<RowDefinition Height="Auto" />
	</Grid.RowDefinitions>
	<Grid.ColumnDefinitions>
		<ColumnDefinition Width="1*" />
		<ColumnDefinition Width="10" />
		<ColumnDefinition Width="1*" />
	</Grid.ColumnDefinitions>
	<Grid.Resources>
		<x:String x:Key="PostTemplate"><![CDATA[		
					{ 
					  "title": "{title}",
					  "body": "{body}",
					  "userId": {userId}
					}
				]]></x:String>
	</Grid.Resources>
	<mashup:DataListPanel Name="UsersList" Grid.Column="0" Grid.Row="0" Grid.RowSpan="3">
		<mashup:DataListPanel.DataService>
			<mashup:DataService Type="REST">
				<mashup:DataService.Operations>
					<mashup:DataOperation Name="List">
						<mashup:DataParameter Key="REST.BaseAddress" Value="https://jsonplaceholder.typicode.com/users/" />
						<mashup:DataParameter Key="REST.OutputType" Value="DataItem" />
						<mashup:DataParameter Key="REST.AcceptHeader" Value="application/json" />
					</mashup:DataOperation>
				</mashup:DataService.Operations>
			</mashup:DataService>
		</mashup:DataListPanel.DataService>
		<mashup:DataListPanel.Events>
			<mashup:Events>
				<mashup:Event SourceEventName="Startup" TargetEventName="List" />
			</mashup:Events>
		</mashup:DataListPanel.Events>
		<ListView Name="ListViewCompany" ItemsSource="{Binding Items}" Style="{DynamicResource styleListView}" ItemContainerStyle="{DynamicResource styleListViewItem}">
			<ListView.View>
				<GridView ColumnHeaderContainerStyle="{DynamicResource styleGridViewColumnHeader}">
					<GridView.Columns>
						<GridViewColumn Header="Company" DisplayMemberBinding="{Binding [company].[name]}" />
						<GridViewColumn Header="City" DisplayMemberBinding="{Binding [address].[city]}" />
						<GridViewColumn Header="Name" DisplayMemberBinding="{Binding [name]}" />
						<GridViewColumn Header="Email" DisplayMemberBinding="{Binding [email]}" />
						<GridViewColumn Header="Longitude" DisplayMemberBinding="{Binding [address].[geo].[lng]}" />
						<GridViewColumn Header="Longitude" DisplayMemberBinding="{Binding [address].[geo].[lng]}" />
					</GridView.Columns>
				</GridView>
			</ListView.View>
		</ListView>
	</mashup:DataListPanel>
	<mashup:DataPanel Grid.Row="0" Grid.Column="2" Grid.RowSpan="3" Name="PostComment">
		<mashup:DataPanel.DataService>
			<mashup:DataService Type="REST">
				<mashup:DataService.Operations>
					<mashup:DataOperation Name="ListUserPost">
						<mashup:DataParameter Key="REST.BaseAddress" Value="https://jsonplaceholder.typicode.com/posts/?userid={userId}" />
						<mashup:DataParameter Key="REST.AcceptHeader" Value="application/json" />
					</mashup:DataOperation>
					<mashup:DataOperation Name="CreateUserPost">
						<mashup:DataParameter Key="REST.BaseAddress" Value="https://jsonplaceholder.typicode.com/posts" />
						<mashup:DataParameter Key="REST.InputJsonTemplate" Value="{StaticResource PostTemplate}" />
						<mashup:DataParameter Key="REST.AcceptHeader" Value="application/json" />
						<mashup:DataParameter Key="REST.HttpMethod" Value="POST" />
						<mashup:DataParameter Key="REST.InputType" Value="json" />
						<mashup:DataParameter Key="REST.OutputType" Value="application/json" />
						<!-- Change to DataItem to use return data -->
					</mashup:DataOperation>
				</mashup:DataService.Operations>
			</mashup:DataService>
		</mashup:DataPanel.DataService>
		<mashup:DataPanel.Events>
			<mashup:Events>
				<mashup:Event SourceEventName="Startup" TargetEventName="List" />				
			</mashup:Events>
		</mashup:DataPanel.Events>
		<Grid Margin="10,0,10,0">
			<Grid.RowDefinitions>
				<RowDefinition Height="Auto" />
				<RowDefinition Height="10" />
				<RowDefinition Height="Auto" />
				<RowDefinition Height="10" />
				<RowDefinition Height="Auto" />
			</Grid.RowDefinitions>
			<DockPanel Grid.Row="0">
				<TextBlock DockPanel.Dock="Left" Text="Post a comment as" Margin="0,0,5,0" />
				<TextBlock DockPanel.Dock="Left" Text="{Binding ElementName=UsersList, Path=CurrentItem[name]}" />
			</DockPanel>
			<DockPanel Grid.Row="2">
				<TextBlock DockPanel.Dock="Top" Text="Title" Margin="5" />
				<TextBox DockPanel.Dock="Top" Name="PostTitle" Margin="5" />
				<TextBlock DockPanel.Dock="Top" Text="Content" Margin="5" />
				<TextBox DockPanel.Dock="Top" Name="PostBody" Margin="5" />
				<Button DockPanel.Dock="Top" HorizontalAlignment="Right" Margin="5" Content="Send">
					<Button.CommandParameter>
						<mashup:Events>
							<mashup:Event TargetName="PostComment" SourceEventName="Click" TargetEventName="CreateUserPost">
								<mashup:Parameter TargetKey="title" Value="{Binding ElementName=PostTitle, Path=Text}" />
								<mashup:Parameter TargetKey="body" Value="{Binding ElementName=PostBody, Path=Text}" />
								<mashup:Parameter TargetKey="userId" Value="{Binding ElementName=UsersList, Path=CurrentItem[id]}" />
							</mashup:Event>
						</mashup:Events>
					</Button.CommandParameter>
				</Button>
			</DockPanel>
			<DockPanel Grid.Row="4">
				<TextBlock DockPanel.Dock="Top" Text="Server Response:" Width="140" Margin="5" VerticalAlignment="Top" HorizontalAlignment="Left" />
				<TextBox Height="120" DockPanel.Dock="Top" Text="{Binding [json]}" Margin="5" HorizontalAlignment="Stretch" VerticalAlignment="Top" VerticalContentAlignment="Top" TextWrapping="Wrap" AcceptsReturn="True" />
			</DockPanel>
		</Grid>
	</mashup:DataPanel>
</Grid>

 

JSON support in REST data service – Part I

In Smart Office 10.2.1.0.367 HF38 there is support for JSON when calling a REST data service from a Mashup. In this blog post I’m going to use a fake online REST service to demonstrate how it’s done.

The response data from the REST service call can be converted into a DataItem hierarchy or as a JSON string. This example will fetch a listing of users from the service and present them in a Mashup ListView and in a TextBox as a JSON string, side-by-side.

Mashup_REST_JSON_LIST

The Mashup XAML syntax will be the same as before when doing a REST service call. All you need to do now is set the REST.AcceptHeader parameter to application/json.
The REST.OutputType determines if the data response is available to the Mashup as an object hierarchy or as a JSON string. Use the value ‘DataItem’ to get DataItem hierarchy or set ‘application/json’ for a JSON string. If REST.OutputType is not set the default will be a DataItem hierarchy.

<mashup:DataListPanel.DataService>
    <mashup:DataService Type="REST">
        <mashup:DataService.Operations>
            <mashup:DataOperation Name="List">
                <mashup:DataParameter Key="REST.BaseAddress" Value="https://jsonplaceholder.typicode.com/users/" />
                <mashup:DataParameter Key="REST.OutputType" Value="DataItem" />
                <mashup:DataParameter Key="REST.AcceptHeader" Value="application/json" />
            </mashup:DataOperation>
        </mashup:DataService.Operations>
    </mashup:DataService>
</mashup:DataListPanel.DataService>

To set the first column in the ListView to the company name use the binding below.

DisplayMemberBinding="{Binding [company].[name]}"

To access the JSON string when that is set as output type simply bind to ‘json’.

Text="{Binding [json]}"

Here is the complete Mashup code if you would like to try it out.

<Grid Margin="15,10,15,15" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ui="clr-namespace:Mango.UI.Controls;assembly=Mango.UI" xmlns:mashup="clr-namespace:Mango.UI.Services.Mashup;assembly=Mango.UI" xmlns:m3="clr-namespace:MForms.Mashup;assembly=MForms" xmlns:sys="clr-namespace:System;assembly=mscorlib">
   <Grid.RowDefinitions>
      <RowDefinition Height="1*" />
   </Grid.RowDefinitions>
   <Grid.ColumnDefinitions>
      <ColumnDefinition Width="1*" />
      <ColumnDefinition Width="10" />
      <ColumnDefinition Width="1*" />
   </Grid.ColumnDefinitions>
   <mashup:DataListPanel DockPanel.Dock="Top" Name="UsersList" Grid.Column="0" Grid.Row="0" Grid.RowSpan="3">
      <mashup:DataListPanel.DataService>
         <mashup:DataService Type="REST">
            <mashup:DataService.Operations>
               <mashup:DataOperation Name="List">
                  <mashup:DataParameter Key="REST.BaseAddress" Value="https://jsonplaceholder.typicode.com/users/" />
                  <mashup:DataParameter Key="REST.OutputType" Value="DataItem" />
                  <mashup:DataParameter Key="REST.AcceptHeader" Value="application/json" />
               </mashup:DataOperation>
            </mashup:DataService.Operations>
         </mashup:DataService>
      </mashup:DataListPanel.DataService>
      <mashup:DataListPanel.Events>
         <mashup:Events>
            <mashup:Event SourceEventName="Startup" TargetEventName="List" />
         </mashup:Events>
      </mashup:DataListPanel.Events>
      <DockPanel LastChildFill="False">
         <ListView DockPanel.Dock="Top" Name="ListViewCompany" ItemsSource="{Binding Items}" Style="{DynamicResource styleListView}" ItemContainerStyle="{DynamicResource styleListViewItem}">
            <ListView.View>
               <GridView ColumnHeaderContainerStyle="{DynamicResource styleGridViewColumnHeader}">
                  <GridView.Columns>
                     <GridViewColumn Header="Company" DisplayMemberBinding="{Binding [company].[name]}" />
                     <GridViewColumn Header="City" DisplayMemberBinding="{Binding [address].[city]}" />
                     <GridViewColumn Header="Name" DisplayMemberBinding="{Binding [name]}" />
                     <GridViewColumn Header="Email" DisplayMemberBinding="{Binding [email]}" />
                     <GridViewColumn Header="Longitude" DisplayMemberBinding="{Binding [address].[geo].[lng]}" />
                     <GridViewColumn Header="Longitude" DisplayMemberBinding="{Binding [address].[geo].[lng]}" />
                  </GridView.Columns>
               </GridView>
            </ListView.View>
         </ListView>
      </DockPanel>
   </mashup:DataListPanel>
   <mashup:DataPanel Grid.Row="0" Grid.Column="2" Name="JsonString">
      <mashup:DataPanel.DataService>
         <mashup:DataService Type="REST">
            <mashup:DataService.Operations>
               <mashup:DataOperation Name="List">
                  <mashup:DataParameter Key="REST.BaseAddress" Value="https://jsonplaceholder.typicode.com/users/" />
                  <mashup:DataParameter Key="REST.AcceptHeader" Value="application/json" />
                  <mashup:DataParameter Key="REST.OutputType" Value="application/json" />
               </mashup:DataOperation>
            </mashup:DataService.Operations>
         </mashup:DataService>
      </mashup:DataPanel.DataService>
      <mashup:DataPanel.Events>
         <mashup:Events>
            <mashup:Event SourceEventName="Startup" TargetEventName="List" />
         </mashup:Events>
      </mashup:DataPanel.Events>
      <Grid Margin="10,0,10,0">
         <TextBox Text="{Binding [json]}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" AcceptsReturn="True" VerticalContentAlignment="Top" TextWrapping="Wrap" />
      </Grid>
   </mashup:DataPanel>
</Grid>

Mashup Converter with Zero Focus

In Smart Office 10.2.1.0.367 HF 38 there is a new value converter that converts a number to a string label focusing on zero; equals, above or below.

Mashup_Converter_Zero

The default behavior for the value converter is:

value < 0 returns NA
value = 0 returns No
value > 0 returns Yes
otherwise for non numeric input it returns an empty string

 Text="{Binding ElementName=InputValue, Path=Text, Converter={utils:AboveBelowZeroConverter}}"

 

The return labels for negative, zero and positive values can be customized by adding the ConverterParameter in the data binding containing three strings separated by _ or – characters. If the parameter is incorrect formatted the converter falls back to the default values and logs the issue if the log is in DEBUG mode.

Setting the following ConverterParameter will change the return labels to become Negative, Zero, Positive.

 Text="{Binding ElementName=InputValue, Path=Text, Converter={utils:AboveBelowZeroConverter}, ConverterParameter=Negative_Zero-Positive}"

 

Here is a the full code for you to try the converter out.

<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ui="clr-namespace:Mango.UI.Controls;assembly=Mango.UI" xmlns:mashup="clr-namespace:Mango.UI.Services.Mashup;assembly=Mango.UI" xmlns:utils="clr-namespace:Mango.UI.Services.Mashup;assembly=Mango.UI">
   <Grid.ColumnDefinitions>
      <ColumnDefinition Width="*" />
   </Grid.ColumnDefinitions>
   <Grid.RowDefinitions>
      <RowDefinition Height="Auto" />
      <RowDefinition Height="10" />
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
      <RowDefinition Height="*" />
   </Grid.RowDefinitions>
   <TextBlock Grid.Row="0" Text="Test of AboveBelowZeroConverter. Default: Return NA for negative values, No for zero and Yes for positive values, otherwise empty string" Margin="10" />
   <DockPanel Grid.Row="2" Margin="10" LastChildFill="False">
      <TextBlock DockPanel.Dock="Left" Text="Enter a value:" Margin="5" />
      <TextBox DockPanel.Dock="Left" Name="InputValue" Height="24" Width="60" VerticalAlignment="Top" HorizontalAlignment="Stretch" />
      <TextBlock DockPanel.Dock="Left" FontSize="16" Foreground="Green" Text="{Binding ElementName=InputValue, Path=Text, Converter={utils:AboveBelowZeroConverter}}" Margin="5" />
   </DockPanel>
   <DockPanel Grid.Row="3" Margin="10" LastChildFill="False">
      <TextBlock DockPanel.Dock="Top" Text="Custom return value. Set binding 'ConverterParameter=negative zero-positive' (Three strings separated by _ or - character, if invalid converter falls back to default)" Margin="5" />
      <TextBlock DockPanel.Dock="Top" FontSize="16" Foreground="Green" Text="{Binding ElementName=InputValue, Path=Text, Converter={utils:AboveBelowZeroConverter}, ConverterParameter=Negative_Zero-Positive}" Margin="150,5,5,5" />
   </DockPanel>
</Grid>

 

 

 

Calling a REST service from JScript

In this post we will take a look how to consume a REST service from the Smart Office JScript code. Related blog posts that focus on M3 APIs can be found at the following links.
Calling M3 APIs in JScript
Background workers in Smart Office scripts

Setting up the REST Data Service

Let’s start with the script code. It’s necessary to import the following three assemblies to gain access to the necessary classes and interfaces.

import MForms;
import Mango.Core.Services;
import System.ComponentModel;

The first step is to get a handle to the REST data service from the DataServiceManager.

var svc : IDataService = DataServiceManager.Current.Get("REST");

The REST data service is configured with a parameter set put in a DataItemCollection, so we start by setting the service address. I’m going to use a fake online REST service for the example to demonstrate how it’s done.

var params = new DataItemCollection();
params.Add(new DataItem(RestDataService.KeyBaseAddress, "https://jsonplaceholder.typicode.com"));

The available parameters can be found in the RestDataService class, the name starting with Key. It’s necessary to set KeyBaseAddress for the REST requests. KeyUri can contain a relative URI that is combined with KeyBaseAddress. Both parameters support variable substitution using names within curly braces {}. For example:

params.Add(new DataItem(RestDataService.KeyUri, "/users/{userId}"));
params.Add(new DataItem("userId", e.Argument));

or like this. Both will will result in the same endpoint URL.

params.Add(new DataItem(RestDataService.KeyUri, "/users/" + e.Argument));

If the userId variable equals to 7 the combined parameters results in the REST URL:

https://jsonplaceholder.typicode.com/users/7

The service request and response type headers must be specified unless the default values are required. The default values are the following two lines, that can be added for clarity but also omitted.

params.Add(new DataItem(RestDataService.KeyAcceptHeader, RestDataService.ContentTypeApplicationXml));
params.Add(new DataItem(RestDataService.KeyOutputType, RestDataService.OutputTypeDataItem));

 

Use the Background Worker to Prevent UI Freeze

Now we’re almost done, but the data service require that it’s running on a background thread. If not, it will throw an Exception to make you aware of that. This is done to prevent potential UI freeze and annoyed users. Let’s have a look at a simple solution by using the BackgroundWorker class.

var worker = new BackgroundWorker();
// Set the function that performs the service call
worker.add_DoWork(OnDoWork); 

// Set the function that performs update and clean-up after the call is done        
worker.add_RunWorkerCompleted(OnRunWorkerCompleted); 

// Launch the service call and provide start parameters
worker.RunWorkerAsync(serviceParameters);

The OnDoWork function will execute on the background  thread and call the data service. When done the OnRunWorkerCompleted function will execute on the UI thread to handle the response and do clean up after the call. Please, always handle Exceptions in your code!

public function OnDoWork(sender : Object, e : DoWorkEventArgs) 
{
    try 
    {
       var serviceParameters = e.Argument;
       // The data service call is done here (this is the background thread)
       e.Result = serverResponse
    } 
    catch(ex) 
    {
       // Exceptions must be handled
    }         
}   
public function OnRunWorkerCompleted(sender : Object, e : RunWorkerCompletedEventArgs) 
{
    try 
    {
       var worker = sender;
       worker.remove_DoWork(OnDoWork);
       worker.remove_RunWorkerCompleted(OnRunWorkerCompleted);
       var serviceResponse = e.Result;
       // The worker has completed its work (this is the UI thread) 
    } 
    catch(ex) 
    {
        // Exceptions must be handled 
    } 
}

The Full Script Example

Here is the full script example for you to test in the ISO Script Tool mforms://jscript:

import MForms;
import Mango.Core.Services;
import System.ComponentModel;

package MForms.JScript 
{
   class RESTDemo 
   {
      var debug;
      var controller;     
      public function Init(element: Object, args: Object, controller : Object, debug : Object) 
      {
         this.debug = debug;
         this.controller = controller;        
         debug.WriteLine("Script Initializing.");            
         DoRestCallOnBackgroundWorker();        
      }
    
      public function DoRestCallOnBackgroundWorker()
      { 
         debug.WriteLine("The REST service call is made on a background worker");          
         var worker = new BackgroundWorker();
         worker.add_DoWork(OnDoWork);
         worker.add_RunWorkerCompleted(OnRunWorkerCompleted);
        
         var userId = 7;         
         worker.RunWorkerAsync(userId);
      }
      public function OnDoWork(sender : Object, e : DoWorkEventArgs) 
      {
         try 
         {
            debug.WriteLine("OnDoWork started");
            debug.WriteLine("Calling a REST service getting DataItem response data");

            var svc : IDataService = DataServiceManager.Current.Get("REST");
            var params = new DataItemCollection();
            params.Add(new DataItem(RestDataService.KeyBaseAddress, "https://jsonplaceholder.typicode.com/"));             
            params.Add(new DataItem(RestDataService.KeyUri, "users/{userId}"));                          
            params.Add(new DataItem(RestDataService.KeyAcceptHeader, RestDataService.ContentTypeTextXml)); // default value                                  
            params.Add(new DataItem(RestDataService.KeyOutputType, RestDataService.OutputTypeDataItem));   // default value
            params.Add(new DataItem("userId", e.Argument)); 
            
            var dataRequest = new DataRequest(params);
            dataRequest.OperationType = "GET";
            var dataResponse = svc.Execute(dataRequest.OperationType, dataRequest);
            e.Result = dataResponse.Data;            
            debug.WriteLine("OnDoWork done");    
         } 
         catch(ex) 
         {
           debug.WriteLine("Exception: " + ex.Message);
           e.Result = null;
         }         
      }
      public function OnRunWorkerCompleted(sender : Object, e : RunWorkerCompletedEventArgs) 
      {
         try 
         {
            debug.WriteLine("OnRunWorkerCompleted running on UI thread");    
            var worker = sender; 
            worker.remove_DoWork(OnDoWork);
            worker.remove_RunWorkerCompleted(OnRunWorkerCompleted);
            if(e.Error != null) 
            {
               debug.WriteLine("Error: " + e.Error.Message); 
               return;
            }
            debug.WriteLine("Fetched user data for:");           
            var dataItem = e.Result;
            debug.WriteLine(dataItem["name"] + " (" + dataItem["username"] + ")" + " Phone " + dataItem["phone"]);  
         } 
         catch(ex) 
         {
            debug.WriteLine("Exception: " + ex.Message);
         }
      }
   }
}

 

Debug output when running the sample in the Script Tool.

Script Initializing.
The REST service call is made on a background worker
OnDoWork started
Calling a REST service getting DataItem response data
OnDoWork done
OnRunWorkerCompleted running on UI thread
Fetched user data for:
Kurtis Weissnat (Elwyn.Skiles) Phone 210.067.6132

Recent enhancements to Infor Smart Office for Lawson applications

With each 10.2.1.x hotfix for Smart Office, in addition to fixes of reported issues, many enhancements have also been included.  And with each release, we update the Knowledgebase article that summarizes those changes (KB-1626878). This post will provide some more detail about the enhancements included over the second half of 2018 and those so far in 2019.

10.2.1.0.329 (HF31)

1) A new user setting (Toolbox width on the Advanced tab) allows for a wider presentation of the forms and lists toolbox area.

2) In addition to the width setting, there is now also a setting to allow the toolbox to be locked in the open or displayed position.

Toolbox

3) Alpha filter fields on lists have always defaulted to a ‘Starts with’ comparison, but a new application setting (Default alpha search type – set in the Settings Editor for S3.Client) allows an administrator to change the default to ‘Contains’.

AlphaFilter

10.2.1.0.333 (HF32)

1) On the Actions menu of standard forms, there is now a menu item to launch the list view for forms that support it.

LaunchVendorList

2) Three enhancements were made to the Script tool: a) the help menu links to the Microsoft .NET reference; b) some common .NET event handler helpers were added; and c) the search providers were added to the context menu.

 

10.2.1.0.341 (HF33)

1)  Three new methods were added to the supported scripting model (as documented in the Object Browser): IForm GetFieldByNumer and IField Get/SetAttribute.

2) Before publishing a script file to the server, it is now scanned for non-ASCII characters which might cause subsequent compilations to fail. These characters might be introduced by writing script in another editor and pasting to the Script tool.

ScriptTool

3) A search feature was added to the Personalization Manager which allows an administrator the ability to find form and list personalizations by token as well as finding references to Jscript files.

LawsonPersonalizationManager

 

10.2.1.0.351 (HF34)

1) In addition to several minor enhancements, two major personalization enhancements for standard forms were introduced: the ability to display hidden fields and the ability to move standard elements. These are both described in greater details in this post: New Personalization features for Infor Smart Office for Lawson.

10.2.1.0.353 (HF35)

1)  A search feature was added to the Object Browser for help in locating property and method documentation and sample code.

SearchObjectBrowser

10.2.1.0.359 (HF36)

Note: this hot fix has been pulled due to the introduction of a serious regression in the M3 client. Since hotfixes are cumulative, the enhancements listed here are included in hot fix 37

1)  Infor Browser calculated fields can now be used as an operand for another calculated field.

2) A Lawson System Check utility is available to administrators to examine various settings and provide recommended changes, if any.

3) Three new scripting enhancements were added: two methods added to ControlsUtil (AddCustomTextBlock and AddCustomGroupLine); and constant for setting element foreground colors to the selected theme color (ScriptConstants.ThemeBrush).

4) The net://auth protocol is intended only to support Lawson web content and since the only version of Internet Explorer now supported by Lawson is IE 11, if the user’s browser emulation is not set to IE 11 with the net application is launched, a warning will be displayed. Administrators should consider locking the browser emulation application setting in the Settings Editor.

WebBrowserEmulation

10.2.1.0.361 (HF37)

1)  Two scripting enhancement were added to the supported scripting model (as documented in the Object Browser): an IField event, FieldValueChanged and the static class, HotkeyActions, which documents hotkey action names.

2) A new Profile setting in the SmartClient section of the Profile Editor (LogonBannerUrl) allows administrators to specify the URL of custom content to display in a browser control as users logon.

LogonBannerUrl

Smart Office commands cheat sheet

Smart Office has a number of commands, or in fact they are all links that will perform an action like opening a tool or clearing a cache. A special thanks to Jarda Dedek at EYELEVEL for sending me the original list. I took it and added a few tips of my own. In Smart Office every application is launched by an Uri. The Uri consists of a schema, like http and https, but we use mforms, sforms etc to identify different applications. As it turns out the short form links: is also a valid Uri, it’s just that it has no path, just the schema.

Download PDF version

Command Description
mforms://cmp600 Company switch
mforms://cmp100200 Division switch
mforms://lngcz Language switch
mforms://mitest MI Test
mforms://_command?value=clear cache Clear language and view definition cache. (No other caches) The reply from the command will show what caches have been cleared.
mforms://_command?value=clear view Clear view
mforms://_command?value=clear view all Clear all views
mforms://_command?value=clear lngcache Clear language cache
mforms://_command/?value=clear custcache Clear customization cashes in UI Adapter
mforms://jscript Script Tool
mforms://jscript/clear Clearing jScript cache with confirmation
mforms://jscript/clear&silent Clearing jScript cache without confirmation
mforms://_automation?template=<TEMPLATE NAME>&<NAME1>=<VALUE2>&<NAMEn>=<VALUEn> Automation links crated in Automation builder
mforms://_automation/?data=<AUTOMATION XML>  Automation link with automation file uploaded to server
admin://file/category Category File Administration
admin://importexportmanager Import/Export Manger
admin://installpointmnanager Install Point Manager
admin://predefinedwidgets Predefined Widgets
admin://profileeditor Profile Editor
admin://settingseditor Settings Editor
admin://userhistory User Logon History
admin://videosmanager Videos Manager
startpad://manager StartPad Manager
admin://file/category?category=StartPad StartPad File Administration – Start pad
admin://file/category?category=Mashup StartPad File Administration – Mashup
links: Link Manager
dev:help List of development commands
dev:icons Show icons
dev:local Show local folder
dev:localscriptpath Show local script path
dev:log Shows log
dev:supportzip Creates a zip with the log file and some other files like settings
dev:roaming Shows roaming folder
dev:root Shows root folder
dev:bin Shows bin folder
dev:user Shows local folder with user profile. All U** in S0 folder then can be deleted to refresh views/starting_values/user_data.
C:\Users\jarded\AppData\Local\Apps\2.0\
dev:server Show server folder
dev:shared Shows share folder
dev:resetlang Resets language
internal://log Smart office Log viewer
internal://log?search=idledetect Add a free-text search parameter to start with all entries matching the search criteria, in this example all entries related to user idle detection.
internal://log?search=idledetect&session=2

internal://log?search=idledetect&session=last

internal://log?search=idledetect&session=prev

This can be extended with the session parameter to specify the initial session to select in the log. It can be a value [1..n], last will select the current session, prev will select the previous session and full selects the whole log file. Previous is useful if you examine something that happens during shutdown and you examine the log after a restart.
internal://log?cat=service unavailable
internal://log?cat=service%20unavailable
internal://log?cat=service unavailable&session=full
You can specify the category you defined as initial selection.

 

internal://log?search=idledetect&sessiontime=13:10 The session time parameter that may be useful in some special cases and if the session time parameter is present the session parameter is ignored.
internal://themeulator Canvas theme Emulator
tool://wstest Web services test tool
widget: Opens the widget library
widget://add/?scheme=Mango.Widgets.MenuWidget Adds a widget to the canvas, use the widget ID to specify which widget to add.

 

 

 

M3 TechEd – Reporting capabilities

I took the opportunity to leave the hands-on lab and watched Mike Fletcher’s session on Reporting capabilities within Infor M3. He started off showing Homepages, and how to use it to present operational metrics, analyzing current data from M3. The session presented a number of other techniques as well.

F4DC1C6C-3C17-4C94-9622-9FC8E14252E5

An example is to show a monitor, with the count of stopped orders, and then being able to go to the list from the Monitor Widget.

Another example was to show items under development without prices by using search. The query was built by selecting “add to search” from the context menu on the column header. The search query can then be added to a menu in Homepages so that you have a link to the tasks that you work with on a regular basis.

In business context and context applications are also a form of reporting that can provide context information to M3 data, including Birst reports, Infor Document Management documents, as well as H5 Mashup applications as Context Applications.

A great session that give business context to the development that I’ve been working on. It is always nice to see how powerful the different widgets, SDKs and H5 are from a business perspective.

I also got some good feedback from customers and partners which is really what TechEd is all about, learning from each other.

M3 TechEd – Day 2

Today is day 2 at M3 TechEd. We started with Infor OS and Heath on the main stage. Infor OS is great and will give you a lot of value. If you want to learn all about Infor OS don’t miss the coming TechEd in Las Vegas in February.

C772F287-2EB2-44A9-839C-1A6FE11CDED1

Heath started with Infor OS and a quick overview.

CE542FE8-0EE1-4B96-A396-219DB68E5E1D

Infor Document Management is a living repository for collaborating around documents.

Next on stage was Torbjörn on User Experience starting with Homepages the application that is built for role based, highly configurable widgets presenting a process based view, highlighting those items you need to take action on.

94DA5E14-864C-41D2-9475-CF5658E83FF7.jpeg

Then there was a great session on organization change management by Carol Tyler and how we at Infor is transforming the industry and cultivating our culture of innovation.

Yesterday I arrived early, in the dark, and I left, in the dark. That’s just how it is in December in Stockholm. Today I was half an hour later, just in time to catch the sunrise.

 

Beautiful sunrise facing Gröna Lund and Djurgården, on the other side.

Moving to H5 and Infor OS from Smart Office

My last session of the day was  Smart Office compared to H5 and how to move from Smart Office to H5 and Infor OS. I was assisting Magnus as a subject expert. Not that he needed my help.

B06ED645-9BBD-4AC2-98A6-84E59A9AFD75

This session will be held tomorrow as well, so if you missed it, there is another chance.

There are a few tools that will help you make the move. For example moving links and favorites. But please note that not all links will work. H5 will handle mforms:// links, but there might be some links that will not work, for example anything that is accessing the file system or other schemas than mforms and https.

The clients are of course different but the M3 UI Adapter is still the same and both client talk to the MUA server when it runs M3 programs. With H5 comes Infor Ming.le and the complete Infor OS. Below is a comparison between the featureset in H5 and Smart Office.

SmartOfficevsH5

We spent some time on Ming.le Homepages which is the product I’ve been working on lately. It is a great starting point for all types of users and with the M3 widgets you can monitor and take actions and have standard pages for different roles.

The M3 Implementation Accelerator (IA) solutions from July 2018 incorporate a small number of role-based Ming.le homepages designed to complement the preconfigured solutions. Depending on the industry, there are typically six roles containing these templates:

  • General Ledger Controller
  • Accounts Payable Controller
  • Accounts Receivable Controller
  • Customer Services
  • Production Manager
  • Purchase Manager

They are all available for download, fee, in KB 2008101.

M3 TechEd

I’m at M3 TechEd in Stockholm. If you are attending don’t miss checking out the hands-on open lab. There are a lot of experts available to help you and show you what we are working on and how we can help you be successful.

50D4E859-514E-4A66-A44B-D7676036A32B

Ole Rasmussen presenting M3 product strategy.

Great location at Elite Hotel at Nacka stand. I came in early.

 

I’ll be on two tracks, Extensibility and User Experience, see you there 😀.

The Sound of a Silent Install

The conventional way to install Infor Smart Office on client machines is to use Microsoft ClickOnce network deployment to get an isolated, secure, self-updating application that can be installed and run with minimal user interaction.

However, if you want to run Infor Smart Office client in a virtualized desktop environment, the Microsoft ClickOnce deployment is not recommended. Instead, install the Infor Smart Office client using the Windows installer (InforSmartOffice.msi) bundled in the software package.

The Windows installer launch a sequence of user interface screens that lead the user through the installation process and gather necessary information. In this case the user needs to enter the Smart Office server URL and the location of the Smart Office features.

For mass deployment it’s more convenient to have a silent scripted install without user interaction and this is possible if you know the name of the public properties for the Smart Office Windows installer and apply the appropriate MSI Command-Line Options.

The public properties for the Smart Office installation are SERVERURL and FEATURELOCATION as described in the Infor Smart Office Installation Guide [ISOIG].

Here is an example of a silent install using msiexec on the command-line run as administrator.

msiexec.exe /I InforSmartOffice.msi /QN /L*V iso_msi_install.log SERVERURL=httpsUrl_to_isoserver FEATURELOCATION=httpsUrl_to_features 

Explanation of options and parameters:
/I 				Install option
InforSmartOffice.msi 		Set the location path to the InforSmartOffice.msi file
/QN 				No user interface option
/L*V 				Log all information with Verbose output option
iso_msi_install.log 		EXISTING location path to the log file
SERVERURL=https... 		Smart Office server URL
FEATURELOCATION=https...	Smart Office feature URL

If you don’t have sufficient administrative privileges to do the installation for all users on the machine you will get a failed installation error code [1925] in the log file.

Important information!
Read the Infor Smart Office Installation Guide [ISOIG] for ISO feature limitations and the Virtualization & Hardware Support Statement regarding Smart Office client support for virtualized environments.

 

Snooping my Mashup for Missing Data

Sometimes when you’re developing a Mashup it happens that the data you’re expecting to appear in a databound control is not showing up. There’s a small developer tool named Snoop that will help you examine the runtime visual tree of a Windows Presentation Foundation (WPF) user interface.

Snoop WPF Spy utility

Download

Launch Smart Office and open the Mashup Designer. In this post we use an example from the Help menu named Document found in the Infor Document Management category.

ISOIDMMashupEx

Run the example, save it and search for any Document Type e g CLM_Document. Depending on the available data, it will look something like this in the running Mashup.

ISOIDMMashupExRunning

Now it’s time to launch the Snoop utlitity tool. It has a slim and narrow window. ISOSnoop

Drag the Snoop hair-cross (marked yellow) and drop it on your Mashup window. A new window will open and present the visual tree representation of the user interface in the left-hand panel and the details of the selected UI element in the right-hand panel. Below the details panel there’s a preview panel to render and show the selected UI element (if actived with the power button).

Press down the CTRL- and SHIFT- key simultaneously while you drag the mouse over the Mashup window. The control beneath the mouse will be selected and decorated with a red border and the corresponding element in the Snoop visual tree view will be highlighted and selected. Navigation can also be done directly in the tree-view by selecting UI element nodes.

ISO IDM Snoop

ISOIDMMashupExAttrSelected

Let’s examine the bound data for the document attribute panel. Use the mouse or locate and select the LangAttrList (ListBox) in the visual tree. Scroll down in the Properties list until you find ItemsSource property for the ListBox control.

ISO IDM Snoop ItemsSource

It’s now possible to drill-down into the data collection with a double-click on the value of ItemsSource, in this example [System.Windows.Data.CompositeCollection]. Now you see that the collection contains 1 item: this[0]. You can continue to drill-down with a double-click on the value this[0] to see the details of the item or navigate back up by clicking the up-arrow button (marked yellow).

ISO IDM Snoop Drill-down and up

In this case the ListBox will apply a template for each document attribute found in a collection item. Select the attrTemplate under the ListBoxItem to show its properties. In most template cases the data will be in the DataContext property of the element. Select the Data Context tab for a dedicated view of the property, in this case the DataContext is a piece of XML.
Note that there’s a Binding Errors column in the properties list that will show an error message if the data binding declaration for a UI element property is wrong and the data source can’t be found.

ISO IDM Snoop Data Context

We’ve only scratched the surface of the Snoop tool functionality but it’s the basics needed to investige why your data is not showing up.

New Log Viewer – Part V

Part I – Overview of the Improved UI
Part II – How to Find Your Way
Part III – Getting the Facts and Categories
Part IV – ClickOnce Installation Log

M3 Transaction Time Measurements

When the Smart Office log (debug mode) contains M3 (MForms) time measurements, a tool area will open up beneath the content area with an overview of where the time is spent for the selected time log entry. The purpose of the overview is to put the time values in context and give a better understanding where to look further for perceived performance issues. Note that the chart values don’t always add up exactly due to rounding errors and measurement precision.

Click on the category Timer under the M3 section to focus on the time measurements. Select the first log line and use the up-/down- arrow keyboard keys to scroll through the list and see how the values change in the overview chart.

ISOLogViewerMFormsTimer

Depicted here is the standalone version of the Log Viewer, where Smart Office client dependent functionality have been hidden, therefore a few buttons are missing.

In the Tools context menu (found on any log line) you have export options for the measured times. Selecting an export option extracts all measurements and formats them as comma separated values that can be copied into the Clipboard, exported to Microsoft Excel or opened in the Windows associated text viewer.

Exporting the measurements to Microsoft Excel makes it easy to generate a line chart over the measured times for selected function calls.

ISOLogViewerExcelM3

How do I turn on the M3 time measurement on the server side?

In the Smart Office client you control the time measurement by launching:

mforms://timer/on
mforms://timer/off

The Smart Office log level setting must also be set to Debug.

New Log Viewer – Part IV

Part I – Overview of the Improved UI
Part II – How to Find Your Way
Part III – Getting the Facts and Categories
Part V – M3 Transaction Time Measurements

Smart Office ClickOnce Installation Log

Smart Office is using Microsoft ClickOnce Technology to achieve a self-updating client that can be installed and launched with minimal user interaction from a central location. Once in a while it can happen that the ClickOnce installation fails and the brief details provided in the error dialog can be hard to interpret. In verbose log mode you can follow the installation step-by-step.

In ISO 10.2.1.0.223 HF23 and beyond the Log Viewer can enable the verbose Microsoft ClickOnce log from the tool menu. The ClickOnce log file setting is active for the current Windows user on the local machine until deactivated.
The verbose log can be activated manually, from another tool or other Smart Office installation by setting Windows Registry values.

ClickOnce Log Tool Options

ISOLogViewerClickOnceToolMenu

Enable Log

Toggle the activation of the verbose ClickOnce log with the Enable Log menu item. A log will be created at the next launch of Smart Office or by any other application using ClickOnce technology. If ClickOnce log is deactivated with an Enable Log uncheck and the activation was not made by this tool, a warning will be presented to inform and verify the deactivation. The ClickOnce.log file is stored in the temporary path defined in the Windows environment.

Copy Log to Clipboard

If a ClickOnce.log file exists, the log content is copied into the Windows Clipboard.

Open Log in External Viewer

If a ClickOnce.log file exists, the log is opened in the Windows associated text viewer.

Open Log File Location

If a ClickOnce.log file exists, the folder location will open in Windows File Explorer.

Analyze ClickOnce Log

Runs an analysis of the current ClickOnce log and displays a summary overview  highlighting the sessions containing errors.

ISOLogViewerClickOnceToolMenu

The Clipboard button copies the selected session to the Windows Clipboard and the View External button opens the selected session in the Windows associated text viewer.

Create Verbose Mode Setup File

As a Smart Office administrator you have the option to generate a file that modify the Windows Registry to enable/disable the ClickOnce verbose log when targeting another Windows client where the Smart Office installation fails. Windows security applies to the launch of registry files.

Enable ClickOnce Log

Select Enable ClickOnce Log to create the EnableClickOnceVerboseLog.reg file. After the file has been created the Windows File Explorer will open to its location. Verify that the named log file location matches the target client, otherwise change it. The default location is the temporary folder of the Windows environment.

Disable ClickOnce Log

Select Enable ClickOnce Log to create the DisableClickOnceVerboseLog.reg file. After the file has been created the Windows File Explorer will open to its location.

Open a Log from Another Computer

If you have a log file sent to you or copied from another machine, set the Enable Log toggle menu as checked and click on the menu Open Log File Location to get a Windows File Explorer showing the target folder. Ensure that the log file is named ClickOnce.log  and copy it to the target folder. Now you can select Analyze ClickOnce Log and see the overview in the tool view.

 

Moving Smart Office Favorites into H5

If you’re moving from the Smart Office client to the H5 client for M3, there are two utility tools that will help you to export and transform the compatible favorites of the Smart Office users. You can find the tools in the latest Smart Office delivery package (v10.2.1.0) under the Additional Files Folder; MangoAdminTool.zip and ISOtoH5FavoritesExportTool.zip. Get the zip archives and extract the tools.

For older Smart Office versions read the chapter Importing User Files in Smart Office 10.2 using the MangoAdminTool in the Infor Smart Office Administration Guide (ISOAG_10.2.1 page 254).

Export the User Favorites from Smart Office

Launch the MangoAdminTool.exe, select and login to the Smart Office server.

MangoAdminTool

  • Select the Export tab in the Mango Admin Tool
  • Check the Roaming files checkbox and type Favorites.lfv as filter in the field
  • Press the Export button to create the MangoData.zip file
  • Extract the MangoData.zip archive into a temporary folder

Transform the Favorites and Prepare for H5 Import

Launch the FavoritesMigrationTool.exe, browse and select the UserFiles in the temporary folder with the extracted content from MangoData.zip. The favorites for the selected users will be filtered, excluding all links but the ones with http, https and mforms as scheme.

ISOFavoritesExportH5

  • Select the users to be exported among the ones listed
  • Press the Export button to create the ISOtoH5FavoritesExport.zip

Windows File Explorer will open and show the location of the created ISOtoH5FavoritesExport.zip. In the same folder there is a transformation report named ISOtoH5FavoritesExportReport.txt as a receipt of what has been done during the operation.

Import the Favorites into H5

Launch the H5 client and select Administration Tools and Import of Favorites.

H5ImportFavorites

  • Press the Upload button and select the ISOtoH5FavoritesExport.zip file
  • Press the Import button and then restart the server

Done!