Since there is not an executable for mklink, it took me a minute to figure a good solution to running mklink as a target in my build script.
The NAnt has an exec task that is used for running command line targets, but it requires an executable as its target. The command shell works in this situation, so I just need to exec the command shell, and have it execute mklink as its target.
Here is my NAnt target:
<exec program="cmd" commandline="/c mklink /d mobile\source\CustomControllers customizations\Controllers"/>
In Clarify, Logistics and Quality both use transitions to determine how a request moves from one condition to another. To change a condition, a transition must be defined between the two conditions for that part request type. To restrict access to critical part operations, you can administer permissions according to privilege classes.
In complex installations, the number of transitions across both business areas can easily reach the imposed limit of 256 transitions. This limitation can truly impose headaches when the transitions cannot be accurately configured to match the business process.
Dovetail Admin can also be used to configure the transitions, and now it does not enforce the same limitation. It does use the same mechanism for storage, so a little bit of work needs to be done to allow more transitions to be set up, and using Dovetail Agent (or the Dovetail SDK) to take advantage of the enhanced capability.
The limitation is actually caused by the size of the trans_mask field in table_privclass. Normally, transitions are ranked from 0 to 254, and this string field size is set to 255 characters in the baseline database. The use of a transition is stored for each privilege class is indicated by the corresponding character in this field according to the rank of the transition.
To allow for more transitions, the size of the trans_mask field in table_privclass has to be increased. For example, to double the number of transitions allowed, the field size would need to be changed to a string of 512 characters. This change in the schema can be easily accomplished using Dovetail SchemaEditor, by applying the following SchemaScript file to the database:
<schemaScript xmlns="http://www.dovetailsoftware.com/2006/10/SchemaScript.xsd">
<updateColumn name="trans_mask" table="privclass" >
<length>512</length>
</updateColumn>
</schemaScript>
Once this change is done, open Dovetail Admin and update the Schema cache.
With the new schema, Dovetail Admin can now be used to add the rest of the transitions required to match the business process. There will still be a message displayed when the maximum number of transitions is reached, but now you have the power to transition beyond this limitation.
In Dovetail Admin, there are a few pages where there is an option to check or uncheck a list of checkbox controls that are in an iframe. The Select All and Unselect All buttons perform the action, and using jQuery the codes becomes really simple and efficient.
The code that the buttons use is shared, and the true/false value is passed in to the function to check or uncheck the boxes.
The old code gets the iframe, gets all of the input fields, and then loops through all of the input fields. As it loops, if the field type is a checkbox, then the field’s checked attribute is set to the true/false value that was passed in.
Here is the original function:
function SetAllCheckBoxes(bOnOff) {
var the_iframe= document.getElementById('priv_classes');
var doc = the_iframe.contentWindow.document;
all_fields = doc.getElementsByTagName("input");
var element_count = all_fields.length;
for(i = 0;i < element_count; i++) {
objInput = all_fields
;
if (objInput.getAttribute('type') == 'checkbox') {
objInput.checked=bOnOff;
}
}
}
Using jQuery, this code is reduced to one simple line:
$("#priv_classes").contents().find("input:checkbox").attr("checked", bOnOff);
jQuery selects the iframe by its id tag, and gets all of the checkbox input controls from the iframe content, and sets the checked attribute on each one.
Once again, jQuery makes javascript simple, accurate, and easy to maintain.
One of our core products at Dovetail Software is our classic ASP application, Dovetail Agent. This application has been in production for many years, and is very stable and reliable. However, since it is a classic ASP application, there is a lot of room for improvement.
I recently upgraded the Notification feature of the application. Dovetail Agent is a web application that has a main page (the console), and notifications need to be delivered to users automatically, without the users having to find them manually.
In the previous releases of Dovetail Agent, this process used an iframe control on the console. The source page for the iframe control would query the database for new messages. If new messages were found, a hyperlink would be displayed on the console, and a window for the notifications was opened. The source page for the iframe control used a meta tag to make it automatically refresh after a number of seconds (x_notify_frequency - set by the user).
<meta http-equiv="refresh" content="<%=x_notify_frequency%>;url="check_notifier.asp">
The first change was to replace this iframe and meta-refresh with an AJAX call, which is set to repeat. I am a big fan of jQuery, so I added a few lines of code to set up the timer and make it call the AJAX function.
$(document).ready(function() {
$.timer(<%=x_notify_frequency%>, function (timer) {
checkNotifier(timer);
});
});
This uses the jQuery timer plugin, which makes it easy to set the frequency and target for the recurring event. The timer plugin uses the standard javascript methods setInterval() and clearInterval(), but makes them shorter and easier to use.
The checkNotifier() function sets up the jQuery AJAX call and evaluates the result. If successful and new messages were found, a hyperlink is displayed on the console, and a window for the notifications is opened. If an error occurs, the recurring event is stopped, and an alert is displayed to the user.
function checkNotifier(timer) {
var strURL = strAppPath + "notifier/ajax_notifier.asp";
$.ajax({
type: "POST",
url: strURL,
dataType: "json",
success: function(results){
if(results.success == true) {
if(results.messages > 0) {
$("#notifier_message").text("New Notifier Messages").css("color", "<%=x_notify_color1%>");
if(<%=x_notify_immediate%> == 1) {
OpenNotifierWindow();
}
} else {
$("#notifier_message").empty();
}
} else {
alert("An error occurred while checking for Notifications: " + results.errorMessage);
timer.stop();
}
},
error: function(xhr) {
alert("An error occurred while checking for Notifications");
timer.stop();
}
});
}
The success actions were previously being done by the iframe source page, and it was updating the iframe’s parent window (the console) controls.
Now the iframe and its surrounding div are completely removed. Making the updates directly from the console is much more reliable than referring to a parent window. After writing this post I realize it doesn’t sound like a huge change, but it does make the application easier to maintain.
Dovetail SDK is a software development kit for writing applications, using .NET, to access Clarify™ databases. One of the more common requirements for objects created for Clarify is performing workflow operations on those objects, such as assigning cases or dispatching change requests. Dovetail SDK provides a powerful set of tools to make workflow operations easy to implement. This guide explains how to get started.
Using WorkflowManager for Workflow Actions
Dovetail SDK supports the following Workflow Methods for Queueable Objects in Clarify with the WorkflowManager object:
- Accept
- Assign
- Dispatch
- Forward
- Move
- Reject
- Yank
- ChangeStatus
Queueable Object Types that are currently supported:
CASE, SUBCASE, DEMAND_DTL, TASK, OPPORTUNITY, CONTRACT, BUG, PROBDESC, DIALOGUE
All of these methods follow the same basic model for parameters. For example, the Dispatch method takes
- id number of the object being dispatched
- type of object being dispatched
- queue that the object is being dispatched to
- time that the dispatch occurred
- login name of the user performing the dispatch
- boolean indicating to generate time bombs
Here is the signature for the Dispatch method:
public WorkFlowInfo Dispatch(string idNum, string objectName, string queueName, DateTime dispatchDate, string userName, bool generateTimeBomb)
Examples of using WorkflowManager:
The following examples demonstrate use of WorkflowManager APIs. The first example dispatches the case to the queue, and the second example accepts the case into the user’s wipbin.
[C#]
// Dispatch Case 1 to the Support queue
string caseId = "1";
string queueName = "Support";
string userName = "sa";
bool generateTimeBombs = true;
DateTime eventTime = DateTime.Now;
ClarifyDataSet ds = new ClarifyDataSet(session);
WorkflowManager workflowManager = new WorkflowManager(ds);
workflowManager.Dispatch(caseId, "case", queueName, eventTime, userName, generateTimeBombs);
// Accept Case 1 into the default wipbin
string caseId = "1";
string wipbinTitle = "default";
string userName = "sa";
bool generateTimeBombs = true;
DateTime eventTime = DateTime.Now;
ClarifyDataSet ds = new ClarifyDataSet(session);
WorkflowManager workflowManager = new WorkflowManager(ds);
workflowManager.Accept(caseId, "case", wipbinTitle, eventTime, userName, generateTimeBombs);
Using .Net Version of Compatibility Toolkits for Workflow Actions
Dovetail SDK also support workflow methods for queueable objects through the Compatibility Toolkits. These APIs are grouped by the toolkit which govern the objects primary functions. For example, the Case workflow APIs are found in the Support Toolkit. At this time, all of the queueable objects except Dialogue have compatibility versions of the workflow APIs.
Since the Compatibility Toolkits were originally developed in VB, all of the Compatibility Toolkit APIs are also provided for use in .Net applications. These APIs have names similar to the VB versions. Each one has a name with the pattern of “<Action><Object>”, i.e. DispatchCase. These APIs have a different signature, and all use a matching setup object, i.e. DispatchCaseSetup. The setup objects are created first, and have parameters for each required property.
Looking at the DispatchCase API, the only required properties are the case id number and the queue name. Correspondingly, the setup object only has two parameters:
[C#]
DispatchCaseSetup( string caseIdNum, string queue )
Once the setup object is created, then the other properties can also be set. This allows less code to be required to perform the operation, which makes it easier to use. It also allows the API to be easily modified if necessary. Here is an example of setting the optional properties:
[C#]
string caseId = "1";
string queueName = "Support";
DispatchCaseSetup setup = new DispatchCaseSetup(caseId, queueName);
setup.DispatchDate = DateTime.Now;
setup.UserName = "sa";
setup.GenerateTimeBombs = false;
At this point, the setup object is ready for use. The corresponding API uses its setup object as input, and the API is invoked:
[C#]
SupportToolkit supportToolkit = new SupportToolkit(fcApplication, fcSession);
ToolkitResult result = supportToolkit.DispatchCase(setup);
The ToolkitResult will have all of the details from executing the API. Any error code would be in the ReturnCode property.
[C#]
int returnCode = result.ReturnCode;
Summary
The APIs for the workflow events can be implemented in multiple environments. Dovetail SDK executes the same code to perform a workflow action, regardless of how the API is called. All of the necessary validation of the input parameters is done, and the appropriate activity logs and time bombs will be generated as required.
I needed to add a new feature today, and wanted an easy client-side solution. Since I use jQuery in the project already, I searched and found a good starting point in a blog post by Bill Richards.
The code I found adds a function and a sort handler that can be applied to a list.
jQuery.fn.sort = function() {
return this.pushStack( [].sort.apply( this, arguments ), []);
};
function sortAlpha(a,b){
return a.innerHTML > b.innerHTML ? 1 : -1;
};
Here is a simple example of the HTML I am working with:
<ul id="caseListing">
<li>
<div id="1">
<a href="/Cases/Show/1">Case 1</a>: need help with VPN connection
</div>
</li>
<li>
<div id="3">
<a href="/Cases/Show/3">Case 3</a>: blue screen of death upon login
</div>
</li>
<li>
<div id="6">
<a href="/Cases/Show/6">Case 6</a>: Computer has no power
</div>
</li>
</ul>
When the page loads, all of the list items are sorted by the id number of the case. Since I need to be able to inverse the order of a list of items after it is displayed, I added this anchor element to change the sort order:
<a id="sortOrder" href="">Sort Desc</a>
For the javascript required, I added another sort handler for reversing the order, and added code for the hyperlink to initiate the sort function. The jQuery code I end up with looks like this:
<script type="text/javascript">
jQuery.fn.sort = function() {
return this.pushStack([].sort.apply(this, arguments), []);
};
function sortAscending(a, b) {
return a.innerHTML > b.innerHTML ? 1 : -1;
};
function sortDescending(a, b) {
return a.innerHTML < b.innerHTML ? 1 : -1;
};
$(document).ready(function() {
$("#sortOrder").toggle(
function() {
$('#caseListing li').sort(sortDescending).appendTo('#caseListing');
$(this).text("Sort Asc");
},
function() {
$('#caseListing li').sort(sortAscending).appendTo('#caseListing');
$(this).text("Sort Desc");
});
});
</script>
Each time the anchor tag is clicked, the order of the list elements is reversed. The toggle event handler for the anchor element does most of the work. It calls the correct sort handler, and updates the text of the anchor tag to keep it clear for the user.
This ends up being a very light-weight, fast client side sorting solution for small sets of list items.
Technorati Tags:
jQuery,
sort()
In the Dovetail SDK, there is a very useful SqlHelper class. SqlHelper provides a convenience and data provider-agnostic wrapper around ADO.NET. This allows an application to execute SQL commands in a database.
SqlHelper has various ways of being initialized, which all depend on a DbProvider instance to make a connection to the database. In most applications the DbProvider is separately instantiated based on configuration, so the examples here will not detail that process.
SqlHelper can execute a variety of SQL commands. SQL SELECT statements are usually performed by the ExecuteDataSet method. The ExecuteNonQuery method can be used to perform other SQL commands such as UPDATE or DELETE statements. ExecuteScalar is used frequently to get a value returned from a SQL statement for use later in the application.
In the first example, a simple database select statement is being executed to retrieve an ADO dataset. The dataset is then processed, writing the value from each of the two columns for each row in the first table in the dataset to the console. If no rows were retrieved, then nothing would be written to the console.
public void SqlHelper_example()
{
var sqlCommand = "select title, id_number from table_subcase";
var sqlHelper = new SqlHelper(_dbProvider, sqlCommand);
var dataset = sqlHelper.ExecuteDataSet();
var table = dataset.Tables[0];
foreach (DataRow row in table.Rows)
{
var subcaseInfo = String.Format("Subcase {0}: {1}", row["id_number"], row["title"]);
Console.WriteLine(subcaseInfo);
}
}
In this example, a database DELETE statement is being executed using a static SqlHelper instance to delete rows from the notes_log table.
public void SqlHelper_example()
{
SqlHelper.ExecuteNonQuery(“DELETE from table_notes_log WHERE internal = ‘-666’”);
}
Finally, here is an example using ExecuteScalar. The SQL SELECT statement will return the requested identifier, which is converted to an integer.
public void SqlHelper_example()
{
int caseObjid = Convert.ToInt32(SqlHelper.ExecuteScalar(“SELECT objid from table_case WHERE id_number = ‘1’”));
}
In reality, these examples just scratched the surface of possibilities for the uses of SqlHelper. The online documentation for SqlHelper provides a list if all the available methods, and putting them to use in applications gets easier with experience, and good examples.
Please add other examples that you have implemented in your comments, and we can all discuss!
The abilities to have global users and a centralized server is an important feature of Dovetail Agent. Making localized date handling for all of the users is an important part of getting the right date format displayed to the users, and making sure the dates are stored correctly in the database.
Dovetail Agent recognizes the computer and browser settings for each user. Dovetail Agent allows each user to input dates in their localized date format, and relies on Dovetail SDK to convert the dates into a consistent format for storage in the database.
Regional Options
From the Control Panel for each client, the Regional Options tab of the Regional and Language Options has a big impact on Dovetail Agent functionality. Dovetail Agent uses the standard Visual Basic FormatDate routine, which uses the Regional settings to determine the client's preferred date format.
For this discussion, the client is located in the Bristol, England office. His regional setting is set to English (United Kingdom).
Language Preferences
More importantly though, in order for Dovetail SDK to recognize the localized date formats and make the necessary adjustments, the browser needs to have the Language setting in IE correspond to the user's Regional Options setting.
From the Tools Menu in Internet Explorer, click Internet Options, then click on the Languages button to open the Language Preference dialog.
Since our user is located in the Bristol, England office, the English (United Kingdom) [en-GB] needs to be selected. If is is not displayed, click the Add… button and select it from the list. Once it is shown, move the English (United Kingdom) [en-GB] line to the top of the list by clicking the row and using the Move Up button.

Verifying in Dovetail Agent
Have the user log in to Dovetail Agent, and open the About Dovetail Agent page from the Profile menu. The Current Culture is displayed at the bottom of the page, and should now show en-GB:

After these configuration changes are made, dates throughout Dovetail Agent are displayed to the UK user in DD/MM/YYY format, but saved to the database in MM/DD/YYYY format.
Summary
Having dates localized is important for ease of use for the users, and is pretty easy to set up.
jQuery just released its latest version, and celebrates its 3rd birthday as a JavaScript Library.
The big features of this release are:
- Sizzle: A sizzlin’ hot CSS selector engine.
- Live Events: Event delegation with a jQuery twist.
- jQuery Event Overhaul: Completely rewired to simplify event handling.
- HTML Injection Rewrite: Lightning-fast HTML appending.
- Offset Rewrite: Super-quick position calculation.
- No More Browser Sniffing: Using feature detection to help jQuery last for many more years to come.
jQuery at Dovetail Software
Almost all of our current development projects are using jQuery, so upgrading to the new version to take advantage of the new features and performance will be an exciting improvement. The performance gains in the selector engine and HTML injection should help us improve our software. The benefits of using jQuery is already apparent, and we continue to use it more every day in development and maintenance of our software.
jQuery API browsers
One of the best development aids for jQuery is Visual jQuery. It shows the syntax and definition for every jQuery method available, in a very easy-to-use interface. Remy Sharp just released a new jQuery API browser, and this one is available offline as well as an Adobe AIR application.
Download and install the AIR API browser
The new API browser includes the following features:
- All the latest jQuery and jQuery UI documentation
- The ability to mark pages as favorites for those pages you keep wanting to return to
- Syntax highlighting in the code examples
- Live running of examples within the browser
- Links to edit and experiment with the code examples

This is always a good sign for a software company, and here at Dovetail Software this has just become an easier task.
It is important for us to get the news about a new product release to our customers. By automating some of the steps in this task, we can get the information to the customer faster, and also add some business value to the process.
Now when a new release is ready, we can use the new Product Announcement tool to prepare the message for our customers.
Opening this application from within our in-house version of Dovetail Agent, we can see a list of all of our products that are currently under contract with any of our customers.
Clicking on one of the products will retrieve all of the customers under contract, and show a second grid filled with the contact information for those sites. Each contact that has an email address will be selected, and those without an email address are highlighted differently.
The contact information can be updated by double-clicking the contact's row in the grid. Once the changes are saved for the contact, then the contact grid can be refreshed by clicking on the product again.
Each available email address is added to the BCC list, and the product details are filled into the subject and email body text. All of these text areas can be changed before the email is sent.
The body of the email is stored in the database, so the default template can be changed from within Dovetail Agent, instead of having to change to source code of the Product Announcement application.
When all of the changes are complete, the email can be sent. We send the message using Outlook, so clicking the Send Email button is the last step. It really makes it easy for us to get the word out to our customers.
Inside the Code
Inside the source code there a few interesting things that show how our applications are evolving as we develop new applications and features in our software.
The Product Announcement application has a built-in Help Page, accessible by clicking on the Help link at the top of the page. We use JavaScript to show the help contents directly in the page, so the focus of the user is not taken somewhere else.
Table Sorting
Both of the grids use the jQuery tableSorter plugin. This is a very simple way to add sorting for all of the columns in the grid. Here is the code that adds sorting for the customer grid, which adds the option of making the first column non-sortable.
$(".tablesorter").tablesorter({
headers: {
0: {
sorter: false
}
},
sortList: [[1,0]]
});
Tracking Announcement Activity
When the emails are sent, we also add an activity log for each contact, and one for the product itself. This allows us to track when the announcements were sent to the customers. To do this we are using AJAX through jQuery to add these activity entries for each customer and part.
$.ajax({
url: "addActivityLogs.asp",
type: "POST",
data: "site_part_list=" + site_part_list + "&product=" + escape(product) + "&contact_list=" + contact_list,
error: function(xhr) {
alert("Failed to add Activity Logs for " + product);
}
});
The addActivityLogs.asp code that gets executed by the AJAX call loops through the contacts and site parts, and uses the Dovetail Software SDK to create the new records for the database.
Better Layout and Style
All of the HTML layout and styling for the Product Announcement application is done using CSS. This is very different from the previous versions of Dovetail Agent. Dovetail Agent and Dovetail Admin will both be improving in this area as well in future releases.
This makes it easier to develop new applications, and much easier to change when necessary.
Using Outlook
Creating and sending emails through Outlook is very easy to do. First, when the the Product Announcement application is opened, we establish a connection to Outlook.
var objSession = new ActiveXObject("MAPI.Session");
objSession.Logon("","",true,false);
To create and send the email, we create the new Message with the subject and text, add the recipients to the BCC list and send!
var objMessage = objSession.Outbox.Messages.Add;
var objRecipients = objMessage.Recipients;
objMessage.Subject = $("#txtSubject").val();
objMessage.Text = $("#txtBody").val();
var bcc = new String($("#txtBCC").val()).split(";");
for(var i=0; i < bcc.length; i++) {
email = bcc
;
if(email > "") {
objRecip = objMessage.Recipients.Add();
objRecip.Name = email;
objRecip.Type = 3;
objRecip.Resolve();
}
}
// Send the Message
try{
objMessage.Send();
} catch (e){
objMessage = null;
objRecipients = null;
alert("Unexpected error:\n\n" + e.description + "\nError Number: " + e.number);
return false;
}
$("#sendButton").attr("disabled", true);
alert("Email successfully sent.");
You may notice that we use jQuery to interact with the page. This simplifies the JavaScript code required to get the subject, body, and recipients. We also disable the Send Email button after completion.
Summary
As we develop new applications, we also improve our techniques. Hopefully these examples will help you improve your techniques as well. Comments/Ideas/Suggestions are always welcome.