Please add block possibility to Permission table

Yesterday I suggested to Microsoft an enhancement to the permission functionality.  That was an informal suggestion so I logged into Microsoft Connect and added a formal suggestion.

In table 2000000005 Permission we can assign permission to objects.  In the Classic Client we had the possibility to assign permissions to objects with the type System.  This is not working in the NAV 2013 (R2) client.

This causes a problem, for example a user with SUPER (Data) permission can delete a company from the database.

I suggest that a new option be added to fields 6, 7, 8, 9, 10 in the above table.
Current option string is ” ,Yes,Indirect”
The new option string would be ” ,Yes,Indirect,Blocked”

If an access type is blocked in any permission entry the access will be blocked even if there is access in another permission set.

I would for example add a line to the Permission table for the SUPER (Data) permission set that will block Insert, Modify and Delete for table 2000000006 Company.

Please help me by voting for the suggestion.

InvalidSecurity error when connecting to a NAV Server

For a few weeks I have been running an Internet facing NAV Server.  This server is using “NAVUserPassword” authentication and a SSL certificate.

Everything was working as planned until a few days ago when I was using my PowerShell scripts to install the newest knowledge base package, Update Rollup 3 for Microsoft Dynamics NAV 2013 R2 (Build 36035).

When I opened the client after the update a new version was downloaded and started.  However, I got an error.

FaultCode = „InvalidSecurity“
Reason = „An error occurred when verifying security for the message.“

I rolled back the upgrade and tried again with the same error.  This was the strangest thing.

The Web Client was working perfectly but not the Windows Client.

As I was walking and wandering the solution dropped in.  I recognized that a secure connection is established with by using the date and time of both the client and the server.  I sat back down and checked – the clock on my server was 6 minutes late!

I set the clock and retried the connection – successfully !

I then configured my server to synchronize the clock with a time source like is shown here or here.

Using XML to transfer Data between NAV companies

In November 2010 I blogged about Transferring small amount of data between databases.  There I had a Form that can read and write a XML file with table data.  This Form was used frequently in my company to move data between companies and databases.

The good thing about this method is that I can move data between database versions.

XMLDataTransfer

Yesterday I got a request to share a NAV 2013 R2 version of this solution.  Here it is !

This version includes support for BLOB data in the tables.  BLOB data is converted to Base64 and included in the XML file.

Be careful when importing.  Existing data will be overwritten.

Dynamics XML Data Transfer for NAV 2013 R2

Images on the NAV 2013 Role Center Ribbon

As a NAV 2013 user I have customized my role center.  That includes putting on my Action Ribbon my most used actions.

NAV2013Ribbon

Then I searched for a way to update the images for my newly added actions but found none.  I asked on Mibuso and got confirmed from Mark Brummel that this option is not available.

From the Departments menu I right-click my most used items and select to “Add to Actions on Role Center Ribbon”.  On the Ribbon I want to be able to customize the images to my customized actions.  I suggested this on Microsoft Connect and if you agree please vote for the suggestion.

Reading the NAV 2013 Service Configuration

In a previous blog I showed how to get the database name and the database server name for NAV 2009.  I used Automation object to read the service configuration file and pull from there the required information.

This method is obsolete for NAV 2013 but here is the required code to pick similar and more information from the service configuration file.

[code]OBJECT Codeunit 50000 Read Service Config
{
OBJECT-PROPERTIES
{
Date=18.06.13;
Time=22:51:31;
Modified=Yes;
Version List=Dynamics.is;
}
PROPERTIES
{
SingleInstance=Yes;
OnRun=BEGIN
END;

}
CODE
{

PROCEDURE FindMiddleTierServicePath@1000000002() ServicePath : Text[1024];
VAR
ActiveSession@1000000002 : Record 2000000110;
ServerFile@1000000001 : DotNet "’mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′.System.IO.File";
XMLDoc@1000000000 : DotNet "’System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′.System.Xml.XmlDocument";
XMLNode@1000000003 : DotNet "’System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′.System.Xml.XmlNode";
BEGIN
ActiveSession.SETRANGE("Session ID",SESSIONID);
ActiveSession.FINDFIRST;

XMLDoc := XMLDoc.XmlDocument;
IF ServerFile.Exists(APPLICATIONPATH + ‘Instances\’ + ActiveSession."Server Instance Name" + ‘\CustomSettings.config’) THEN
XMLDoc.Load(APPLICATIONPATH + ‘Instances\’ + ActiveSession."Server Instance Name" + ‘\CustomSettings.config’)
ELSE
XMLDoc.Load(APPLICATIONPATH + ‘CustomSettings.config’);

ServicePath := ‘DynamicsNAV://’ + ActiveSession."Server Computer Name" + ‘:’;

XMLNode := XMLDoc.SelectSingleNode(‘//appSettings/add[@key=”ClientServicesPort”]’);
ServicePath := ServicePath + XMLNode.Attributes.Item(1).InnerText + ‘/’ + ActiveSession."Server Instance Name";
CLEAR(XMLDoc);
END;

PROCEDURE FindSOAPWebServicePath@10010403() ServicePath : Text[1024];
VAR
ActiveSession@1000000002 : Record 2000000110;
ServerFile@1000000001 : DotNet "’mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′.System.IO.File";
XMLDoc@1000000000 : DotNet "’System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′.System.Xml.XmlDocument";
XMLNode@1000000003 : DotNet "’System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′.System.Xml.XmlNode";
httpUtility@1000000004 : DotNet "’System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’.System.Web.HttpUtility";
BEGIN
ActiveSession.SETRANGE("Session ID",SESSIONID);
ActiveSession.FINDFIRST;

httpUtility := httpUtility.HttpUtility;
XMLDoc := XMLDoc.XmlDocument;
IF ServerFile.Exists(APPLICATIONPATH + ‘Instances\’ + ActiveSession."Server Instance Name" + ‘\CustomSettings.config’) THEN
XMLDoc.Load(APPLICATIONPATH + ‘Instances\’ + ActiveSession."Server Instance Name" + ‘\CustomSettings.config’)
ELSE
XMLDoc.Load(APPLICATIONPATH + ‘CustomSettings.config’);

XMLNode := XMLDoc.SelectSingleNode(‘//appSettings/add[@key=”SOAPServicesSSLEnabled”]’);
IF UPPERCASE(XMLNode.Attributes.Item(1).InnerText) = ‘FALSE’ THEN
ServicePath := ‘http://’
ELSE
ServicePath := ‘https://’;

ServicePath := ServicePath + ActiveSession."Server Computer Name" + ‘:’;

XMLNode := XMLDoc.SelectSingleNode(‘//appSettings/add[@key=”SOAPServicesPort”]’);
ServicePath := ServicePath + XMLNode.Attributes.Item(1).InnerText + ‘/’ + ActiveSession."Server Instance Name" + ‘/WS/’;
ServicePath := ServicePath + httpUtility.UrlPathEncode(COMPANYNAME) + ‘/Services’;

CLEAR(XMLDoc);
END;

PROCEDURE FindODataWebServicePath@10010405() ServicePath : Text[1024];
VAR
ActiveSession@1000000002 : Record 2000000110;
ServerFile@1000000001 : DotNet "’mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′.System.IO.File";
XMLDoc@1000000000 : DotNet "’System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′.System.Xml.XmlDocument";
XMLNode@1000000003 : DotNet "’System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′.System.Xml.XmlNode";
BEGIN
ActiveSession.SETRANGE("Session ID",SESSIONID);
ActiveSession.FINDFIRST;

XMLDoc := XMLDoc.XmlDocument;
IF ServerFile.Exists(APPLICATIONPATH + ‘Instances\’ + ActiveSession."Server Instance Name" + ‘\CustomSettings.config’) THEN
XMLDoc.Load(APPLICATIONPATH + ‘Instances\’ + ActiveSession."Server Instance Name" + ‘\CustomSettings.config’)
ELSE
XMLDoc.Load(APPLICATIONPATH + ‘CustomSettings.config’);

ServicePath := ‘http://’ + ActiveSession."Server Computer Name" + ‘:’;

XMLNode := XMLDoc.SelectSingleNode(‘//appSettings/add[@key=”ODataServicesPort”]’);
ServicePath := ServicePath + XMLNode.Attributes.Item(1).InnerText + ‘/’ + ActiveSession."Server Instance Name" + ‘/OData/’;

CLEAR(XMLDoc);
END;

PROCEDURE FindDatabaseServerName@1000000000() DatabaseServerName : Text[1024];
VAR
ActiveSession@1000000003 : Record 2000000110;
ServerFile@1000000002 : DotNet "’mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′.System.IO.File";
XMLDoc@1000000001 : DotNet "’System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′.System.Xml.XmlDocument";
XMLNode@1000000000 : DotNet "’System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′.System.Xml.XmlNode";
DatabaseServer@1000000004 : Text[1024];
DatabaseInstance@1000000005 : Text[1023];
BEGIN
ActiveSession.SETRANGE("Session ID",SESSIONID);
ActiveSession.FINDFIRST;

XMLDoc := XMLDoc.XmlDocument;
IF ServerFile.Exists(APPLICATIONPATH + ‘Instances\’ + ActiveSession."Server Instance Name" + ‘\CustomSettings.config’) THEN
XMLDoc.Load(APPLICATIONPATH + ‘Instances\’ + ActiveSession."Server Instance Name" + ‘\CustomSettings.config’)
ELSE
XMLDoc.Load(APPLICATIONPATH + ‘CustomSettings.config’);

XMLNode := XMLDoc.SelectSingleNode(‘//appSettings/add[@key=”DatabaseServer”]’);
DatabaseServer := XMLNode.Attributes.Item(1).InnerText;

XMLNode := XMLDoc.SelectSingleNode(‘//appSettings/add[@key=”DatabaseInstance”]’);
DatabaseInstance := XMLNode.Attributes.Item(1).InnerText;

CLEAR(XMLDoc);

IF DatabaseInstance = ” THEN
DatabaseServerName := DatabaseServer
ELSE
DatabaseServerName := DatabaseServer + ‘\’ + DatabaseInstance;
END;

BEGIN
END.
}
}

[/code]

The four functions here will give you the path to start the Windows Client, the path to the Soap Web Service, the path to OData Web Service and finally the database server name including instance name if used.

The Codeunit is available here: ReadServiceConfig

The new PingPong add-in for NAV 2013

In NAV 2009 I used a custom control add-in to enable timers in the Role Tailored Client.  NAV 2013 ships with a control add-in that is called PingPong.  In upgrading one of my solution to NAV 2013 I wanted to remove the custom control and introduce the PingPong instead.

PingPongProperties

The control requires a name and as the standard functionality does I use the name PingPong.  The code I had in the – OnControlAddin trigger is now moved to a new trigger, PingPong::Pong.  The new timer does not work the same way the old one does.  The method used is similar to the new method in the Job Queue where the sleep function is used instead of a regular timer.  By executing Currpage.PingPong.Ping(500) a new thread is started that sleeps for 500 milliseconds and then fires the trigger PingPong::Pong.  Hence the add-in name.  When all required code in this trigger has been executed another Ping is required to thow the next ball.

PingPongTriggers

This is all good if the application is only using one timer.  I saw that if I already had one PingPong working then in the subsequent page PingPong did not work.  The good news is that the timer control add-in that I created for NAV 2009 also works in NAV 2013.  The page that I open from the page running PingPong will continue to use the timer add-in that originated from Freddy and I changed a little bit.

Scan document with NAV via the Hardware Hub

I have now created a document scanning solution that uses the Hardware Hub.  This means that you can place the scanner on any computer and the NAV Windows Client on any or the same computer.

All you need is the Hardware Hub Twain Client on the computer that is connected to the scanner.  The software is available for download and install in by selecting the link above.

Attached is a Page and a Codeunit for NAV 2013 for you to test the scanning.  Import the fob file into your NAV and run page 50093.  This will require the Hardware Hub Proxy installed on your server or your client add-ins folder.

HubScanning

Use the AssistEdit button to create a new Twain Scanner GUID.  The installed Hardware Hub Twain Client will minimize to you notification area.  Locate and double-click the notification icon to bring up the client settings and copy the GUID from the page to the client.

HubCLient

Then just minimize the client again to the notification area.  Close and reopen page 50093, select your scanner and scan.  The scanned document will be located on the server and you can download and open the document with the assist button.

In the attached ZIP file you will find the file HardwareHubProxy.dll that you will need to put in your server add-ins folder.  If you would like to test this on your local machine just change the RunOnClient property in Codeunit 50093 and put the HardwareHubProxy.dll in your Windows Client add-ins folder.

HubLocalSettings

Here is the required objects.  HubScanning
Updated HardwareHubProxy.dll

Hardware Hub IIS Service on Objects4NAV.com