Two good friends: KFS and AD

When it comes to providing a good user experience, a convenient user interface is key to success. We have lots of possibilities within Kofax Transformation Modules, with ActionEvents being only one of many – but what about other Kofax products? This time I’d like to focus on Kofax Front Office Server.

But first, let me ask you a question: have you ever worked with a multifuntional printer? Many of them do have a touch panel, allowing you to choose what kind of service you are looking for: maybe you want to make a copy of something, but – more interesting – maybe you require a document scanned. And sometimes those MFPs do allow you to send that very document via email. Okay, this is where I finally ask my real question: have you ever typed in a long email address on one of those panels?

It’s not exactly one of those fancy tablet you are confonted with. There is no multi-touch, no auto-complete, and sometimes not even an @-sign at the virtual keyboard without going into another sub-menu. In short: typing on a MFP is all but the definition of user-friendly. At least it drives me mad. And I am not alone, since many of my customers or colleagues share these feelings.

Our primary goal: make your users type as little as possible.

Wouldn’t it be great if there was an auto-complete function on the MFP? For example, when I type the first three letters of my email address – “wol”, I’d like to be prompted with all the possibilities already? Well, let me tell you the good news first: it is possible. The bad news is that we won’t get a super-fancy combobox with autocomplete. And something neutral: we need an additional datasource – where to get the email addresses from? I want to proof that even on multifunctional printers (MFP) a good user experience is possible. In this example I want to focus on Active Directory as external datasource.

The final solution:

MFP-Demo

This is what I came up with. The user enters the first n (mostly 3) letters into the “Search” field and then clicks on the drop-down. The contents will be filtered depending on the search, saving your users a lot of time. Two possible use cases spring into our minds: you want to scan a document, convert it into a searchable PDF (OCR’ed) and

  • store it on a general fileshare in your personal folder (which is named after the users’ login name),
  • send it via e-mail to a colleague of yours.

Where to get data from?

The possibilities are endless, but as said, for this example I focussed on Active Directory only. This database stores all the information we require to satisfy both our use cases: a users’ logon name and email address (of course there is much more). Querying the Active Directory is rather simple and is achieved utilizing LDAP. As we are going to write a validation script in Kofax Capture, we can utilitze all functionality the .NET framework has to offer, so I’d decided to go for the DirectorySearcher class from the Systems.DirectoryServices namespace. I wrote one helper class and a class capable of storing the entities returned:

ADHelper

The class ADHelper contains static (alright, VB.NET calls them “Shared”) methods only. GetAllAvailableProperties is a pretty nice method if you want to find out which properties are exposed by the AD. Well, just provide that method with a path to the AD object you want to query (which should be a group or a person), and you’ll get back a list of exposed properties. Here’s an example:

I queried my own demo AD using the following string: “LDAP://localhost/CN=Jane Doe,CN=Users,DC=lynx,DC=local”

That is the result, as you can see: plenty of options to query. For our use case, I am going to use “samaccountname” which will hold the user’s logon name plus “mail”, which will hold the email address.

ADProperties

The other method, GetUsersFromActiveDirectory is a little bit more complex. It needs to be provided with a path to the AD objects as well, but requires information about the properties you want to load (in our case, “samaccountname” and “mail”) and a filter to apply – in our case I want to look for certain users belonging to a so-called Distribution List (a group) only. If I would not apply such a filter, you’d get back all service accounts as well. And you do not want to send your emails to them, do you?

This is a sample call of the GetUsersFromActiveDirectory method:


Dim persons As New List(Of Entity)
Dim properties As New List(Of String)
Dim filter As String

' this list contains all the properties we want to load from the AD, such as email address, name, home directory, etc.
properties.Add("samaccountname")
properties.Add("mail")
properties.Add("homedirectory")

' this is the filter applied to the items returned from the AD. for example, we can select persons only, but also persons from a specific group.
' example 1: select persons only
filter = "(objectCategory=Person)"
' example 2: select users who are part of a specific distribution list (a group in AD)
filter = "(memberOf=CN=DL-All,CN=Users,DC=lynx,DC=local)"

' now we collect all the persons from AD that satisfy the filter we just set up. note that only those properties you selected earlier on will be saved!
persons = GetUsersFromActiveDirectory(PATH_TO_AD, properties, filter)
' here we sort the persons in alphabetic order based on a property (in this case: 0 = the user account name)
SortPersons(properties(0), persons)

' now you can do whatever you need to be done with the list of persons
For Each p As Entity In persons
Console.WriteLine(p.ToString)
Next

Note that at the very end you’ll end up with a list of entities (type of Entity class). This class is pretty straight-forward: it only consits of a dictionary of the properties you loaded earlier on, and it overrides the ToString method to produce some more readable output. It also provides a method to check whether a user property does exist, which can become quite handy if you want to avoid KeyNotFound-Exceptions. This could occur if you – for example – try to load the homedirectory-property, which is not set at some users. So, I queried all users from my distribution list “DL-All”, this is the result:

ADUsers

Note that – as mentioned earlier – not all properties are set for all users. In my example, only John Doe has a home directory set. If you would like to download the sample project, click on the following link. Just make sure to alter the path to the active directory.

How to link KFS and the AD

Fortunately, KFS comes with plenty of examples on how to populate drop-downs and which events to react, so I won’t elaborate too much about that. Just make sure to add a reference to Kofax.KFSLib.KFSValidation (best thing is to start with the CityDemo provided with KFS). Plus, we need a list of entities that is available to all methods within the validation class.

First we start with two helper subs. One does initialize and populate the list of persons we will use further on, the second one links their properties (email and user name) with the dropdowns.


Private Sub GetADPersons()

Dim properties As New List(Of String)
Dim filter As String

' this list contains all the properties we want to load from the AD, such as email address, name, home directory, etc.
properties.Add("samaccountname")
properties.Add("mail")
properties.Add("homedirectory")

' this is the filter applied to the items returned from the AD. for example, we can select persons only, but also persons from a specific group.
' example 1: select persons only
filter = "(objectCategory=Person)"
' example 2: select users who are part of a specific distribution list (a group in AD)
filter = "(memberOf=CN=DL-All,CN=Users,DC=lynx,DC=local)"

' now we collect all the persons from AD that satisfy the filter we just set up. note that only those properties you selected earlier on will be saved!
persons = GetUsersFromActiveDirectory(PATH_TO_AD, properties, filter)
' here we sort the persons in alphabetic order based on a property (in this case: 0 = the user account name)
SortPersons(properties(0), persons)

End Sub

Private Sub FillDropdowns()
' first we prefill the drop-downs; we only add a value if the property does exist
For Each p As Entity In persons
If p.UserPropertyExists("mail") Then Email.IndexField.SuggestedValues.Add(p.UserProperties("mail"))
If p.UserPropertyExists("samaccountname") Then Name.IndexField.SuggestedValues.Add(p.UserProperties("samaccountname"))
Next
End Sub

Before the document gets loaded, we are going to populate the index fields with all there is available from AD:


Private Sub MFP_AD_Demo_DC_DocumentPreProcessing(ByVal sender As Object, ByVal e As Kofax.AscentCapture.Scripting.PreDocumentEventArgs) Handles Me.DocumentPreProcessing

' load all persons from AD
GetADPersons()

' make sure the user must pick a value from the list
Name.IndexField.SuggestedValuesForceMatch = True
Email.IndexField.SuggestedValuesForceMatch = True

' fill the dropdowns
FillDropdowns()

End Sub

Then, after the “search” field is left, we either perform a search and filter both dropdowns, or we restore their original content in case the user has left the field empty.


Private Sub Search_FieldPostProcessing(ByVal sender As Object, ByVal e As Kofax.AscentCapture.Scripting.PostFieldEventArgs) Handles Search.FieldPostProcessing

' now we filter the contents of both comboboxes based on the search value
' first clear them ..
Email.IndexField.SuggestedValues.RemoveAll()
Name.IndexField.SuggestedValues.RemoveAll()

' if the user left the search box empty, we again fill it with all values available
If Search.IndexField.Value = "" Then
FillDropdowns()
Else
For Each p As Entity In persons
If p.UserPropertyExists("mail") And InStr(p.UserProperties("mail"), Me.Search.IndexField.Value) > 0 Then
Email.IndexField.SuggestedValues.Add(p.UserProperties("mail"))
End If
If p.UserPropertyExists("samaccountname") And InStr(p.UserProperties("samaccountname"), Me.Search.IndexField.Value) > 0 Then
Name.IndexField.SuggestedValues.Add(p.UserProperties("samaccountname"))
End If
Next
End If

End Sub

Finally, you have saved our users a lot of keystrokes, time and provided them with ease-of-use.