///
/// Copyright © 2003-2008 JetBrains s.r.o.
/// You may distribute under the terms of the GNU General Public License, as published by the Free Software Foundation, version 2 (see License.txt in the repository root folder).
///
using System;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using JetBrains.Omea.GUIControls;
using JetBrains.Omea.Jiffa.Res;
using JetBrains.Omea.Nntp;
using JetBrains.Omea.OpenAPI;
namespace JetBrains.Omea.Jiffa
{
[PluginDescription("(H) Serge Baltic", "JIRA connectivity.")]
public class Jiffa : IPlugin, IJiffaService
{
///
///
/// Registers the plugin resource types, actions and other services.
///
///
///
///
///
///
///This is the first method called after the plugin is loaded. It should
/// be used to register any resource types or services that could be used by other plugins.
///
///
///
///To access the services provided by the core, methods of the static class
/// can be used. All core services are already available when this
/// method is called.
///
///
///
///
public void Register()
{
try
{
Core.PluginLoader.RegisterPluginService(this);
RegisterActions();
JiraServer.Register(this);
SubmitterOptionsPane.Register(this);
Core.TabManager.RegisterResourceTypeTab(Name, Stringtable.Jira, new string[] {Types.JiraServer, Types.JiraProject, Types.JiraComponent, Types.JiraIssueType, Types.JiraPriority, Types.JiraStatus, Types.JiraCustomField, Types.JiraUser}, Core.Props.Parent, 10);
IResourceTreePane pane = Core.LeftSidebar.RegisterResourceStructureTreePane(Name, Name, Stringtable.Jira, null, JiraServer.Type);
pane.ParentProperty = Core.Props.Parent;
pane.WorkspaceFilterTypes = new string[] {Types.JiraServer, Types.JiraProject, Types.JiraComponent, Types.JiraIssueType, Types.JiraPriority, Types.JiraStatus, Types.JiraCustomField, Types.JiraUser};
pane.AddNodeFilter(new ResourceTreePaneNodeFilter());
}
catch(Exception ex)
{
Core.ReportException(ex, false);
}
}
///
/// Registers the main menu, context menu, etc actions.
///
protected void RegisterActions()
{
IAction actionSubmit = new SubmitNntpMessageToJiraAction();
//IAction actionSample = new MethodInvokerAction(OnTestAction, null);
IAction actionServerProperties = new MethodInvokerAction(OnServerProperties, null);
IAction actionAddServer = new MethodInvokerAction(OnAddServer, null);
IAction actionDeleteServer = new MethodInvokerAction(OnDeleteServer, null);
string sGroupId;
Core.ActionManager.RegisterContextMenuActionGroup(sGroupId = Name, ListAnchor.Last);
Core.ActionManager.RegisterContextMenuAction(actionSubmit, sGroupId, ListAnchor.Last, Stringtable.SubmitToJira, null, "Article", null);
Core.ActionManager.RegisterContextMenuAction(actionServerProperties, sGroupId, ListAnchor.Last, Stringtable.JiraServerProperties, null, JiraServer.Type, null);
Core.ActionManager.RegisterContextMenuAction(actionAddServer, sGroupId, ListAnchor.Last, Stringtable.AddJiraServer,null, null, new IActionStateFilter[] {new JiraResourceTreePaneFilter()});
Core.ActionManager.RegisterContextMenuAction(actionDeleteServer, sGroupId, ListAnchor.Last, Stringtable.DeleteJiraServer, null, JiraServer.Type, null);
/*
Core.ActionManager.RegisterMainMenu(Stringtable.Jira, ListAnchor.Last);
Core.ActionManager.RegisterMainMenuActionGroup(sGroupId, Stringtable.Jira, ListAnchor.Last);
Core.ActionManager.RegisterMainMenuAction(actionSubmit, sGroupId, ListAnchor.Last, Stringtable.SubmitToJira, null, null);
* */
//IActionStateFilter[] filters = new IActionStateFilter[] { new PhoboTabFilter() };
//Core.ActionManager.RegisterMainMenuAction(actionSample, sGroupId, ListAnchor.Last, "Jiffa Action", null, new IActionStateFilter[] {});
// Context Menu
//Core.ActionManager.RegisterContextMenuActionGroup(sGroup = "PhoboContext", ListAnchor.Last);
//Core.ActionManager.RegisterContextMenuAction(actProperties, sGroup, ListAnchor.Last, "PropertiesЕ", Const.TypeName, null);
//Core.ActionManager.RegisterContextMenuAction(actionSample, sGroupId, ListAnchor.Last, "Jiffa Action", null, null);
}
private class JiraResourceTreePaneFilter : IActionStateFilter
{
///
///
/// For the specified context, updates the presentation state of an action.
///
///
///
///
/// Context, containing information about resources to which the action will be applied.
///
///
/// The state of the UI element which presents the action to the user. For the
/// first filter in the chain, the presentation is initialized with the default
/// values. For subsequent filters, it contains the data set by previous filters.
///
public void Update(IActionContext context, ref ActionPresentation presentation)
{
if(context.SelectedResources.Count != 0)
{
presentation.Visible = false;
return;
}
if(context.Instance is JetResourceTreePane)
{
presentation.Visible = true;
presentation.Enabled = true;
return;
}
presentation.Visible = false;
return;
}
}
public static readonly string Name = "Jiffa";
protected void OnTestAction(IActionContext ctx)
{
JiraServer server;
server = new JiraServer(Core.ResourceStore.FindUniqueResource(Types.JiraServer, Core.Props.Name, "TestJira"));
server.Uri = "http://unit-138:8080/rpc/soap/jirasoapservice-v2";
/*
ResourceProxy proxy = ResourceProxy.BeginNewResource(Types.JiraServer);
proxy.AddLink(Core.Props.Parent, Core.ResourceTreeManager.GetRootForType(Types.JiraServer));
proxy.EndUpdate();
server = new JiraServer(proxy.Resource);
server.Uri = "http://unit-138:8080/rpc/soap/jirasoapservice-v2";
server.Username = "admin";
server.Password = "password";
server.Name = "TestJira";
*/
//return;
/*
IResourceList servers = Core.ResourceStore.GetAllResources(Types.JiraServer);
if(servers.Count == 0)
{
if(MessageBox.Show(Core.MainWindow, "No JIRA servers found. Create one?", "Jiffa", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) != DialogResult.Yes)
return;
ResourceProxy proxy = ResourceProxy.BeginNewResource(Types.JiraServer);
proxy.EndUpdate();
server = new JiraServer(proxy.Resource);
server.Uri = "http://www.jetbrains.net/jira/rpc/soap/jirasoapservice-v2";
server.Username = "baltic";
server.Name = "JetBrains JIRA";
}
else if(servers.Count != 1)
{
MessageBox.Show(Core.MainWindow, "Expecting exactly one JIRA server.", "Jiffa");
return;
}
else
server = new JiraServer(servers[0]);*/
//new ResourceProxy(server.Resource).AddLink(Core.Props.Parent, Core.ResourceTreeManager.GetRootForType(Types.JiraServer));
//new ResourceProxy(server.Resource).SetPropAsync(Core.Props.Name, "JetBrains JIRA");
if(MessageBox.Show(Core.MainWindow, string.Format("Syncing the JIRA server at {0}.", server.Uri), Name, MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation) != DialogResult.OK)
return;
server.Sync();
}
protected void OnServerProperties(IActionContext ctx)
{
foreach(IResource resource in ctx.SelectedResources)
{
if(resource.Type != Types.JiraServer)
continue;
new JiraServer(resource).ShowPropertySheet();
}
}
protected void OnAddServer(IActionContext ctx)
{
JiraServer server = JiraServer.CreateNew();
server.ShowPropertySheet();
}
protected void OnDeleteServer(IActionContext ctx)
{
foreach(IResource resource in ctx.SelectedResources)
{
if(resource.Type != Types.JiraServer)
continue;
JiraServer server = new JiraServer(resource);
while(MessageBox.Show(Core.MainWindow, string.Format(Stringtable.ConfirmDeleteServer, server.Name), Name, MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.No)
Thread.Sleep(500);
}
}
///
///
/// Performs the longer initialization activities of the plugin and starts up
/// background activities, if any are necessary.
///
///
///
///
///
///
///This is the second method called in the plugin startup sequence.
/// It is called after the method has already been called for
/// all plugins, so the code in this method can use the services provided by other
/// plugins.
///
///
///
///To access the services provided by the core, methods of the static class
/// can be used. All core services are already available when this
/// method is called.
///
///
///
///
public void Startup()
{
}
///
///
/// Terminates the plugin.
///
///
///
///
///If the plugin needs any shutdown activities (like deleting temporary
/// files), these should be performed in these method. All services
/// are still available when the method is called.
///
///
public void Shutdown()
{
}
public static string GetRandomName()
{
Random rand = new Random();
string wovels = "aeiouy";
string consonants = "bcdfghjklmnpqrstvwxz";
StringBuilder sb = new StringBuilder();
for(int a = 0; a < 7; a++)
{
char c;
string sFrom = a % 2 != 0 ? wovels : consonants;
c = sFrom[rand.Next(sFrom.Length)];
if(sb.Length == 0)
c = char.ToUpper(c);
sb.Append(c);
}
return sb.ToString();
}
}
internal class ResourceTreePaneNodeFilter : IResourceNodeFilter
{
public bool AcceptNode(IResource res, int level)
{
return (res.Type == JiraServer.Type) || (res.Type == JiraProject.Type) || (res.Type == JiraComponent.Type);
}
}
///
/// An interface that represents the Jiffa services.
///
public interface IJiffaService
{
}
public class SubmitNntpMessageToJiraAction : SimpleAction
{
public override void Execute(IActionContext context)
{
try
{
if(context.SelectedResources.Count == 0)
{
MessageBox.Show(Core.MainWindow, Stringtable.ActionNoSelection);
return;
}
//IResource resProject = Core.ResourceStore.FindUniqueResource(Types.JiraProject, Props.Key, "RSRP");
/*
IResource resProject = Core.ResourceStore.FindUniqueResource(Types.JiraProject, Props.Key, "RSP");
IResource resServer = resProject.GetLinksFrom(Types.JiraServer, Core.Props.Parent)[0];
JiraProject project = new JiraProject(new JiraServer(resServer), resProject);
* */
foreach(IResource res in context.SelectedResources)
new Impl(res).Execute();
}
catch(Exception ex)
{
Core.ReportException(ex, ExceptionReportFlags.AttachLog);
}
}
public override void Update(IActionContext context, ref ActionPresentation presentation)
{
presentation.Visible = presentation.Enabled = true;
}
public class Impl
{
protected readonly IResource _resource;
protected Submission _submission = null;
public Impl(IResource resource)
{
_resource = resource;
if(_resource.Type != "Article")
throw new InvalidOperationException();
}
public void Execute()
{
// Chose a project
int nProject = Core.SettingStore.ReadInt("Jiffa.Submission", "SubmitToProject", -1);
IResource resProject = Core.ResourceStore.TryLoadResource(nProject);
if(resProject == null)
{
MessageBox.Show(Core.MainWindow, Stringtable.SubmitProjectNotSpecified, Jiffa.Name, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return;
}
JiraProject project = JiraProject.FromResource(resProject);
// Invoke submission
_submission = new Submission(project);
_submission.IssueSubmitted += new EventHandler(OnIssueSubmitted);
_submission.ReadArticle(_resource);
_submission.ShowUI(null);
}
private void OnIssueSubmitted(object sender, EventArgs e)
{
Core.UserInterfaceAP.QueueJob((MethodInvoker)NewsReplyWithIssue);
}
protected void NewsReplyWithIssue()
{
new ResourceProxy(_resource, JobPriority.Lowest).DeletePropAsync(Core.Props.IsUnread);
string sSubj = _resource.GetPropText(Core.Props.Subject);
if(sSubj.StartsWith("Re: ", true, CultureInfo.CurrentUICulture))
sSubj = sSubj.Substring(3);
sSubj = "Re: " + sSubj;
// Construct the issue url: take the project URI and replace the project key with issue key in it
string sIssueUri = _submission.Project.Uri;
sIssueUri = sIssueUri.Substring(0, sIssueUri.Length - _submission.Project.Key.Length);
sIssueUri += _submission.Issue.key;
string sTemplate = JiffaSettings.Template;
sTemplate = sTemplate.Replace("<%=IssueUri%>", sIssueUri);
IResourceList resGroups = _resource.GetLinksFromLive("NewsGroup", "Newsgroups");
EditMessageForm.EditAndPostMessage(resGroups, sSubj, sTemplate, _resource.GetPropText("ArticleId"), false);
}
}
}
public class JiffaIconProvider : IResourceIconProvider
{
private Icon _iconJiraServer = null;
private Icon _iconJiraProject = null;
private Icon _iconJiraComponent = null;
private Icon _iconOther = null;
public Icon GetResourceIcon(IResource resource)
{
return GetDefaultIcon(resource.Type);
}
public Icon GetDefaultIcon(string resType)
{
if(resType == JiraServer.Type)
return _iconJiraServer ?? (_iconJiraServer = LoadIcon("JiraServer.ico"));
else if(resType == JiraProject.Type)
return _iconJiraProject ?? (_iconJiraProject = LoadIcon("JiraProject.ico"));
else if(resType == JiraComponent.Type)
return _iconJiraComponent ?? (_iconJiraComponent = LoadIcon("JiraComponent.ico"));
else
return _iconOther ?? (_iconOther = LoadIcon("JiraComponent.ico"));
}
///
/// Loads an icon by its short name.
///
public static Icon LoadIcon(string sResLocalName)
{
Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("JetBrains.Omea.Jiffa.Res." + sResLocalName);
if(stream == null)
return null;
return new Icon(stream);
}
}
public interface ISyncableTo
{
void Sync(TJira itemJira);
}
}