Introduction
JetBrains' YouTrack issue tracker is a fantastic tool. Here are 2 Microsoft Office Add-ins to facilitate the work of the support people who receive e-mails that have to be entered in YouTrack or the project manager who does not want to synchronize manually YouTrack issues and Microsoft Project Tasks.
Using the Code
Warnings
- The code of the sample project is unfortunately not great: I'm now IT Manager and don't have the luxury to make it perfect. Please accept all my apologies for the hard-coded bits ;-)
- The Outlook Add-in was created using the VSTO for Office 10. The Microsoft Project Add-in uses VSTO for Office 13.
- This code contained in the zip file won't probably even compile in your environment. It is intended to help you reduce the waste of time that involves understanding 2 world-apart technologies. Therefore, please take it as a copy-paste source!
- The Microsoft Project Add-in assumes that you are using LDAP /AD integration on both sides.
- YouTrack services are provided by the YouTrackSharp project from Hadi Hariri (many, many thanks to him). Unfortunately, this project and its dependencies are not Strongly Signed which lead me to get the sources and include them in the attached solution for signing.
- I did not manage to use the "Attachment" feature of YouTrackSharp. So I implemented a work around (leading to use mixed technics in the same project) which I'm not particularly proud of...
Outlook Add-in
- Create a project for Outlook
- Add a
Ribbon
and change the property OfficeRibbon.RibbonType
for Microsoft.Outlook.Mail.Read
. - Change the property of the
Ribbon RibbonTab.ControlId.ControlIdType
for Office and RibbonTab.ControlId.OfficeId
for TabReadMessage
. - To create a YouTrack Issue from a Mail, you can do the following:
First, add a Button
to the Ribbon
and implement the Click
event:
using Microsoft.Office.Interop.MSProject;
using Microsoft.Office.Tools.Ribbon;
Then, store the elements of the Mail
into a MailItem
object:
Application application = Globals.ThisAddIn.Application;
Inspector inspector = application.ActiveInspector();
MailItem myMailItem = (MailItem) inspector.CurrentItem;
string mailSubject = myMailItem.Subject;
string mailBody = myMailItem.Body;
IEnumerable<Attachment> attachments = myMailItem.Attachments.Cast<Attachment>();
string localTempDirectory = string.Concat(Environment.GetFolderPath
(Environment.SpecialFolder.ApplicationData),
@"\TempYouTrackAttachments\",
Guid.NewGuid(),
"\\");
Directory.CreateDirectory(localTempDirectory);
List<MailAttachment> mailAttachments = new List<MailAttachment>();
foreach (Attachment attachment in attachments)
{
MailAttachment mailAttachment = new MailAttachment();
mailAttachments.Add(mailAttachment);
mailAttachment.DisplayName = attachment.DisplayName;
mailAttachment.FilePath = string.Concat(localTempDirectory, attachment.FileName);
attachment.SaveAsFile(mailAttachment.FilePath);
}
MailAttachment mail = new MailAttachment();
mailAttachments.Add(mail);
mail.DisplayName = "Mail";
mail.FilePath = string.Concat(localTempDirectory, "Mail.msg");
myMailItem.SaveAs(mail.FilePath);
Finally, call the user interface that will allow you to define your new YouTrack Issue:
IConnection connection = new Connection("[YouTrack server]", 80);
ProjectManagement projectManagement = new ProjectManagement(connection);
IssueManagement issueManagement = new IssueManagement(connection);
IssueCreationViewModel viewModel = new IssueCreationViewModel
(projectManagement, issueManagement, mailSubject, mailBody, mailAttachments);
IssueCreationView view = new IssueCreationView(); view.ViewModel = viewModel; view.ShowDialog();
Once the user interface closes, we'll delete the temporary directory containing the attachments:
Directory.Delete(localTempDirectory, true);
- The user interface is initialized as such:
Projects = _projectManagement.GetProjects();
IssueTypes = _projectManagement.GetIssueTypes();
Priorities = _projectManagement.GetPriorities();
SelectedIssueType = IssueTypes.First();
SelectedPriority = Priorities.First(p => p.Name == "Normal");
Note that the Assignees depend on the Project. Therefore it is queried each time the selected project changes.
- This is how we create the new Issue:
dynamic issue = new Issue();
if (SelectedAssignee != null)
issue.Assignee = SelectedAssignee.Login;
issue.Summary = Summary;
issue.Description = Description;
issue.ProjectShortName = SelectedProject.ShortName;
issue.Type = SelectedIssueType;
issue.Department = SelectedDepartment.Value;
issue.State = "Submitted";
string issueId = _issueManagement.CreateIssue(issue);
foreach (MailAttachment attachment in Attachments)
_issueManagement.AttachFileToIssue(issueId, attachment.FilePath);
Microsoft Project Add-in
- Create a project for Microsoft Project
- Add a
Ribbon
and with the property OfficeRibbon.RibbonType
set to Microsoft.Project.Project
. - Change the property of the
Ribbon RibbonTab.ControlId.ControlIdType
for Office and RibbonTab.ControlId.OfficeId
for TabTask
. - To sync the Issue from YouTrack to Microsoft Project, you can do the following:
First, add a Button
to the Ribbon
and implement the Click
event.
using Microsoft.Office.Interop.MSProject;
using Microsoft.Office.Tools.Ribbon;
IConnection connection = new Connection("[YouTrack server]", 80);
IssueManagement issueManagement = new IssueManagement(connection);
IEnumerable<Issue> issues = issueManagement.GetAllIssuesForProject("[my project code]");
I'm using the following Microsoft Project fields to store YouTrack information:
Microsoft Project | YouTrack | Comment |
Text1 | Id | Issue Id |
Work | Estimation | Estimation that will change through Microsoft Project changes |
Duration2 | Estimation | Initial Estimation that stays fix for further comparison |
Name | Summary | |
ResourceNames | AssigneeName | |
ActualWork | SpentTime | Time tracked in YouTrack |
Now, let's update Microsoft Project:
Project project = application.ActiveProject;
List<Task> tasks = project.Tasks.Cast<Task>().ToList();
foreach (dynamic issue in issues)
{
Task task = tasks.FirstOrDefault(t => t.Text1 == issue.Id);
if (task == null)
{
task = project.Tasks.Add();
task.Text1 = issue.Id;
task.Manual = false;
task.Work = Convert.ToDouble(issue.Estimation);
task.Duration2 = task.Work;
}
task.Name = issue.Summary;
task.ResourceNames = GetResourceName(issue.AssigneeName);
task.ActualWork = Convert.ToDouble(issue.SpentTime);
}
With the function GetResourceName
defined as:
private string GetResourceName(string assignee)
{
if (assignee == null)
return string.Empty;
Application application = Globals.ThisAddIn.Application;
Project project = application.ActiveProject;
List<Resource> resources = project.Resources.Cast<Resource>().ToList();
Resource resource = resources.FirstOrDefault
(r => r.WindowsUserAccount.ToLower().Contains(assignee));
return resource != null? resource.Name : string.Empty;
}
- To sync the Issue from Microsoft Project to YouTrack , you can do the following:
I'm using the following YouTrack fields to store Microsoft Project information:
YouTrack | MS Project | Comment |
Planned start date | Start | Custom YouTrack field |
Target date | Finish | Custom YouTrack field |
Assignee | Resources[0] | |
Now, let's update YouTrack:
Application application = Globals.ThisAddIn.Application;
Project project = application.ActiveProject;
foreach (Task task in project.Tasks)
{
string issueId = task.Text1;
if (string.IsNullOrEmpty(issueId))
continue;
string command = string.Format("Planned Start Date {0} Target date {1} Assignee {2}",
((DateTime) task.Start).ToString("yyyy-MM-dd"),
((DateTime) task.Finish).ToString("yyyy-MM-dd"),
GetYoutrackAssigneeName(task.Resources));
issueManagement.ApplyCommand(issueId,command,"MS Project Update",true); }
With the function GetYoutrackAssigneeName
defined as:
private string GetYoutrackAssigneeName(Resources resources)
{
if (resources.Count == 0)
return "Unassigned";
Resource resource = resources.Cast<Resource>().First();
string userAccountName = resource.WindowsUserAccount.Split('\\').GetValue(1).ToString();
return userAccountName;
}
That's it, job done!