Uncategorized
We got a problem with implementing inner grids based on ExtJS. ExtJS GridPanel reacts on every event from inner GridPanel by default. For example sorting in nested grid causes the sorting in parent. That is not good. Find below the code which will allow to deny the event processing in GridPanel if it is fired in its child grid panel:
Ext.override(Ext.grid.GridPanel, {
processEvent: function(name, e) {
var t = e.getTarget();
if (!t) {
return;
}
if (!this.el) {
return;
}
if (jQuery('#' + this.el.id).find('.x-grid3').length > 1 && jQuery(t).parents('.x-grid3').length > 1) {
return;
}
this.fireEvent(name, e);
var v = this.view;
var header = v.findHeaderIndex(t);
if (header !== false) {
this.fireEvent("header" + name, this, header, e);
} else {
var row = v.findRowIndex(t);
var cell = v.findCellIndex(t);
if (row !== false) {
this.fireEvent("row" + name, this, row, e);
if (cell !== false) {
this.fireEvent("cell" + name, this, row, cell, e);
}
}
}
}
});
We are developing the new audit history mechanism. SQL triggers were added. They do the shadow copy of added/deleted/updated data in important tables such as user story, project. The problem is that we need the custom context in these triggers implementation such as logged user and client date. In other words we need to set some custom info into the connection session before any change.
The code below shows how to set and extract context on MS SQL side:
GO
CREATE PROCEDURE setTpCnt
@userID INT,
@clientDate DATETIME
AS
DECLARE @BinVar varbinary(128)
SET @BinVar = CAST(CAST(@userID as nvarchar(20)) +
'_' + CONVERT(nvarchar(100), @clientDate, 13)
+ '_' AS varbinary(128))
SET CONTEXT_INFO @BinVar
GO
CREATE FUNCTION f_GetLoggedUserID()
RETURNS INT
AS
BEGIN
DECLARE @CONTEXT AS NVARCHAR(120)
SET @CONTEXT = NULL
SELECT @CONTEXT = CAST(CONTEXT_INFO AS NVARCHAR(120))
FROM master.dbo.sysprocesses WHERE spid = @@spid
IF (@CONTEXT IS NULL)
RETURN NULL
RETURN CAST(SUBSTRING(@CONTEXT, 0, CHARINDEX('_', @CONTEXT)) as INT)
END
GO
CREATE FUNCTION f_GetClientTime()
RETURNS DATETIME
AS
BEGIN
DECLARE @PAD_INDEX AS INT
DECLARE @CONTEXT AS NVARCHAR(120)
SET @CONTEXT = NULL
SELECT @CONTEXT = CAST(CONTEXT_INFO AS NVARCHAR(120))
FROM master.dbo.sysprocesses WHERE spid = @@spid
IF (@CONTEXT IS NULL)
RETURN NULL
SET @PAD_INDEX = CHARINDEX('_', @CONTEXT)
SET @CONTEXT = SUBSTRING(@CONTEXT, @PAD_INDEX + 1, LEN(@CONTEXT) - @PAD_INDEX)
SET @CONTEXT = SUBSTRING(@CONTEXT, 0, CHARINDEX('_', @CONTEXT))
RETURN CONVERT(DATETIME, @CONTEXT, 13)
END
GO
We can do the following things with the procedure and functions above
- We can set the context using stored procedure setTpCnt
- We can get the logged user anywhere using function f_GetLoggedUserID
- We can get the client time using the function f_GetClientTime
Now we need to set the context from our client. Only our client knows the logged user id and the date. We need somehow to the call of stored procedure setTpCnt in our C# client. We are using NHibernate. So we need to figure out how to pass the custom context information into every connection which is created by NHibernate.
Please find the solution below. We created the custom driver for NHibernate to make a call to stored procedure with setting required value:
#region
using System;
using System.Data;
using System.Data.SqlClient;
using NHibernate.Driver;
using NHibernate.SqlCommand;
using NHibernate.SqlTypes;
using Tp.BusinessObjects.Components.Authentication;
#endregion
namespace Tp.BusinessObjects.Data
{
public class Driver : SqlClientDriver
{
private bool _isSecurityInjected;
public override IDbConnection CreateConnection()
{
var connection = base.CreateConnection();
_isSecurityInjected = false;
return connection;
}
public override IDbCommand GenerateCommand(CommandType type,
SqlString sqlString, SqlType[] parameterTypes)
{
var command = base.GenerateCommand(type, sqlString, parameterTypes);
if (_isSecurityInjected)
return command;
var commandText = command.CommandText;
if (string.IsNullOrEmpty(commandText))
return command;
if ((commandText.IndexOf("INSERT ",
StringComparison.InvariantCultureIgnoreCase) < 0)
&& (commandText.IndexOf("UPDATE ",
StringComparison.InvariantCultureIgnoreCase) < 0)
&& (commandText.IndexOf("DELETE ",
StringComparison.InvariantCultureIgnoreCase) < 0))
{
return command;
}
var userID = UserAuthentication.UserID;
if (userID != null)
{
_isSecurityInjected = true;
var text = @"EXEC setTpCnt @cnt_userID, @cnt_ClientDate" + Environment.NewLine;
command.CommandText = text + commandText;
command.Parameters.Add(new SqlParameter("@cnt_userID", userID));
command.Parameters.Add(new SqlParameter("@cnt_ClientDate", CurrentDate.Value));
}
return command;
}
}
}
Now we need to improve NHibernate configuration to include the driver created above. It should be done in the following way

Will keep you informed about other tricks (if have time for sure).
Follow us on Twitter.
Short comparison of several agile project management tools. I can’t resist to provide the quote :)
Even though it is [TargetProcess] a massive application somehow it feels very intuitive. Reports are easily customizable, processes can be changed to fit your style such as XP or SCRUM. You can comprehensively organize your stories into iterations by dragging and dropping. In-place editing almost everywhere allows for super quick updates. Overall the app seemed extremely configurable yet uncluttered. Also available is a public API, support for Subversion integration and a rich story card board that seems easier to use than Mingle’s.
Today we held one more retrospective meeting. First we evaluated some action items from previous meeting:
| Practice |
Result |
| Daily Code Demos: Each developer must spend ~15-30 minutes every day demonstrating his code to other developer. |
Development teem liked this practice and decided to keep it forever. |
| Coding Deadline: Two days before Iteration end all user stories that are not completed excluded from the iteration delivery and moved to the next iteration. |
Not sure whether it helped. Team decided to keep this practice for one more iteration to try. |
| Define Iteration scope and specs before iteration start. |
Good practice that allows to start iteration right away. Decided to keep it. |
The major discussion topic on this meeting was the product quality. Sometimes testing is not started when required. For example, developer completes the feature on Monday, but testing starts on Thursday. The problem is in communication between development and testing teams. Two practices will be tried in the next iteration:
Developer/Tester team for user story. Each user story will have a pair of developer and tester. It will be known who is responsible for the user story testing and development and make it clear for all team members. It should help to discuss user story for interested parties.
Developer-to-QA demo. When developer completes the user story it should demonstrate the functionality to the tester right away. It will clearly identity the moment when implementation is done and help to identify initial most critical problems early. Also developer should demonstrate ready pieces of functionality during daily code demos (if he has something new to show).
You have a project and several teams. You want to have a single project backlog and manage work separately for each development team. How can you do that in TargetProcess? The quick answer is “tags and bundles”, however some guidance will help to get the idea.
Tags and bundles is a way to categorize information. With creativity you may find very nice applications for this idea. Let’s try to see how it help to manage several teams’ work in scope of a single project.
First of all, we will create a bundle Teams and add tags Team A and Team B to the bundle. Team A is just a tag, people should know to what team they belong. Usually it is not a problem at all.

The next step is to assign user stories to teams. It can be done in several places. For example, you can go to User Stories list, mark several user stories and add “Team A” tag to these stories.

Alternative way is to use Tags Board. Navigate to General -> Tags Board screen and select Teams bundle. On the left you will see backlog of user stories that are not assigned to teams. On the right you will see two teams tags. All you need is to drag some user stories and drop them to required team.

As a result you will have backlog for each team. What about iteration planning? Navigate to Iteration Plan page and filter user stories by “Team A” tag if you want to see all user stories for Team A. Thus you may plan iteration for one specific team.

Potential problem is a team velocity. If you track overall velocity, it will work out of the box. To have a separate velocity for each team it is required to create parallel releases and iterations inside the project. Each team should have separate releases thread. For example, we create Release 1.A for Team A and Release 1.B for Team B. All releases will have the same Start/End dates (it is not a requirement, if you want you may have different dates).

Now you have iteration plan for both teams. All you need is progress tracking. First of all, user stories list itself give overview of the stories progress.

You may filter the list by Team A or Team B to see the progress specific for each team. Overall progress accessible in Release and Iteration Burn Down charts. However if you have parallel releases it is impossible to see combined burn down for all teams.
As you see, multiple teams support in TargetProcess is possible and quite painless.
TargetProcess v.2.10.4 has some problems reported. We are working on them and likely will release new build today. The list of known problems:
- Date custom field is not visible in lists.
- Exception in list if it is filtered by checkbox custom field.
- Sometimes javascript errors appear in lists (not reproduced yet, but several customers reported this issue).
UPD: bug with javascript reproduced and fixed, new build will be available today.
UPD2: v.2.10.7 released and contains all the fixes above.
Last week we had retrospective meeting. We have identified two major problems. We often unable to ship fully tested and complete build in the end of iteration. The main reason is that some user stories still in development even in the last iteration day, it means integration and acceptance tests will not be run. The other problem is knowledge transfer. Some developers are not willing to use pair programming all the time, the team decided to pair only for complex refactoring and development tasks.

We decided to try three new practices in the next iteration:
- Daily Code Demos: Each developer must spend ~15-30 minutes every day demonstrating his code to other developer. It is a kind of code review on a daily basis, it does not take much time and should improve awareness inside the team.
- Coding Deadline: Two days before Iteration end all user stories that are not completed excluded from the iteration delivery and moved to the next iteration. They must be hidden in UI and do not affect application in any way.
- Define Iteration scope and specs before iteration start. When iteration starts, the following must be ready: iteration scope clearly defined, all use stories in the iteration scope have brief description, iteration planing meeting is done. It will help to start iteration development from the day one.
Let’s see the results in two weeks :)
TargetProcess v.2.10 released yesterday. This release includes quite many features and was too long on my opinion. We will focus on shorter releases (about 6 weeks) starting from v.2.11.
The killer feature in v.2.10 is painless tags categorization. We’ve provided a framework that resolve many categorization problems, from Modules to several Teams in one project. It is possible to add tags to entities, filter and search by tags, see tags in lists and views. There is also exceptional Tags Board area where you can categorize entities easily.

The most important features in v.2.10 include:
- Tags and Bundles
- JIRA Integration
- Next/Prev navigation in views
- Convert entities from one type to another (for example, bug to user story)
- Import custom fields values
- Plugin: Auto-reply email when new request created
- Plugin: Bind emails/requests to a project based on a [key] in the subject
- Create custom field for several entities at once
- Feature is the assignable entity now (it has own workflow, assignments, appears in ToDo list, etc.)
- Help Desk Tickets
One of the latest trends in client-side development is the unobtrusive JavaScript. This trend is very good, since it extends separation of concerns. Several years ago the same happened with CSS. Web designers used FONT tag, BGCOLOR attribute and other visual style representation HTML elements extensively. Pages were bloated with style elements and style change was a real pain.
CSS changed that completely. It separated document structure from presentation and modern web designers likely do not know what FONT tag was designed for. While CSS separates presentation from document structure, unobtrusive JavaScript separates behavior from the structure.
There are quite many frameworks and techniques that enable unobtrusive JavaScript, but jQuery on my opinion is one of the best. It supports Unobtrusive JavaScript nicely and very elegant by itself.
| Inline JavaScript |
Unobtrusive JavaScript |
Have you rjavascript:void(0)
Publish Postead our
<a href="javascript:window.open(
'terms.html', 'popup',
'height=500,width=400,toolbar=no'
);">terms and conditions</a>?
|
Have you read our
<a href="terms.html"
class="sidenote" >terms and conditions</a>?
jQuery(function($) {
$('a.sidenote').click(function() {
var href = $(this).attr('href');
window.open(href, 'popup',
'height=500,width=400,toolbar=no');
return false;
});
});
|
You may decide that the second way is more verbose, but it does not matter in general. You will have all handlers in a single place, they will be quite similar and document structure will not suffer from tons of inline javascript.