/* * Greenshot - a free and open source screenshot tool * Copyright (C) 2007-2016 Thomas Braun, Jens Klingen, Robin Krom * * For more information see: http://getgreenshot.org/ * The Greenshot project is hosted on GitHub: https://github.com/greenshot * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Dapplo.Jira; using Dapplo.Log; using GreenshotJiraPlugin.Hooking; namespace GreenshotJiraPlugin { /// /// This class will monitor all _jira activity by registering for title changes /// It keeps a list of the last "accessed" jiras, and makes it easy to upload to one. /// Make sure this is instanciated on the UI thread! /// public class JiraMonitor : IDisposable { private static readonly LogSource Log = new LogSource(); private readonly Regex _jiraKeyPattern = new Regex(@"[A-Z][A-Z0-9]+\-[0-9]+"); private readonly WindowsTitleMonitor _monitor; private readonly IList _jiraInstances = new List(); private readonly IDictionary _projectJiraClientMap = new Dictionary(); private readonly int _maxEntries; // TODO: Add issues from issueHistory (JQL -> Where.IssueKey.InIssueHistory()) private IDictionary _recentJiras = new Dictionary(); /// /// Register to this event to get events when new jira issues are detected /// public event EventHandler JiraEvent; public JiraMonitor(int maxEntries = 40) { _maxEntries = maxEntries; _monitor = new WindowsTitleMonitor(); _monitor.TitleChangeEvent += MonitorTitleChangeEvent; } #region Dispose /// /// Dispose /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Dispose all managed resources /// /// when true is passed all managed resources are disposed. protected void Dispose(bool disposing) { if (!disposing) { return; } // free managed resources _monitor.TitleChangeEvent -= MonitorTitleChangeEvent; _monitor.Dispose(); // free native resources if there are any. } #endregion /// /// Retrieve the API belonging to a JiraDetails /// /// /// IJiraClient public IJiraClient GetJiraClientForKey(JiraDetails jiraDetails) { return _projectJiraClientMap[jiraDetails.ProjectKey]; } /// /// Get the "list" of recently seen Jiras /// public IEnumerable RecentJiras => (from jiraDetails in _recentJiras.Values orderby jiraDetails.SeenAt descending select jiraDetails); /// /// Check if this monitor has active instances /// public bool HasJiraInstances => _jiraInstances.Count > 0; /// /// Add an instance of a JIRA system /// /// /// public async Task AddJiraInstanceAsync(IJiraClient jiraInstance, CancellationToken token = default(CancellationToken)) { _jiraInstances.Add(jiraInstance); var projects = await jiraInstance.Project.GetAllAsync(cancellationToken: token).ConfigureAwait(false); if (projects != null) { foreach (var project in projects) { if (!_projectJiraClientMap.ContainsKey(project.Key)) { _projectJiraClientMap.Add(project.Key, jiraInstance); } } } } /// /// This method will update details, like the title, and send an event to registed listeners of the JiraEvent /// /// Contains the jira key to retrieve the title (XYZ-1234) /// Task private async Task DetectedNewJiraIssueAsync(JiraDetails jiraDetails) { try { IJiraClient jiraClient; if (_projectJiraClientMap.TryGetValue(jiraDetails.ProjectKey, out jiraClient)) { var issue = await jiraClient.Issue.GetAsync(jiraDetails.JiraKey).ConfigureAwait(false); jiraDetails.JiraIssue = issue; } // Send event JiraEvent?.Invoke(this, new JiraEventArgs { Details = jiraDetails, EventType = JiraEventTypes.DetectedNewJiraIssue }); } catch (Exception ex) { Log.Warn().WriteLine("Couldn't retrieve JIRA title: {0}", ex.Message); } } /// /// Handle title changes, check for JIRA /// /// private void MonitorTitleChangeEvent(TitleChangeEventArgs eventArgs) { string windowTitle = eventArgs.Title; if (string.IsNullOrEmpty(windowTitle)) { return; } var jiraKeyMatch = _jiraKeyPattern.Match(windowTitle); if (!jiraKeyMatch.Success) { return; } // Found a possible JIRA title var jiraKey = jiraKeyMatch.Value; var jiraKeyParts = jiraKey.Split('-'); var projectKey = jiraKeyParts[0]; var jiraId = jiraKeyParts[1]; IJiraClient jiraClient; // Check if we have a JIRA instance with a project for this key if (_projectJiraClientMap.TryGetValue(projectKey, out jiraClient)) { // We have found a project for this _jira key, so it must be a valid & known JIRA JiraDetails currentJiraDetails; if (_recentJiras.TryGetValue(jiraKey, out currentJiraDetails)) { // update currentJiraDetails.SeenAt = DateTimeOffset.Now; // Notify the order change JiraEvent?.Invoke(this, new JiraEventArgs { Details = currentJiraDetails, EventType = JiraEventTypes.OrderChanged }); // Nothing else to do return; } // We detected an unknown JIRA, so add it to our list currentJiraDetails = new JiraDetails { Id = jiraId, ProjectKey = projectKey }; _recentJiras.Add(currentJiraDetails.JiraKey, currentJiraDetails); // Make sure we don't collect _jira's until the memory is full if (_recentJiras.Count > _maxEntries) { // Add it to the list of recent Jiras _recentJiras = (from jiraDetails in _recentJiras.Values.ToList() orderby jiraDetails.SeenAt descending select jiraDetails).Take(_maxEntries).ToDictionary(jd => jd.JiraKey, jd => jd); } // Now we can get the title from JIRA itself // ReSharper disable once UnusedVariable var updateTitleTask = DetectedNewJiraIssueAsync(currentJiraDetails); } else { Log.Info().WriteLine("Couldn't match possible JIRA key {0} to projects in a configured JIRA instance, ignoring", projectKey); } } } }