--- /dev/null
+Issue Tracker Plugins\r
+=====================\r
+\r
+What is this for?\r
+-----------------\r
+\r
+Imagine the following scenario:\r
+\r
+ 1. Marvin the manager assigns a ticket or task to Dave, the developer, using\r
+ the issue-tracking or task-management software.\r
+ 2. Dave looks at his list of tasks using the issue-tracking software's front\r
+ end (i.e. a web-dashboard). He picks something to work on this morning.\r
+ 3. Dave fires up Visual Studio; hacks on some code; does some work toward\r
+ the task.\r
+ 4. Dave brings up TSVN's commit dialog. He types in a comment that lists,\r
+ depending on coding standards:\r
+ a) The ticket/task number of what he's been working on.\r
+ b) Some comments about what he's changed.\r
+ 5. He clicks OK, and his changes are committed to the repository.\r
+ 6. The issue-tracking software monitors the SVN repository, watching for\r
+ commits.It sees Dave's commit message, extracts the information from it,\r
+ and updates the ticket appropriately.\r
+ 7. Marvin looks in the task-management software, and can monitor the\r
+ project's progress.\r
+\r
+As it stands, TSVN supports (most of) this admirably. For example, with the\r
+Trac integration, I can put "See #43" or "Fixes #99" somewhere in the commit\r
+message, and Trac's post-commit hook will update the tickets accordingly.\r
+\r
+However:\r
+ 1. Dave has to keep the issue-tracker front-end open in order to look up the\r
+ ticket numbers.\r
+ 2. Some issue trackers want more information (e.g. time spent, time\r
+ remaining), and it needs to be formatted more rigidly.\r
+\r
+So, the user story looks like this:\r
+\r
+ "From the TSVN commit dialog, the user should be able to display up a list of\r
+ assigned tickets from the issue-tracker associated with that repository. The\r
+ user should be able to pick one or more tasks from this list."\r
+ \r
+ "This will populate the commit message with the information required by the\r
+ issue-tracker's SVN server-side post-commit hook."\r
+\r
+ "This functionality should be available from the commit dialog, because the\r
+ user may wish to choose which files to commit based on which ticket they\r
+ select, or he may wish to refresh his memory about what's changed before\r
+ selecting the appropriate ticket."\r
+\r
+\r
+Implementing an issue tracker plugin\r
+------------------------------------\r
+\r
+To write an integration plugin, you implement the IBugTraqProvider COM\r
+interface, and register your object as implementing the "TortoiseSVN BugTraq\r
+Providers" component category. This registration makes it easy for the\r
+settings dialog to find a list of available plugins.\r
+\r
+The IBugTraqProvider interface is documented in the inc\IBugTraqProvider.idl\r
+file.\r
+\r
+The component category is defined (in C++) as follows:\r
+\r
+// {3494FA92-B139-4730-9591-01135D5E7831}\r
+DEFINE_GUID(CATID_BugTraqProvider, \r
+ 0x3494fa92, 0xb139, 0x4730, 0x95, 0x91, 0x1, 0x13, 0x5d, 0x5e, 0x78, 0x31);\r
+\r
+\r
+Example Plugins\r
+---------------\r
+\r
+There are two example plugins in this folder.\r
+\r
+ - ExampleAtlPlugin, written in C++, using ATL.\r
+ - ExampleCsPlugin, written in C#.\r
+ \r
+They get the list of available "issues" from a hard-coded list, rather than a\r
+database or web service, but this should be sufficient to demonstrate the\r
+plugin API.\r
+\r
+\r
+Licensing and GPL compatibility\r
+-------------------------------\r
+\r
+TortoiseSVN is licensed under the GNU General Public License (see the file\r
+LICENSE for details).\r
+\r
+There is a specific exception for plugins that implement the issue tracker\r
+plugin interfaces; these do not need to be GPL-licensed.\r
+\r
+\r
+The IBugTraqProvider interface\r
+------------------------------\r
+\r
+In the contrib\issue-tracker-plugins\inc directory, you'll find the following\r
+files:\r
+\r
+ * IBugTraqProvider.idl\r
+ This is a copy of the src\IBugTraqProvider\IBugTraqProvider.idl file; it's\r
+ provided for reference; you'll probably use the files below.\r
+ \r
+ * IBugTraqProvider_h.h, IBugTraqProvider_i.c\r
+ These are the files you'll probably use if you implement a plugin in C++.\r
+\r
+ * Interop.BugTraqProvider.dll\r
+ Interop Assembly for implementing plugins in .NET. It's not a Primary\r
+ Interop Assembly (PIA).\r
+ The source code for this project is in the Interop.BugTraqProvider folder.\r
+\r
+The interface is documented in the .IDL file.\r
+\r
+\r
+Walkthrough: Creating an issue tracker plugin in C#\r
+---------------------------------------------------\r
+\r
+(This assumes a basic familiarity with creating Windows Forms applications).\r
+\r
+In Visual Studio 2005 or 2008, create a new "Class Library" project; give it a\r
+name.\r
+\r
+Delete the "Class1" class; we'll create another one in a moment.\r
+\r
+Add a reference to the inc\Interop.BugTraqProvider.dll file.\r
+\r
+Create a new class named "MyPlugin".\r
+\r
+Derive MyPlugin from the Interop.BugTraqProvider.IBugTraqProvider interface,\r
+and then implement the first two methods as follows:\r
+\r
+The ValidateParameters method should look like this:\r
+\r
+ public bool ValidateParameters(IntPtr hParentWnd, string parameters)\r
+ {\r
+ return true;\r
+ }\r
+\r
+The GetLinkText method should look like this:\r
+\r
+ public string GetLinkText(IntPtr hParentWnd, string parameters)\r
+ {\r
+ return "Choose Issue";\r
+ }\r
+\r
+We'll come back to GetCommitMessage shortly.\r
+\r
+The class also needs some attributes; it should look like this:\r
+\r
+ [ComVisible(true),\r
+ Guid("PUT-GUID-HERE"),\r
+ ClassInterface(ClassInterfaceType.None)]\r
+ public class Provider : IBugTraqProvider\r
+ {\r
+ // etc.\r
+\r
+(Replace "PUT-GUID-HERE" with a new GUID).\r
+\r
+Add a class to hold our example ticket data:\r
+\r
+ internal class TicketItem\r
+ {\r
+ private readonly int _ticketNumber;\r
+ private readonly string _ticketSummary;\r
+\r
+ public TicketItem(int ticketNumber, string ticketSummary)\r
+ {\r
+ _ticketNumber = ticketNumber;\r
+ _ticketSummary = ticketSummary;\r
+ }\r
+\r
+ public int Number\r
+ {\r
+ get { return _ticketNumber; }\r
+ }\r
+\r
+ public string Summary\r
+ {\r
+ get { return _ticketSummary; }\r
+ }\r
+ }\r
+\r
+We can now implement the GetCommitMessage method as follows:\r
+\r
+ public string GetCommitMessage(IntPtr hParentWnd, string parameters, string commonRoot, string[] pathList, string originalMessage)\r
+ {\r
+ List<TicketItem> tickets = new List<TicketItem>();\r
+ tickets.Add(new TicketItem(12, "Service doesn't start on Windows Vista"));\r
+ tickets.Add(new TicketItem(19, "About box doesn't render correctly in large fonts mode"));\r
+\r
+ MyIssuesForm form = new MyIssuesForm(tickets);\r
+ if (form.ShowDialog() != DialogResult.OK)\r
+ return originalMessage;\r
+\r
+ StringBuilder result = new StringBuilder(originalMessage);\r
+ if (originalMessage.Length != 0 && !originalMessage.EndsWith("\n"))\r
+ result.AppendLine();\r
+\r
+ foreach (TicketItem ticket in form.TicketsFixed)\r
+ {\r
+ result.AppendFormat("Fixed #{0}: {1}", ticket.Number, ticket.Summary);\r
+ result.AppendLine();\r
+ }\r
+\r
+ return result.ToString();\r
+ }\r
+\r
+This passes the list of open issues to the MyIssuesForm object, where the\r
+user will be able to check those ones that she's fixed. These are available\r
+through the TicketsFixed property.\r
+\r
+We use these to build a string that looks something like this:\r
+\r
+Fixed #12: Service doesn't start on Windows Vista.\r
+\r
+A commit message formatted like this will cause, (e.g.) Trac's post-commit\r
+hook to close the tickets.\r
+\r
+Anything that the user has already entered into the commit message is left\r
+there.\r
+\r
+Now we need a dialog box that displays the issues assigned to the\r
+current user.\r
+\r
+Add a Windows Form item to your project. Name it "MyIssuesForm". Set the\r
+following properties:\r
+\r
+ StartPosition = CenterParent\r
+ MaximizeBox = False\r
+ MinimizeBox = False\r
+ ShowIcon = False\r
+ ShowInTaskbar = False\r
+\r
+Put the usual OK and Cancel buttons on it; arrange them and wire them up\r
+properly.\r
+\r
+Add a ListView control to the form and arrange it appropriately. Set the\r
+following properties:\r
+\r
+ Checkboxes = True\r
+ FullRowSelect = True\r
+ View = Details\r
+ HeaderStyle = Nonclickable\r
+\r
+Change the class so that it looks like this:\r
+\r
+ partial class MyIssuesForm : Form\r
+ {\r
+ private readonly IEnumerable<TicketItem> _tickets;\r
+ private readonly List<TicketItem> _ticketsAffected = new List<TicketItem>();\r
+\r
+ public MyIssuesForm(IEnumerable<TicketItem> tickets)\r
+ {\r
+ InitializeComponent();\r
+ _tickets = tickets;\r
+ }\r
+\r
+ public IEnumerable<TicketItem> TicketsFixed\r
+ {\r
+ get { return _ticketsAffected; }\r
+ }\r
+\r
+ // etc.\r
+\r
+Then implement some event handlers in MyIssuesForm as follows:\r
+\r
+ private void MyIssuesForm_Load(object sender, EventArgs e)\r
+ {\r
+ listView1.Columns.Add("");\r
+ listView1.Columns.Add("#");\r
+ listView1.Columns.Add("Summary");\r
+\r
+ foreach(TicketItem ticketItem in _tickets)\r
+ {\r
+ ListViewItem lvi = new ListViewItem();\r
+ lvi.Text = "";\r
+ lvi.SubItems.Add(ticketItem.Number.ToString());\r
+ lvi.SubItems.Add(ticketItem.Summary);\r
+ lvi.Tag = ticketItem;\r
+\r
+ listView1.Items.Add(lvi);\r
+ }\r
+\r
+ listView1.Columns[0].Width = -1;\r
+ listView1.Columns[1].Width = -1;\r
+ listView1.Columns[2].Width = -1;\r
+ }\r
+\r
+ private void okButton_Click(object sender, EventArgs e)\r
+ {\r
+ foreach (ListViewItem lvi in listView1.Items)\r
+ {\r
+ TicketItem ticketItem = lvi.Tag as TicketItem;\r
+ if (ticketItem != null && lvi.Checked)\r
+ _ticketsAffected.Add(ticketItem);\r
+ }\r
+ }\r
+\r
+Registering your new C# class can be done by using RegAsm from the command\r
+line, as follows:\r
+\r
+ RegAsm bin\Debug\MyCsPlugin.dll /codebase /regfile:MyCsPlugin.reg\r
+\r
+You'll need to edit the .REG file, by adding another "Implemented Categories"\r
+entry that looks like this:\r
+\r
+[HKEY_CLASSES_ROOT\CLSID\{PUT-GUID-HERE}\Implemented Categories\{3494FA92-B139-4730-9591-01135D5E7831}]\r
+\r
+Replace "PUT-GUID-HERE" with the same value you used earlier.\r
+\r
+Then, merge that .REG file into the registry, and your plugin is ready to go!\r