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