Microsoft Office Addins (Outlook & Project) for YouTrack






4.89/5 (4 votes)
Create YouTrack issues from Outlook and manage your issues from Microsoft Project
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 propertyOfficeRibbon.RibbonType
forMicrosoft.Outlook.Mail.Read
. - Change the property of the
Ribbon RibbonTab.ControlId.ControlIdType
for Office andRibbonTab.ControlId.OfficeId
forTabReadMessage
. - To create a YouTrack Issue from a Mail, you can do the following:
First, add a
Button
to theRibbon
and implement theClick
event:using Microsoft.Office.Interop.MSProject; using Microsoft.Office.Tools.Ribbon;
Then, store the elements of the
Mail
into aMailItem
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); //Save all the attachments in the temp directory 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); } //Save the mail itself in the temp directory 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 propertyOfficeRibbon.RibbonType
set toMicrosoft.Project.Project
. - Change the property of the
Ribbon RibbonTab.ControlId.ControlIdType
for Office andRibbonTab.ControlId.OfficeId
forTabTask
. - To sync the Issue from YouTrack to Microsoft Project, you can do the following:
First, add a
Button
to theRibbon
and implement theClick
event.using Microsoft.Office.Interop.MSProject; using Microsoft.Office.Tools.Ribbon; //Get Issues from YouTrack 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:
//Get the MS Project Tasks Application application = Globals.ThisAddIn.Application; Project project = application.ActiveProject; List<Task> tasks = project.Tasks.Cast<Task>().ToList(); //For Each YouTrack Issue, we try to get the corresponding MS Project Task //If none is found, we create one. 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;
// The POST method allows updating issue summary and/or description, only.
// To update issue fields, we need to apply a Command to the Isse.
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!