1 Issue Tracker Plugins
\r
2 =====================
\r
7 Imagine the following scenario:
\r
9 1. Marvin the manager assigns a ticket or task to Dave, the developer, using
\r
10 the issue-tracking or task-management software.
\r
11 2. Dave looks at his list of tasks using the issue-tracking software's front
\r
12 end (i.e. a web-dashboard). He picks something to work on this morning.
\r
13 3. Dave fires up Visual Studio; hacks on some code; does some work toward
\r
15 4. Dave brings up TSVN's commit dialog. He types in a comment that lists,
\r
16 depending on coding standards:
\r
17 a) The ticket/task number of what he's been working on.
\r
18 b) Some comments about what he's changed.
\r
19 5. He clicks OK, and his changes are committed to the repository.
\r
20 6. The issue-tracking software monitors the SVN repository, watching for
\r
21 commits.It sees Dave's commit message, extracts the information from it,
\r
22 and updates the ticket appropriately.
\r
23 7. Marvin looks in the task-management software, and can monitor the
\r
26 As it stands, TSVN supports (most of) this admirably. For example, with the
\r
27 Trac integration, I can put "See #43" or "Fixes #99" somewhere in the commit
\r
28 message, and Trac's post-commit hook will update the tickets accordingly.
\r
31 1. Dave has to keep the issue-tracker front-end open in order to look up the
\r
33 2. Some issue trackers want more information (e.g. time spent, time
\r
34 remaining), and it needs to be formatted more rigidly.
\r
36 So, the user story looks like this:
\r
38 "From the TSVN commit dialog, the user should be able to display up a list of
\r
39 assigned tickets from the issue-tracker associated with that repository. The
\r
40 user should be able to pick one or more tasks from this list."
\r
42 "This will populate the commit message with the information required by the
\r
43 issue-tracker's SVN server-side post-commit hook."
\r
45 "This functionality should be available from the commit dialog, because the
\r
46 user may wish to choose which files to commit based on which ticket they
\r
47 select, or he may wish to refresh his memory about what's changed before
\r
48 selecting the appropriate ticket."
\r
51 Implementing an issue tracker plugin
\r
52 ------------------------------------
\r
54 To write an integration plugin, you implement the IBugTraqProvider COM
\r
55 interface, and register your object as implementing the "TortoiseSVN BugTraq
\r
56 Providers" component category. This registration makes it easy for the
\r
57 settings dialog to find a list of available plugins.
\r
59 The IBugTraqProvider interface is documented in the inc\IBugTraqProvider.idl
\r
62 The component category is defined (in C++) as follows:
\r
64 // {3494FA92-B139-4730-9591-01135D5E7831}
\r
65 DEFINE_GUID(CATID_BugTraqProvider,
\r
66 0x3494fa92, 0xb139, 0x4730, 0x95, 0x91, 0x1, 0x13, 0x5d, 0x5e, 0x78, 0x31);
\r
72 There are two example plugins in this folder.
\r
74 - ExampleAtlPlugin, written in C++, using ATL.
\r
75 - ExampleCsPlugin, written in C#.
\r
77 They get the list of available "issues" from a hard-coded list, rather than a
\r
78 database or web service, but this should be sufficient to demonstrate the
\r
82 Licensing and GPL compatibility
\r
83 -------------------------------
\r
85 TortoiseSVN is licensed under the GNU General Public License (see the file
\r
86 LICENSE for details).
\r
88 There is a specific exception for plugins that implement the issue tracker
\r
89 plugin interfaces; these do not need to be GPL-licensed.
\r
92 The IBugTraqProvider interface
\r
93 ------------------------------
\r
95 In the contrib\issue-tracker-plugins\inc directory, you'll find the following
\r
98 * IBugTraqProvider.idl
\r
99 This is a copy of the src\IBugTraqProvider\IBugTraqProvider.idl file; it's
\r
100 provided for reference; you'll probably use the files below.
\r
102 * IBugTraqProvider_h.h, IBugTraqProvider_i.c
\r
103 These are the files you'll probably use if you implement a plugin in C++.
\r
105 * Interop.BugTraqProvider.dll
\r
106 Interop Assembly for implementing plugins in .NET. It's not a Primary
\r
107 Interop Assembly (PIA).
\r
108 The source code for this project is in the Interop.BugTraqProvider folder.
\r
110 The interface is documented in the .IDL file.
\r
113 Walkthrough: Creating an issue tracker plugin in C#
\r
114 ---------------------------------------------------
\r
116 (This assumes a basic familiarity with creating Windows Forms applications).
\r
118 In Visual Studio 2005 or 2008, create a new "Class Library" project; give it a
\r
121 Delete the "Class1" class; we'll create another one in a moment.
\r
123 Add a reference to the inc\Interop.BugTraqProvider.dll file.
\r
125 Create a new class named "MyPlugin".
\r
127 Derive MyPlugin from the Interop.BugTraqProvider.IBugTraqProvider interface,
\r
128 and then implement the first two methods as follows:
\r
130 The ValidateParameters method should look like this:
\r
132 public bool ValidateParameters(IntPtr hParentWnd, string parameters)
\r
137 The GetLinkText method should look like this:
\r
139 public string GetLinkText(IntPtr hParentWnd, string parameters)
\r
141 return "Choose Issue";
\r
144 We'll come back to GetCommitMessage shortly.
\r
146 The class also needs some attributes; it should look like this:
\r
149 Guid("PUT-GUID-HERE"),
\r
150 ClassInterface(ClassInterfaceType.None)]
\r
151 public class Provider : IBugTraqProvider
\r
155 (Replace "PUT-GUID-HERE" with a new GUID).
\r
157 Add a class to hold our example ticket data:
\r
159 internal class TicketItem
\r
161 private readonly int _ticketNumber;
\r
162 private readonly string _ticketSummary;
\r
164 public TicketItem(int ticketNumber, string ticketSummary)
\r
166 _ticketNumber = ticketNumber;
\r
167 _ticketSummary = ticketSummary;
\r
172 get { return _ticketNumber; }
\r
175 public string Summary
\r
177 get { return _ticketSummary; }
\r
181 We can now implement the GetCommitMessage method as follows:
\r
183 public string GetCommitMessage(IntPtr hParentWnd, string parameters, string commonRoot, string[] pathList, string originalMessage)
\r
185 List<TicketItem> tickets = new List<TicketItem>();
\r
186 tickets.Add(new TicketItem(12, "Service doesn't start on Windows Vista"));
\r
187 tickets.Add(new TicketItem(19, "About box doesn't render correctly in large fonts mode"));
\r
189 MyIssuesForm form = new MyIssuesForm(tickets);
\r
190 if (form.ShowDialog() != DialogResult.OK)
\r
191 return originalMessage;
\r
193 StringBuilder result = new StringBuilder(originalMessage);
\r
194 if (originalMessage.Length != 0 && !originalMessage.EndsWith("\n"))
\r
195 result.AppendLine();
\r
197 foreach (TicketItem ticket in form.TicketsFixed)
\r
199 result.AppendFormat("Fixed #{0}: {1}", ticket.Number, ticket.Summary);
\r
200 result.AppendLine();
\r
203 return result.ToString();
\r
206 This passes the list of open issues to the MyIssuesForm object, where the
\r
207 user will be able to check those ones that she's fixed. These are available
\r
208 through the TicketsFixed property.
\r
210 We use these to build a string that looks something like this:
\r
212 Fixed #12: Service doesn't start on Windows Vista.
\r
214 A commit message formatted like this will cause, (e.g.) Trac's post-commit
\r
215 hook to close the tickets.
\r
217 Anything that the user has already entered into the commit message is left
\r
220 Now we need a dialog box that displays the issues assigned to the
\r
223 Add a Windows Form item to your project. Name it "MyIssuesForm". Set the
\r
224 following properties:
\r
226 StartPosition = CenterParent
\r
227 MaximizeBox = False
\r
228 MinimizeBox = False
\r
230 ShowInTaskbar = False
\r
232 Put the usual OK and Cancel buttons on it; arrange them and wire them up
\r
235 Add a ListView control to the form and arrange it appropriately. Set the
\r
236 following properties:
\r
239 FullRowSelect = True
\r
241 HeaderStyle = Nonclickable
\r
243 Change the class so that it looks like this:
\r
245 partial class MyIssuesForm : Form
\r
247 private readonly IEnumerable<TicketItem> _tickets;
\r
248 private readonly List<TicketItem> _ticketsAffected = new List<TicketItem>();
\r
250 public MyIssuesForm(IEnumerable<TicketItem> tickets)
\r
252 InitializeComponent();
\r
253 _tickets = tickets;
\r
256 public IEnumerable<TicketItem> TicketsFixed
\r
258 get { return _ticketsAffected; }
\r
263 Then implement some event handlers in MyIssuesForm as follows:
\r
265 private void MyIssuesForm_Load(object sender, EventArgs e)
\r
267 listView1.Columns.Add("");
\r
268 listView1.Columns.Add("#");
\r
269 listView1.Columns.Add("Summary");
\r
271 foreach(TicketItem ticketItem in _tickets)
\r
273 ListViewItem lvi = new ListViewItem();
\r
275 lvi.SubItems.Add(ticketItem.Number.ToString());
\r
276 lvi.SubItems.Add(ticketItem.Summary);
\r
277 lvi.Tag = ticketItem;
\r
279 listView1.Items.Add(lvi);
\r
282 listView1.Columns[0].Width = -1;
\r
283 listView1.Columns[1].Width = -1;
\r
284 listView1.Columns[2].Width = -1;
\r
287 private void okButton_Click(object sender, EventArgs e)
\r
289 foreach (ListViewItem lvi in listView1.Items)
\r
291 TicketItem ticketItem = lvi.Tag as TicketItem;
\r
292 if (ticketItem != null && lvi.Checked)
\r
293 _ticketsAffected.Add(ticketItem);
\r
297 Registering your new C# class can be done by using RegAsm from the command
\r
300 RegAsm bin\Debug\MyCsPlugin.dll /codebase /regfile:MyCsPlugin.reg
\r
302 You'll need to edit the .REG file, by adding another "Implemented Categories"
\r
303 entry that looks like this:
\r
305 [HKEY_CLASSES_ROOT\CLSID\{PUT-GUID-HERE}\Implemented Categories\{3494FA92-B139-4730-9591-01135D5E7831}]
\r
307 Replace "PUT-GUID-HERE" with the same value you used earlier.
\r
309 Then, merge that .REG file into the registry, and your plugin is ready to go!
\r