Code formatting (indents etc.) to get it consistent, simplify it for contributors.

This commit is contained in:
Robin Krom 2021-03-28 19:24:26 +02:00
commit e8c0b307ee
No known key found for this signature in database
GPG key ID: BCC01364F1371490
435 changed files with 46647 additions and 39014 deletions

View file

@ -25,56 +25,55 @@ using Greenshot.Plugin.Box.Forms;
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.Box {
/// <summary>
/// Description of ImgurConfiguration.
/// </summary>
[IniSection("Box", Description = "Greenshot Box Plugin configuration")]
public class BoxConfiguration : IniSection {
[IniProperty("UploadFormat", Description="What file type to use for uploading", DefaultValue="png")]
public OutputFormat UploadFormat { get; set; }
namespace Greenshot.Plugin.Box
{
/// <summary>
/// Description of ImgurConfiguration.
/// </summary>
[IniSection("Box", Description = "Greenshot Box Plugin configuration")]
public class BoxConfiguration : IniSection
{
[IniProperty("UploadFormat", Description = "What file type to use for uploading", DefaultValue = "png")]
public OutputFormat UploadFormat { get; set; }
[IniProperty("UploadJpegQuality", Description="JPEG file save quality in %.", DefaultValue="80")]
public int UploadJpegQuality { get; set; }
[IniProperty("UploadJpegQuality", Description = "JPEG file save quality in %.", DefaultValue = "80")]
public int UploadJpegQuality { get; set; }
[IniProperty("AfterUploadLinkToClipBoard", Description = "After upload send Box link to clipboard.", DefaultValue = "true")]
public bool AfterUploadLinkToClipBoard { get; set; }
[IniProperty("AfterUploadLinkToClipBoard", Description = "After upload send Box link to clipboard.", DefaultValue = "true")]
public bool AfterUploadLinkToClipBoard { get; set; }
[IniProperty("UseSharedLink", Description = "Use the shared link, instead of the private, on the clipboard", DefaultValue = "True")]
public bool UseSharedLink { get; set; }
[IniProperty("FolderId", Description = "Folder ID to upload to, only change if you know what you are doing!", DefaultValue = "0")]
public string FolderId { get; set; }
[IniProperty("UseSharedLink", Description = "Use the shared link, instead of the private, on the clipboard", DefaultValue = "True")]
public bool UseSharedLink { get; set; }
[IniProperty("RefreshToken", Description = "Box authorization refresh Token", Encrypted = true)]
public string RefreshToken { get; set; }
[IniProperty("FolderId", Description = "Folder ID to upload to, only change if you know what you are doing!", DefaultValue = "0")]
public string FolderId { get; set; }
/// <summary>
/// Not stored
/// </summary>
public string AccessToken {
get;
set;
}
[IniProperty("RefreshToken", Description = "Box authorization refresh Token", Encrypted = true)]
public string RefreshToken { get; set; }
/// <summary>
/// Not stored
/// </summary>
public DateTimeOffset AccessTokenExpires {
get;
set;
}
/// <summary>
/// Not stored
/// </summary>
public string AccessToken { get; set; }
/// <summary>
/// A form for token
/// </summary>
/// <returns>bool true if OK was pressed, false if cancel</returns>
public bool ShowConfigDialog() {
DialogResult result = new SettingsForm().ShowDialog();
if (result == DialogResult.OK) {
return true;
}
return false;
}
/// <summary>
/// Not stored
/// </summary>
public DateTimeOffset AccessTokenExpires { get; set; }
}
/// <summary>
/// A form for token
/// </summary>
/// <returns>bool true if OK was pressed, false if cancel</returns>
public bool ShowConfigDialog()
{
DialogResult result = new SettingsForm().ShowDialog();
if (result == DialogResult.OK)
{
return true;
}
return false;
}
}
}

View file

@ -24,33 +24,42 @@ using System.Drawing;
using GreenshotPlugin.Core;
using GreenshotPlugin.Interfaces;
namespace Greenshot.Plugin.Box {
public class BoxDestination : AbstractDestination {
private readonly BoxPlugin _plugin;
public BoxDestination(BoxPlugin plugin) {
_plugin = plugin;
}
namespace Greenshot.Plugin.Box
{
public class BoxDestination : AbstractDestination
{
private readonly BoxPlugin _plugin;
public override string Designation => "Box";
public BoxDestination(BoxPlugin plugin)
{
_plugin = plugin;
}
public override string Description => Language.GetString("box", LangKey.upload_menu_item);
public override string Designation => "Box";
public override Image DisplayIcon {
get {
ComponentResourceManager resources = new ComponentResourceManager(typeof(BoxPlugin));
return (Image)resources.GetObject("Box");
}
}
public override string Description => Language.GetString("box", LangKey.upload_menu_item);
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) {
ExportInformation exportInformation = new ExportInformation(Designation, Description);
string uploadUrl = _plugin.Upload(captureDetails, surface);
if (uploadUrl != null) {
exportInformation.ExportMade = true;
exportInformation.Uri = uploadUrl;
}
ProcessExport(exportInformation, surface);
return exportInformation;
}
}
public override Image DisplayIcon
{
get
{
ComponentResourceManager resources = new ComponentResourceManager(typeof(BoxPlugin));
return (Image) resources.GetObject("Box");
}
}
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails)
{
ExportInformation exportInformation = new ExportInformation(Designation, Description);
string uploadUrl = _plugin.Upload(captureDetails, surface);
if (uploadUrl != null)
{
exportInformation.ExportMade = true;
exportInformation.Uri = uploadUrl;
}
ProcessExport(exportInformation, surface);
return exportInformation;
}
}
}

View file

@ -22,39 +22,35 @@
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace Greenshot.Plugin.Box {
[DataContract]
public class Authorization {
[DataMember(Name = "access_token")]
public string AccessToken { get; set; }
[DataMember(Name = "expires_in")]
public int ExpiresIn { get; set; }
[DataMember(Name = "refresh_token")]
public string RefreshToken { get; set; }
[DataMember(Name = "token_type")]
public string TokenType { get; set; }
}
[DataContract]
public class SharedLink {
[DataMember(Name = "url")]
public string Url { get; set; }
[DataMember(Name = "download_url")]
public string DownloadUrl { get; set; }
}
namespace Greenshot.Plugin.Box
{
[DataContract]
public class Authorization
{
[DataMember(Name = "access_token")] public string AccessToken { get; set; }
[DataMember(Name = "expires_in")] public int ExpiresIn { get; set; }
[DataMember(Name = "refresh_token")] public string RefreshToken { get; set; }
[DataMember(Name = "token_type")] public string TokenType { get; set; }
}
[DataContract]
public class FileEntry {
[DataMember(Name = "id")]
public string Id { get; set; }
[DataMember(Name = "name")]
public string Name { get; set; }
[DataMember(Name = "shared_link")]
public SharedLink SharedLink { get; set; }
}
[DataContract]
public class SharedLink
{
[DataMember(Name = "url")] public string Url { get; set; }
[DataMember(Name = "download_url")] public string DownloadUrl { get; set; }
}
[DataContract]
public class Upload {
[DataMember(Name = "entries")]
public List<FileEntry> Entries { get; set; }
}
[DataContract]
public class FileEntry
{
[DataMember(Name = "id")] public string Id { get; set; }
[DataMember(Name = "name")] public string Name { get; set; }
[DataMember(Name = "shared_link")] public SharedLink SharedLink { get; set; }
}
[DataContract]
public class Upload
{
[DataMember(Name = "entries")] public List<FileEntry> Entries { get; set; }
}
}

View file

@ -30,99 +30,111 @@ using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
namespace Greenshot.Plugin.Box {
/// <summary>
/// This is the Box base code
/// </summary>
namespace Greenshot.Plugin.Box
{
/// <summary>
/// This is the Box base code
/// </summary>
[Plugin("Box", true)]
public class BoxPlugin : IGreenshotPlugin {
private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(BoxPlugin));
private static BoxConfiguration _config;
private ComponentResourceManager _resources;
private ToolStripMenuItem _itemPlugInConfig;
public class BoxPlugin : IGreenshotPlugin
{
private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(BoxPlugin));
private static BoxConfiguration _config;
private ComponentResourceManager _resources;
private ToolStripMenuItem _itemPlugInConfig;
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(bool disposing)
{
if (!disposing) return;
protected void Dispose(bool disposing)
{
if (!disposing) return;
if (_itemPlugInConfig == null) return;
if (_itemPlugInConfig == null) return;
_itemPlugInConfig.Dispose();
_itemPlugInConfig = null;
}
_itemPlugInConfig.Dispose();
_itemPlugInConfig = null;
}
/// <summary>
/// Implementation of the IGreenshotPlugin.Initialize
/// </summary>
public bool Initialize() {
// Register configuration (don't need the configuration itself)
_config = IniConfig.GetIniSection<BoxConfiguration>();
_resources = new ComponentResourceManager(typeof(BoxPlugin));
/// <summary>
/// Implementation of the IGreenshotPlugin.Initialize
/// </summary>
public bool Initialize()
{
// Register configuration (don't need the configuration itself)
_config = IniConfig.GetIniSection<BoxConfiguration>();
_resources = new ComponentResourceManager(typeof(BoxPlugin));
SimpleServiceProvider.Current.AddService<IDestination>(new BoxDestination(this));
_itemPlugInConfig = new ToolStripMenuItem {
Image = (Image) _resources.GetObject("Box"),
Text = Language.GetString("box", LangKey.Configure)
};
_itemPlugInConfig.Click += ConfigMenuClick;
_itemPlugInConfig = new ToolStripMenuItem
{
Image = (Image) _resources.GetObject("Box"),
Text = Language.GetString("box", LangKey.Configure)
};
_itemPlugInConfig.Click += ConfigMenuClick;
PluginUtils.AddToContextMenu(_itemPlugInConfig);
Language.LanguageChanged += OnLanguageChanged;
return true;
}
PluginUtils.AddToContextMenu(_itemPlugInConfig);
Language.LanguageChanged += OnLanguageChanged;
return true;
}
public void OnLanguageChanged(object sender, EventArgs e) {
if (_itemPlugInConfig != null) {
_itemPlugInConfig.Text = Language.GetString("box", LangKey.Configure);
}
}
public void OnLanguageChanged(object sender, EventArgs e)
{
if (_itemPlugInConfig != null)
{
_itemPlugInConfig.Text = Language.GetString("box", LangKey.Configure);
}
}
public void Shutdown() {
LOG.Debug("Box Plugin shutdown.");
}
public void Shutdown()
{
LOG.Debug("Box Plugin shutdown.");
}
/// <summary>
/// Implementation of the IPlugin.Configure
/// </summary>
public void Configure() {
_config.ShowConfigDialog();
}
/// <summary>
/// Implementation of the IPlugin.Configure
/// </summary>
public void Configure()
{
_config.ShowConfigDialog();
}
public void ConfigMenuClick(object sender, EventArgs eventArgs) {
_config.ShowConfigDialog();
}
public void ConfigMenuClick(object sender, EventArgs eventArgs)
{
_config.ShowConfigDialog();
}
/// <summary>
/// This will be called when the menu item in the Editor is clicked
/// </summary>
public string Upload(ICaptureDetails captureDetails, ISurface surfaceToUpload) {
SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(_config.UploadFormat, _config.UploadJpegQuality, false);
try {
string url = null;
string filename = Path.GetFileName(FilenameHelper.GetFilename(_config.UploadFormat, captureDetails));
SurfaceContainer imageToUpload = new SurfaceContainer(surfaceToUpload, outputSettings, filename);
/// <summary>
/// This will be called when the menu item in the Editor is clicked
/// </summary>
public string Upload(ICaptureDetails captureDetails, ISurface surfaceToUpload)
{
SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(_config.UploadFormat, _config.UploadJpegQuality, false);
try
{
string url = null;
string filename = Path.GetFileName(FilenameHelper.GetFilename(_config.UploadFormat, captureDetails));
SurfaceContainer imageToUpload = new SurfaceContainer(surfaceToUpload, outputSettings, filename);
new PleaseWaitForm().ShowAndWait("Box", Language.GetString("box", LangKey.communication_wait),
delegate {
url = BoxUtils.UploadToBox(imageToUpload, captureDetails.Title, filename);
}
);
new PleaseWaitForm().ShowAndWait("Box", Language.GetString("box", LangKey.communication_wait),
delegate { url = BoxUtils.UploadToBox(imageToUpload, captureDetails.Title, filename); }
);
if (url != null && _config.AfterUploadLinkToClipBoard) {
ClipboardHelper.SetClipboardData(url);
}
if (url != null && _config.AfterUploadLinkToClipBoard)
{
ClipboardHelper.SetClipboardData(url);
}
return url;
} catch (Exception ex) {
LOG.Error("Error uploading.", ex);
MessageBox.Show(Language.GetString("box", LangKey.upload_failure) + " " + ex.Message);
return null;
}
}
}
return url;
}
catch (Exception ex)
{
LOG.Error("Error uploading.", ex);
MessageBox.Show(Language.GetString("box", LangKey.upload_failure) + " " + ex.Message);
return null;
}
}
}
}

View file

@ -27,112 +27,128 @@ using GreenshotPlugin.Core;
using GreenshotPlugin.Core.OAuth;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.Box {
namespace Greenshot.Plugin.Box
{
/// <summary>
/// Description of BoxUtils.
/// </summary>
public static class BoxUtils {
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(BoxUtils));
private static readonly BoxConfiguration Config = IniConfig.GetIniSection<BoxConfiguration>();
private const string UploadFileUri = "https://upload.box.com/api/2.0/files/content";
private const string FilesUri = "https://www.box.com/api/2.0/files/{0}";
public static class BoxUtils
{
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(BoxUtils));
private static readonly BoxConfiguration Config = IniConfig.GetIniSection<BoxConfiguration>();
private const string UploadFileUri = "https://upload.box.com/api/2.0/files/content";
private const string FilesUri = "https://www.box.com/api/2.0/files/{0}";
/// <summary>
/// Put string
/// </summary>
/// <param name="url"></param>
/// <param name="content"></param>
/// <param name="settings">OAuth2Settings</param>
/// <returns>response</returns>
public static string HttpPut(string url, string content, OAuth2Settings settings) {
var webRequest= OAuth2Helper.CreateOAuth2WebRequest(HTTPMethod.PUT, url, settings);
byte[] data = Encoding.UTF8.GetBytes(content);
using (var requestStream = webRequest.GetRequestStream()) {
requestStream.Write(data, 0, data.Length);
}
return NetworkHelper.GetResponseAsString(webRequest);
}
/// <summary>
/// Do the actual upload to Box
/// For more details on the available parameters, see: http://developers.box.net/w/page/12923951/ApiFunction_Upload%20and%20Download
/// </summary>
/// <param name="image">Image for box upload</param>
/// <param name="title">Title of box upload</param>
/// <param name="filename">Filename of box upload</param>
/// <returns>url to uploaded image</returns>
public static string UploadToBox(SurfaceContainer image, string title, string filename) {
// Fill the OAuth2Settings
var settings = new OAuth2Settings
{
AuthUrlPattern = "https://app.box.com/api/oauth2/authorize?client_id={ClientId}&response_type=code&state={State}&redirect_uri={RedirectUrl}",
TokenUrl = "https://api.box.com/oauth2/token",
CloudServiceName = "Box",
ClientId = BoxCredentials.ClientId,
ClientSecret = BoxCredentials.ClientSecret,
RedirectUrl = "https://getgreenshot.org/authorize/box",
AuthorizeMode = OAuth2AuthorizeMode.JsonReceiver,
RefreshToken = Config.RefreshToken,
AccessToken = Config.AccessToken,
AccessTokenExpires = Config.AccessTokenExpires
};
// Copy the settings from the config, which is kept in memory and on the disk
try {
var webRequest = OAuth2Helper.CreateOAuth2WebRequest(HTTPMethod.POST, UploadFileUri, settings);
IDictionary<string, object> parameters = new Dictionary<string, object>
{
{ "file", image },
{ "parent_id", Config.FolderId }
};
NetworkHelper.WriteMultipartFormData(webRequest, parameters);
var response = NetworkHelper.GetResponseAsString(webRequest);
Log.DebugFormat("Box response: {0}", response);
var upload = JsonSerializer.Deserialize<Upload>(response);
if (upload?.Entries == null || upload.Entries.Count == 0) return null;
if (Config.UseSharedLink) {
string filesResponse = HttpPut(string.Format(FilesUri, upload.Entries[0].Id), "{\"shared_link\": {\"access\": \"open\"}}", settings);
var file = JsonSerializer.Deserialize<FileEntry>(filesResponse);
return file.SharedLink.Url;
}
return $"http://www.box.com/files/0/f/0/1/f_{upload.Entries[0].Id}";
} finally {
// Copy the settings back to the config, so they are stored.
Config.RefreshToken = settings.RefreshToken;
Config.AccessToken = settings.AccessToken;
Config.AccessTokenExpires = settings.AccessTokenExpires;
Config.IsDirty = true;
IniConfig.Save();
}
}
}
/// <summary>
/// A simple helper class for the DataContractJsonSerializer
/// </summary>
internal static class JsonSerializer {
/// <summary>
/// Helper method to parse JSON to object
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="jsonString"></param>
/// <returns></returns>
public static T Deserialize<T>(string jsonString) {
var deserializer = new DataContractJsonSerializer(typeof(T));
/// Put string
/// </summary>
/// <param name="url"></param>
/// <param name="content"></param>
/// <param name="settings">OAuth2Settings</param>
/// <returns>response</returns>
public static string HttpPut(string url, string content, OAuth2Settings settings)
{
var webRequest = OAuth2Helper.CreateOAuth2WebRequest(HTTPMethod.PUT, url, settings);
byte[] data = Encoding.UTF8.GetBytes(content);
using (var requestStream = webRequest.GetRequestStream())
{
requestStream.Write(data, 0, data.Length);
}
return NetworkHelper.GetResponseAsString(webRequest);
}
/// <summary>
/// Do the actual upload to Box
/// For more details on the available parameters, see: http://developers.box.net/w/page/12923951/ApiFunction_Upload%20and%20Download
/// </summary>
/// <param name="image">Image for box upload</param>
/// <param name="title">Title of box upload</param>
/// <param name="filename">Filename of box upload</param>
/// <returns>url to uploaded image</returns>
public static string UploadToBox(SurfaceContainer image, string title, string filename)
{
// Fill the OAuth2Settings
var settings = new OAuth2Settings
{
AuthUrlPattern = "https://app.box.com/api/oauth2/authorize?client_id={ClientId}&response_type=code&state={State}&redirect_uri={RedirectUrl}",
TokenUrl = "https://api.box.com/oauth2/token",
CloudServiceName = "Box",
ClientId = BoxCredentials.ClientId,
ClientSecret = BoxCredentials.ClientSecret,
RedirectUrl = "https://getgreenshot.org/authorize/box",
AuthorizeMode = OAuth2AuthorizeMode.JsonReceiver,
RefreshToken = Config.RefreshToken,
AccessToken = Config.AccessToken,
AccessTokenExpires = Config.AccessTokenExpires
};
// Copy the settings from the config, which is kept in memory and on the disk
try
{
var webRequest = OAuth2Helper.CreateOAuth2WebRequest(HTTPMethod.POST, UploadFileUri, settings);
IDictionary<string, object> parameters = new Dictionary<string, object>
{
{
"file", image
},
{
"parent_id", Config.FolderId
}
};
NetworkHelper.WriteMultipartFormData(webRequest, parameters);
var response = NetworkHelper.GetResponseAsString(webRequest);
Log.DebugFormat("Box response: {0}", response);
var upload = JsonSerializer.Deserialize<Upload>(response);
if (upload?.Entries == null || upload.Entries.Count == 0) return null;
if (Config.UseSharedLink)
{
string filesResponse = HttpPut(string.Format(FilesUri, upload.Entries[0].Id), "{\"shared_link\": {\"access\": \"open\"}}", settings);
var file = JsonSerializer.Deserialize<FileEntry>(filesResponse);
return file.SharedLink.Url;
}
return $"http://www.box.com/files/0/f/0/1/f_{upload.Entries[0].Id}";
}
finally
{
// Copy the settings back to the config, so they are stored.
Config.RefreshToken = settings.RefreshToken;
Config.AccessToken = settings.AccessToken;
Config.AccessTokenExpires = settings.AccessTokenExpires;
Config.IsDirty = true;
IniConfig.Save();
}
}
}
/// <summary>
/// A simple helper class for the DataContractJsonSerializer
/// </summary>
internal static class JsonSerializer
{
/// <summary>
/// Helper method to parse JSON to object
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="jsonString"></param>
/// <returns></returns>
public static T Deserialize<T>(string jsonString)
{
var deserializer = new DataContractJsonSerializer(typeof(T));
using var stream = new MemoryStream();
byte[] content = Encoding.UTF8.GetBytes(jsonString);
stream.Write(content, 0, content.Length);
stream.Seek(0, SeekOrigin.Begin);
return (T)deserializer.ReadObject(stream);
return (T) deserializer.ReadObject(stream);
}
}
}
}

View file

@ -21,7 +21,9 @@
using GreenshotPlugin.Controls;
namespace Greenshot.Plugin.Box.Forms {
public class BoxForm : GreenshotForm {
}
namespace Greenshot.Plugin.Box.Forms
{
public class BoxForm : GreenshotForm
{
}
}

View file

@ -19,18 +19,21 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Greenshot.Plugin.Box.Forms {
/// <summary>
/// Description of PasswordRequestForm.
/// </summary>
public partial class SettingsForm : BoxForm {
public SettingsForm() {
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
AcceptButton = buttonOK;
CancelButton = buttonCancel;
}
}
namespace Greenshot.Plugin.Box.Forms
{
/// <summary>
/// Description of PasswordRequestForm.
/// </summary>
public partial class SettingsForm : BoxForm
{
public SettingsForm()
{
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
AcceptButton = buttonOK;
CancelButton = buttonCancel;
}
}
}

View file

@ -18,11 +18,14 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Greenshot.Plugin.Box {
public enum LangKey {
upload_menu_item,
upload_failure,
communication_wait,
Configure
}
namespace Greenshot.Plugin.Box
{
public enum LangKey
{
upload_menu_item,
upload_failure,
communication_wait,
Configure
}
}

View file

@ -26,283 +26,333 @@ using GreenshotConfluencePlugin.confluence;
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.Confluence {
public class Page {
public Page(RemotePage page) {
Id = page.id;
Title = page.title;
SpaceKey = page.space;
Url = page.url;
Content = page.content;
}
public Page(RemoteSearchResult searchResult, string space) {
Id = searchResult.id;
Title = searchResult.title;
SpaceKey = space;
Url = searchResult.url;
Content = searchResult.excerpt;
}
namespace Greenshot.Plugin.Confluence
{
public class Page
{
public Page(RemotePage page)
{
Id = page.id;
Title = page.title;
SpaceKey = page.space;
Url = page.url;
Content = page.content;
}
public Page(RemotePageSummary pageSummary) {
Id = pageSummary.id;
Title = pageSummary.title;
SpaceKey = pageSummary.space;
Url =pageSummary.url;
}
public long Id {
get;
set;
}
public string Title {
get;
set;
}
public string Url {
get;
set;
}
public string Content {
get;
set;
}
public string SpaceKey {
get;
set;
}
}
public class Space {
public Space(RemoteSpaceSummary space) {
Key = space.key;
Name = space.name;
}
public string Key {
get;
set;
}
public string Name {
get;
set;
}
}
public Page(RemoteSearchResult searchResult, string space)
{
Id = searchResult.id;
Title = searchResult.title;
SpaceKey = space;
Url = searchResult.url;
Content = searchResult.excerpt;
}
public Page(RemotePageSummary pageSummary)
{
Id = pageSummary.id;
Title = pageSummary.title;
SpaceKey = pageSummary.space;
Url = pageSummary.url;
}
public long Id { get; set; }
public string Title { get; set; }
public string Url { get; set; }
public string Content { get; set; }
public string SpaceKey { get; set; }
}
public class Space
{
public Space(RemoteSpaceSummary space)
{
Key = space.key;
Name = space.name;
}
public string Key { get; set; }
public string Name { get; set; }
}
/// <summary>
/// For details see the Confluence API site
/// See: http://confluence.atlassian.com/display/CONFDEV/Remote+API+Specification
/// </summary>
public class ConfluenceConnector : IDisposable {
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ConfluenceConnector));
private const string AuthFailedExceptionName = "com.atlassian.confluence.rpc.AuthenticationFailedException";
private const string V2Failed = "AXIS";
private static readonly ConfluenceConfiguration Config = IniConfig.GetIniSection<ConfluenceConfiguration>();
private string _credentials;
private DateTime _loggedInTime = DateTime.Now;
private bool _loggedIn;
private ConfluenceSoapServiceService _confluence;
private readonly int _timeout;
private string _url;
private readonly Cache<string, RemotePage> _pageCache = new Cache<string, RemotePage>(60 * Config.Timeout);
/// For details see the Confluence API site
/// See: http://confluence.atlassian.com/display/CONFDEV/Remote+API+Specification
/// </summary>
public class ConfluenceConnector : IDisposable
{
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ConfluenceConnector));
private const string AuthFailedExceptionName = "com.atlassian.confluence.rpc.AuthenticationFailedException";
private const string V2Failed = "AXIS";
private static readonly ConfluenceConfiguration Config = IniConfig.GetIniSection<ConfluenceConfiguration>();
private string _credentials;
private DateTime _loggedInTime = DateTime.Now;
private bool _loggedIn;
private ConfluenceSoapServiceService _confluence;
private readonly int _timeout;
private string _url;
private readonly Cache<string, RemotePage> _pageCache = new Cache<string, RemotePage>(60 * Config.Timeout);
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(bool disposing) {
if (_confluence != null) {
Logout();
}
if (disposing) {
if (_confluence != null) {
_confluence.Dispose();
_confluence = null;
}
}
}
protected void Dispose(bool disposing)
{
if (_confluence != null)
{
Logout();
}
public ConfluenceConnector(string url, int timeout) {
_timeout = timeout;
Init(url);
}
if (disposing)
{
if (_confluence != null)
{
_confluence.Dispose();
_confluence = null;
}
}
}
private void Init(string url) {
_url = url;
_confluence = new ConfluenceSoapServiceService
{
Url = url,
Proxy = NetworkHelper.CreateProxy(new Uri(url))
};
}
public ConfluenceConnector(string url, int timeout)
{
_timeout = timeout;
Init(url);
}
~ConfluenceConnector() {
Dispose(false);
}
private void Init(string url)
{
_url = url;
_confluence = new ConfluenceSoapServiceService
{
Url = url,
Proxy = NetworkHelper.CreateProxy(new Uri(url))
};
}
/// <summary>
/// Internal login which catches the exceptions
/// </summary>
/// <returns>true if login was done sucessfully</returns>
private bool DoLogin(string user, string password) {
try {
_credentials = _confluence.login(user, password);
_loggedInTime = DateTime.Now;
_loggedIn = true;
} catch (Exception e) {
// Check if confluence-v2 caused an error, use v1 instead
if (e.Message.Contains(V2Failed) && _url.Contains("v2")) {
Init(_url.Replace("v2", "v1"));
return DoLogin(user, password);
}
// check if auth failed
if (e.Message.Contains(AuthFailedExceptionName)) {
return false;
}
// Not an authentication issue
_loggedIn = false;
_credentials = null;
e.Data.Add("user", user);
e.Data.Add("url", _url);
throw;
}
return true;
}
~ConfluenceConnector()
{
Dispose(false);
}
public void Login() {
Logout();
try {
// Get the system name, so the user knows where to login to
string systemName = _url.Replace(ConfluenceConfiguration.DEFAULT_POSTFIX1,"");
systemName = systemName.Replace(ConfluenceConfiguration.DEFAULT_POSTFIX2, "");
CredentialsDialog dialog = new CredentialsDialog(systemName)
{
Name = null
};
while (dialog.Show(dialog.Name) == DialogResult.OK) {
if (DoLogin(dialog.Name, dialog.Password)) {
if (dialog.SaveChecked) {
dialog.Confirm(true);
}
return;
} else {
try {
dialog.Confirm(false);
} catch (ApplicationException e) {
// exception handling ...
Log.Error("Problem using the credentials dialog", e);
}
// For every windows version after XP show an incorrect password baloon
dialog.IncorrectPassword = true;
// Make sure the dialog is display, the password was false!
dialog.AlwaysDisplay = true;
}
}
} catch (ApplicationException e) {
// exception handling ...
Log.Error("Problem using the credentials dialog", e);
}
}
/// <summary>
/// Internal login which catches the exceptions
/// </summary>
/// <returns>true if login was done sucessfully</returns>
private bool DoLogin(string user, string password)
{
try
{
_credentials = _confluence.login(user, password);
_loggedInTime = DateTime.Now;
_loggedIn = true;
}
catch (Exception e)
{
// Check if confluence-v2 caused an error, use v1 instead
if (e.Message.Contains(V2Failed) && _url.Contains("v2"))
{
Init(_url.Replace("v2", "v1"));
return DoLogin(user, password);
}
public void Logout() {
if (_credentials != null) {
_confluence.logout(_credentials);
_credentials = null;
_loggedIn = false;
}
}
// check if auth failed
if (e.Message.Contains(AuthFailedExceptionName))
{
return false;
}
private void CheckCredentials() {
if (_loggedIn) {
if (_loggedInTime.AddMinutes(_timeout-1).CompareTo(DateTime.Now) < 0) {
Logout();
Login();
}
} else {
Login();
}
}
// Not an authentication issue
_loggedIn = false;
_credentials = null;
e.Data.Add("user", user);
e.Data.Add("url", _url);
throw;
}
public bool IsLoggedIn => _loggedIn;
return true;
}
public void AddAttachment(long pageId, string mime, string comment, string filename, IBinaryContainer image) {
CheckCredentials();
// Comment is ignored, see: http://jira.atlassian.com/browse/CONF-9395
var attachment = new RemoteAttachment
{
comment = comment,
fileName = filename,
contentType = mime
};
_confluence.addAttachment(_credentials, pageId, attachment, image.ToByteArray());
}
public void Login()
{
Logout();
try
{
// Get the system name, so the user knows where to login to
string systemName = _url.Replace(ConfluenceConfiguration.DEFAULT_POSTFIX1, "");
systemName = systemName.Replace(ConfluenceConfiguration.DEFAULT_POSTFIX2, "");
CredentialsDialog dialog = new CredentialsDialog(systemName)
{
Name = null
};
while (dialog.Show(dialog.Name) == DialogResult.OK)
{
if (DoLogin(dialog.Name, dialog.Password))
{
if (dialog.SaveChecked)
{
dialog.Confirm(true);
}
public Page GetPage(string spaceKey, string pageTitle) {
RemotePage page = null;
string cacheKey = spaceKey + pageTitle;
if (_pageCache.Contains(cacheKey)) {
page = _pageCache[cacheKey];
}
if (page == null) {
CheckCredentials();
page = _confluence.getPage(_credentials, spaceKey, pageTitle);
_pageCache.Add(cacheKey, page);
}
return new Page(page);
}
return;
}
else
{
try
{
dialog.Confirm(false);
}
catch (ApplicationException e)
{
// exception handling ...
Log.Error("Problem using the credentials dialog", e);
}
public Page GetPage(long pageId) {
RemotePage page = null;
string cacheKey = pageId.ToString();
// For every windows version after XP show an incorrect password baloon
dialog.IncorrectPassword = true;
// Make sure the dialog is display, the password was false!
dialog.AlwaysDisplay = true;
}
}
}
catch (ApplicationException e)
{
// exception handling ...
Log.Error("Problem using the credentials dialog", e);
}
}
if (_pageCache.Contains(cacheKey)) {
page = _pageCache[cacheKey];
}
if (page == null) {
CheckCredentials();
page = _confluence.getPage(_credentials, pageId);
_pageCache.Add(cacheKey, page);
}
return new Page(page);
}
public void Logout()
{
if (_credentials != null)
{
_confluence.logout(_credentials);
_credentials = null;
_loggedIn = false;
}
}
public Page GetSpaceHomepage(Space spaceSummary) {
CheckCredentials();
RemoteSpace spaceDetail = _confluence.getSpace(_credentials, spaceSummary.Key);
RemotePage page = _confluence.getPage(_credentials, spaceDetail.homePage);
return new Page(page);
}
private void CheckCredentials()
{
if (_loggedIn)
{
if (_loggedInTime.AddMinutes(_timeout - 1).CompareTo(DateTime.Now) < 0)
{
Logout();
Login();
}
}
else
{
Login();
}
}
public IEnumerable<Space> GetSpaceSummaries() {
CheckCredentials();
RemoteSpaceSummary [] spaces = _confluence.getSpaces(_credentials);
foreach(RemoteSpaceSummary space in spaces) {
yield return new Space(space);
}
}
public bool IsLoggedIn => _loggedIn;
public IEnumerable<Page> GetPageChildren(Page parentPage) {
CheckCredentials();
RemotePageSummary[] pages = _confluence.getChildren(_credentials, parentPage.Id);
foreach(RemotePageSummary page in pages) {
yield return new Page(page);
}
}
public void AddAttachment(long pageId, string mime, string comment, string filename, IBinaryContainer image)
{
CheckCredentials();
// Comment is ignored, see: http://jira.atlassian.com/browse/CONF-9395
var attachment = new RemoteAttachment
{
comment = comment,
fileName = filename,
contentType = mime
};
_confluence.addAttachment(_credentials, pageId, attachment, image.ToByteArray());
}
public IEnumerable<Page> GetPageSummaries(Space space) {
CheckCredentials();
RemotePageSummary[] pages = _confluence.getPages(_credentials, space.Key);
foreach(RemotePageSummary page in pages) {
yield return new Page(page);
}
}
public Page GetPage(string spaceKey, string pageTitle)
{
RemotePage page = null;
string cacheKey = spaceKey + pageTitle;
if (_pageCache.Contains(cacheKey))
{
page = _pageCache[cacheKey];
}
public IEnumerable<Page> SearchPages(string query, string space) {
CheckCredentials();
foreach(var searchResult in _confluence.search(_credentials, query, 20)) {
Log.DebugFormat("Got result of type {0}", searchResult.type);
if ("page".Equals(searchResult.type))
{
yield return new Page(searchResult, space);
}
}
}
}
if (page == null)
{
CheckCredentials();
page = _confluence.getPage(_credentials, spaceKey, pageTitle);
_pageCache.Add(cacheKey, page);
}
return new Page(page);
}
public Page GetPage(long pageId)
{
RemotePage page = null;
string cacheKey = pageId.ToString();
if (_pageCache.Contains(cacheKey))
{
page = _pageCache[cacheKey];
}
if (page == null)
{
CheckCredentials();
page = _confluence.getPage(_credentials, pageId);
_pageCache.Add(cacheKey, page);
}
return new Page(page);
}
public Page GetSpaceHomepage(Space spaceSummary)
{
CheckCredentials();
RemoteSpace spaceDetail = _confluence.getSpace(_credentials, spaceSummary.Key);
RemotePage page = _confluence.getPage(_credentials, spaceDetail.homePage);
return new Page(page);
}
public IEnumerable<Space> GetSpaceSummaries()
{
CheckCredentials();
RemoteSpaceSummary[] spaces = _confluence.getSpaces(_credentials);
foreach (RemoteSpaceSummary space in spaces)
{
yield return new Space(space);
}
}
public IEnumerable<Page> GetPageChildren(Page parentPage)
{
CheckCredentials();
RemotePageSummary[] pages = _confluence.getChildren(_credentials, parentPage.Id);
foreach (RemotePageSummary page in pages)
{
yield return new Page(page);
}
}
public IEnumerable<Page> GetPageSummaries(Space space)
{
CheckCredentials();
RemotePageSummary[] pages = _confluence.getPages(_credentials, space.Key);
foreach (RemotePageSummary page in pages)
{
yield return new Page(page);
}
}
public IEnumerable<Page> SearchPages(string query, string space)
{
CheckCredentials();
foreach (var searchResult in _confluence.search(_credentials, query, 20))
{
Log.DebugFormat("Got result of type {0}", searchResult.type);
if ("page".Equals(searchResult.type))
{
yield return new Page(searchResult, space);
}
}
}
}
}

View file

@ -23,63 +23,45 @@ using System;
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.Confluence {
/// <summary>
/// Description of ConfluenceConfiguration.
/// </summary>
[Serializable]
[IniSection("Confluence", Description="Greenshot Confluence Plugin configuration")]
public class ConfluenceConfiguration : IniSection {
public const string DEFAULT_POSTFIX1 = "/rpc/soap-axis/confluenceservice-v1?wsdl";
public const string DEFAULT_POSTFIX2 = "/rpc/soap-axis/confluenceservice-v2?wsdl";
public const string DEFAULT_PREFIX = "http://";
private const string DEFAULT_URL = DEFAULT_PREFIX + "confluence";
namespace Greenshot.Plugin.Confluence
{
/// <summary>
/// Description of ConfluenceConfiguration.
/// </summary>
[Serializable]
[IniSection("Confluence", Description = "Greenshot Confluence Plugin configuration")]
public class ConfluenceConfiguration : IniSection
{
public const string DEFAULT_POSTFIX1 = "/rpc/soap-axis/confluenceservice-v1?wsdl";
public const string DEFAULT_POSTFIX2 = "/rpc/soap-axis/confluenceservice-v2?wsdl";
public const string DEFAULT_PREFIX = "http://";
private const string DEFAULT_URL = DEFAULT_PREFIX + "confluence";
[IniProperty("Url", Description="Url to Confluence system, including wsdl.", DefaultValue=DEFAULT_URL)]
public string Url {
get;
set;
}
[IniProperty("Timeout", Description="Session timeout in minutes", DefaultValue="30")]
public int Timeout {
get;
set;
}
[IniProperty("Url", Description = "Url to Confluence system, including wsdl.", DefaultValue = DEFAULT_URL)]
public string Url { get; set; }
[IniProperty("UploadFormat", Description="What file type to use for uploading", DefaultValue="png")]
public OutputFormat UploadFormat {
get;
set;
}
[IniProperty("UploadJpegQuality", Description="JPEG file save quality in %.", DefaultValue="80")]
public int UploadJpegQuality {
get;
set;
}
[IniProperty("UploadReduceColors", Description="Reduce color amount of the uploaded image to 256", DefaultValue="False")]
public bool UploadReduceColors {
get;
set;
}
[IniProperty("OpenPageAfterUpload", Description="Open the page where the picture is uploaded after upload", DefaultValue="True")]
public bool OpenPageAfterUpload {
get;
set;
}
[IniProperty("CopyWikiMarkupForImageToClipboard", Description="Copy the Wikimarkup for the recently uploaded image to the Clipboard", DefaultValue="True")]
public bool CopyWikiMarkupForImageToClipboard {
get;
set;
}
[IniProperty("SearchSpaceKey", Description="Key of last space that was searched for")]
public string SearchSpaceKey {
get;
set;
}
[IniProperty("IncludePersonSpaces", Description = "Include personal spaces in the search & browse spaces list", DefaultValue = "False")]
public bool IncludePersonSpaces {
get;
set;
}
}
[IniProperty("Timeout", Description = "Session timeout in minutes", DefaultValue = "30")]
public int Timeout { get; set; }
[IniProperty("UploadFormat", Description = "What file type to use for uploading", DefaultValue = "png")]
public OutputFormat UploadFormat { get; set; }
[IniProperty("UploadJpegQuality", Description = "JPEG file save quality in %.", DefaultValue = "80")]
public int UploadJpegQuality { get; set; }
[IniProperty("UploadReduceColors", Description = "Reduce color amount of the uploaded image to 256", DefaultValue = "False")]
public bool UploadReduceColors { get; set; }
[IniProperty("OpenPageAfterUpload", Description = "Open the page where the picture is uploaded after upload", DefaultValue = "True")]
public bool OpenPageAfterUpload { get; set; }
[IniProperty("CopyWikiMarkupForImageToClipboard", Description = "Copy the Wikimarkup for the recently uploaded image to the Clipboard", DefaultValue = "True")]
public bool CopyWikiMarkupForImageToClipboard { get; set; }
[IniProperty("SearchSpaceKey", Description = "Key of last space that was searched for")]
public string SearchSpaceKey { get; set; }
[IniProperty("IncludePersonSpaces", Description = "Include personal spaces in the search & browse spaces list", DefaultValue = "False")]
public bool IncludePersonSpaces { get; set; }
}
}

View file

@ -32,174 +32,221 @@ using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
namespace Greenshot.Plugin.Confluence {
/// <summary>
/// Description of ConfluenceDestination.
/// </summary>
public class ConfluenceDestination : AbstractDestination {
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ConfluenceDestination));
private static readonly ConfluenceConfiguration ConfluenceConfig = IniConfig.GetIniSection<ConfluenceConfiguration>();
private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection<CoreConfiguration>();
private static readonly Image ConfluenceIcon;
private readonly Page _page;
namespace Greenshot.Plugin.Confluence
{
/// <summary>
/// Description of ConfluenceDestination.
/// </summary>
public class ConfluenceDestination : AbstractDestination
{
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ConfluenceDestination));
private static readonly ConfluenceConfiguration ConfluenceConfig = IniConfig.GetIniSection<ConfluenceConfiguration>();
private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection<CoreConfiguration>();
private static readonly Image ConfluenceIcon;
private readonly Page _page;
static ConfluenceDestination() {
IsInitialized = false;
try {
Uri confluenceIconUri = new Uri("/GreenshotConfluencePlugin;component/Images/Confluence.ico", UriKind.Relative);
using (Stream iconStream = Application.GetResourceStream(confluenceIconUri)?.Stream)
{
// TODO: Check what to do with the IImage
ConfluenceIcon = ImageHelper.FromStream(iconStream);
}
IsInitialized = true;
} catch (Exception ex) {
Log.ErrorFormat("Problem in the confluence static initializer: {0}", ex.Message);
}
}
public static bool IsInitialized
static ConfluenceDestination()
{
get;
private set;
IsInitialized = false;
try
{
Uri confluenceIconUri = new Uri("/GreenshotConfluencePlugin;component/Images/Confluence.ico", UriKind.Relative);
using (Stream iconStream = Application.GetResourceStream(confluenceIconUri)?.Stream)
{
// TODO: Check what to do with the IImage
ConfluenceIcon = ImageHelper.FromStream(iconStream);
}
IsInitialized = true;
}
catch (Exception ex)
{
Log.ErrorFormat("Problem in the confluence static initializer: {0}", ex.Message);
}
}
public ConfluenceDestination() {
}
public static bool IsInitialized { get; private set; }
public ConfluenceDestination(Page page) {
_page = page;
}
public ConfluenceDestination()
{
}
public override string Designation {
get {
return "Confluence";
}
}
public ConfluenceDestination(Page page)
{
_page = page;
}
public override string Description {
get {
if (_page == null) {
return Language.GetString("confluence", LangKey.upload_menu_item);
} else {
return Language.GetString("confluence", LangKey.upload_menu_item) + ": \"" + _page.Title + "\"";
}
}
}
public override string Designation
{
get { return "Confluence"; }
}
public override bool IsDynamic {
get {
return true;
}
}
public override string Description
{
get
{
if (_page == null)
{
return Language.GetString("confluence", LangKey.upload_menu_item);
}
else
{
return Language.GetString("confluence", LangKey.upload_menu_item) + ": \"" + _page.Title + "\"";
}
}
}
public override bool IsActive {
get {
return base.IsActive && !string.IsNullOrEmpty(ConfluenceConfig.Url);
}
}
public override bool IsDynamic
{
get { return true; }
}
public override Image DisplayIcon {
get {
return ConfluenceIcon;
}
}
public override bool IsActive
{
get { return base.IsActive && !string.IsNullOrEmpty(ConfluenceConfig.Url); }
}
public override IEnumerable<IDestination> DynamicDestinations() {
if (ConfluencePlugin.ConfluenceConnectorNoLogin == null || !ConfluencePlugin.ConfluenceConnectorNoLogin.IsLoggedIn) {
yield break;
}
List<Page> currentPages = ConfluenceUtils.GetCurrentPages();
if (currentPages == null || currentPages.Count == 0) {
yield break;
}
foreach(Page currentPage in currentPages) {
yield return new ConfluenceDestination(currentPage);
}
}
public override Image DisplayIcon
{
get { return ConfluenceIcon; }
}
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) {
ExportInformation exportInformation = new ExportInformation(Designation, Description);
// force password check to take place before the pages load
if (!ConfluencePlugin.ConfluenceConnector.IsLoggedIn) {
return exportInformation;
}
public override IEnumerable<IDestination> DynamicDestinations()
{
if (ConfluencePlugin.ConfluenceConnectorNoLogin == null || !ConfluencePlugin.ConfluenceConnectorNoLogin.IsLoggedIn)
{
yield break;
}
Page selectedPage = _page;
bool openPage = (_page == null) && ConfluenceConfig.OpenPageAfterUpload;
string filename = FilenameHelper.GetFilenameWithoutExtensionFromPattern(CoreConfig.OutputFileFilenamePattern, captureDetails);
if (selectedPage == null) {
Forms.ConfluenceUpload confluenceUpload = new Forms.ConfluenceUpload(filename);
bool? dialogResult = confluenceUpload.ShowDialog();
if (dialogResult.HasValue && dialogResult.Value) {
selectedPage = confluenceUpload.SelectedPage;
if (confluenceUpload.IsOpenPageSelected) {
openPage = false;
}
filename = confluenceUpload.Filename;
}
}
string extension = "." + ConfluenceConfig.UploadFormat;
if (!filename.ToLower().EndsWith(extension)) {
filename += extension;
}
if (selectedPage != null) {
List<Page> currentPages = ConfluenceUtils.GetCurrentPages();
if (currentPages == null || currentPages.Count == 0)
{
yield break;
}
foreach (Page currentPage in currentPages)
{
yield return new ConfluenceDestination(currentPage);
}
}
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails)
{
ExportInformation exportInformation = new ExportInformation(Designation, Description);
// force password check to take place before the pages load
if (!ConfluencePlugin.ConfluenceConnector.IsLoggedIn)
{
return exportInformation;
}
Page selectedPage = _page;
bool openPage = (_page == null) && ConfluenceConfig.OpenPageAfterUpload;
string filename = FilenameHelper.GetFilenameWithoutExtensionFromPattern(CoreConfig.OutputFileFilenamePattern, captureDetails);
if (selectedPage == null)
{
Forms.ConfluenceUpload confluenceUpload = new Forms.ConfluenceUpload(filename);
bool? dialogResult = confluenceUpload.ShowDialog();
if (dialogResult.HasValue && dialogResult.Value)
{
selectedPage = confluenceUpload.SelectedPage;
if (confluenceUpload.IsOpenPageSelected)
{
openPage = false;
}
filename = confluenceUpload.Filename;
}
}
string extension = "." + ConfluenceConfig.UploadFormat;
if (!filename.ToLower().EndsWith(extension))
{
filename += extension;
}
if (selectedPage != null)
{
bool uploaded = Upload(surface, selectedPage, filename, out var errorMessage);
if (uploaded) {
if (openPage) {
try
{
Process.Start(selectedPage.Url);
}
catch
{
// Ignore
}
}
exportInformation.ExportMade = true;
exportInformation.Uri = selectedPage.Url;
} else {
exportInformation.ErrorMessage = errorMessage;
}
}
ProcessExport(exportInformation, surface);
return exportInformation;
}
if (uploaded)
{
if (openPage)
{
try
{
Process.Start(selectedPage.Url);
}
catch
{
// Ignore
}
}
private bool Upload(ISurface surfaceToUpload, Page page, string filename, out string errorMessage) {
SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(ConfluenceConfig.UploadFormat, ConfluenceConfig.UploadJpegQuality, ConfluenceConfig.UploadReduceColors);
errorMessage = null;
try {
new PleaseWaitForm().ShowAndWait(Description, Language.GetString("confluence", LangKey.communication_wait),
delegate {
ConfluencePlugin.ConfluenceConnector.AddAttachment(page.Id, "image/" + ConfluenceConfig.UploadFormat.ToString().ToLower(), null, filename, new SurfaceContainer(surfaceToUpload, outputSettings, filename));
}
);
Log.Debug("Uploaded to Confluence.");
if (!ConfluenceConfig.CopyWikiMarkupForImageToClipboard)
{
return true;
}
int retryCount = 2;
while (retryCount >= 0) {
try {
Clipboard.SetText("!" + filename + "!");
break;
} catch (Exception ee) {
if (retryCount == 0) {
Log.Error(ee);
} else {
Thread.Sleep(100);
}
} finally {
--retryCount;
}
}
return true;
} catch(Exception e) {
errorMessage = e.Message;
}
return false;
}
}
exportInformation.ExportMade = true;
exportInformation.Uri = selectedPage.Url;
}
else
{
exportInformation.ErrorMessage = errorMessage;
}
}
ProcessExport(exportInformation, surface);
return exportInformation;
}
private bool Upload(ISurface surfaceToUpload, Page page, string filename, out string errorMessage)
{
SurfaceOutputSettings outputSettings =
new SurfaceOutputSettings(ConfluenceConfig.UploadFormat, ConfluenceConfig.UploadJpegQuality, ConfluenceConfig.UploadReduceColors);
errorMessage = null;
try
{
new PleaseWaitForm().ShowAndWait(Description, Language.GetString("confluence", LangKey.communication_wait),
delegate
{
ConfluencePlugin.ConfluenceConnector.AddAttachment(page.Id, "image/" + ConfluenceConfig.UploadFormat.ToString().ToLower(), null, filename,
new SurfaceContainer(surfaceToUpload, outputSettings, filename));
}
);
Log.Debug("Uploaded to Confluence.");
if (!ConfluenceConfig.CopyWikiMarkupForImageToClipboard)
{
return true;
}
int retryCount = 2;
while (retryCount >= 0)
{
try
{
Clipboard.SetText("!" + filename + "!");
break;
}
catch (Exception ee)
{
if (retryCount == 0)
{
Log.Error(ee);
}
else
{
Thread.Sleep(100);
}
}
finally
{
--retryCount;
}
}
return true;
}
catch (Exception e)
{
errorMessage = e.Message;
}
return false;
}
}
}

View file

@ -28,109 +28,142 @@ using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
namespace Greenshot.Plugin.Confluence {
/// <summary>
/// This is the ConfluencePlugin base code
/// </summary>
[Plugin("Confluence", true)]
public class ConfluencePlugin : IGreenshotPlugin {
private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(ConfluencePlugin));
private static ConfluenceConnector _confluenceConnector;
private static ConfluenceConfiguration _config;
namespace Greenshot.Plugin.Confluence
{
/// <summary>
/// This is the ConfluencePlugin base code
/// </summary>
[Plugin("Confluence", true)]
public class ConfluencePlugin : IGreenshotPlugin
{
private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(ConfluencePlugin));
private static ConfluenceConnector _confluenceConnector;
private static ConfluenceConfiguration _config;
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(bool disposing) {
//if (disposing) {}
}
protected void Dispose(bool disposing)
{
//if (disposing) {}
}
private static void CreateConfluenceConnector() {
if (_confluenceConnector == null) {
if (_config.Url.Contains("soap-axis")) {
_confluenceConnector = new ConfluenceConnector(_config.Url, _config.Timeout);
} else {
_confluenceConnector = new ConfluenceConnector(_config.Url + ConfluenceConfiguration.DEFAULT_POSTFIX2, _config.Timeout);
}
}
}
private static void CreateConfluenceConnector()
{
if (_confluenceConnector == null)
{
if (_config.Url.Contains("soap-axis"))
{
_confluenceConnector = new ConfluenceConnector(_config.Url, _config.Timeout);
}
else
{
_confluenceConnector = new ConfluenceConnector(_config.Url + ConfluenceConfiguration.DEFAULT_POSTFIX2, _config.Timeout);
}
}
}
public static ConfluenceConnector ConfluenceConnectorNoLogin {
get {
return _confluenceConnector;
}
}
public static ConfluenceConnector ConfluenceConnectorNoLogin
{
get { return _confluenceConnector; }
}
public static ConfluenceConnector ConfluenceConnector {
get {
if (_confluenceConnector == null) {
CreateConfluenceConnector();
}
try {
if (_confluenceConnector != null && !_confluenceConnector.IsLoggedIn) {
_confluenceConnector.Login();
}
} catch (Exception e) {
MessageBox.Show(Language.GetFormattedString("confluence", LangKey.login_error, e.Message));
}
return _confluenceConnector;
}
}
public static ConfluenceConnector ConfluenceConnector
{
get
{
if (_confluenceConnector == null)
{
CreateConfluenceConnector();
}
try
{
if (_confluenceConnector != null && !_confluenceConnector.IsLoggedIn)
{
_confluenceConnector.Login();
}
}
catch (Exception e)
{
MessageBox.Show(Language.GetFormattedString("confluence", LangKey.login_error, e.Message));
}
return _confluenceConnector;
}
}
/// <summary>
/// Implementation of the IGreenshotPlugin.Initialize
/// </summary>
public bool Initialize()
{
// Register configuration (don't need the configuration itself)
_config = IniConfig.GetIniSection<ConfluenceConfiguration>();
if (_config.IsDirty)
{
IniConfig.Save();
}
try
{
TranslationManager.Instance.TranslationProvider = new LanguageXMLTranslationProvider();
//resources = new ComponentResourceManager(typeof(ConfluencePlugin));
}
catch (Exception ex)
{
LOG.ErrorFormat("Problem in ConfluencePlugin.Initialize: {0}", ex.Message);
return false;
}
/// <summary>
/// Implementation of the IGreenshotPlugin.Initialize
/// </summary>
public bool Initialize() {
// Register configuration (don't need the configuration itself)
_config = IniConfig.GetIniSection<ConfluenceConfiguration>();
if(_config.IsDirty) {
IniConfig.Save();
}
try {
TranslationManager.Instance.TranslationProvider = new LanguageXMLTranslationProvider();
//resources = new ComponentResourceManager(typeof(ConfluencePlugin));
} catch (Exception ex) {
LOG.ErrorFormat("Problem in ConfluencePlugin.Initialize: {0}", ex.Message);
return false;
}
if (ConfluenceDestination.IsInitialized)
{
SimpleServiceProvider.Current.AddService<IDestination>(new ConfluenceDestination());
}
return true;
}
public void Shutdown() {
LOG.Debug("Confluence Plugin shutdown.");
if (_confluenceConnector != null) {
_confluenceConnector.Logout();
_confluenceConnector = null;
}
}
return true;
}
/// <summary>
/// Implementation of the IPlugin.Configure
/// </summary>
public void Configure() {
ConfluenceConfiguration clonedConfig = _config.Clone();
ConfluenceConfigurationForm configForm = new ConfluenceConfigurationForm(clonedConfig);
string url = _config.Url;
bool? dialogResult = configForm.ShowDialog();
if (dialogResult.HasValue && dialogResult.Value) {
// copy the new object to the old...
clonedConfig.CloneTo(_config);
IniConfig.Save();
if (_confluenceConnector != null) {
if (!url.Equals(_config.Url)) {
if (_confluenceConnector.IsLoggedIn) {
_confluenceConnector.Logout();
}
_confluenceConnector = null;
}
}
}
}
}
public void Shutdown()
{
LOG.Debug("Confluence Plugin shutdown.");
if (_confluenceConnector != null)
{
_confluenceConnector.Logout();
_confluenceConnector = null;
}
}
/// <summary>
/// Implementation of the IPlugin.Configure
/// </summary>
public void Configure()
{
ConfluenceConfiguration clonedConfig = _config.Clone();
ConfluenceConfigurationForm configForm = new ConfluenceConfigurationForm(clonedConfig);
string url = _config.Url;
bool? dialogResult = configForm.ShowDialog();
if (dialogResult.HasValue && dialogResult.Value)
{
// copy the new object to the old...
clonedConfig.CloneTo(_config);
IniConfig.Save();
if (_confluenceConnector != null)
{
if (!url.Equals(_config.Url))
{
if (_confluenceConnector.IsLoggedIn)
{
_confluenceConnector.Logout();
}
_confluenceConnector = null;
}
}
}
}
}
}

View file

@ -26,126 +26,172 @@ using System.Text.RegularExpressions;
using System.Windows.Automation;
using GreenshotPlugin.Core;
namespace Greenshot.Plugin.Confluence {
/// <summary>
/// Description of ConfluenceUtils.
/// </summary>
public class ConfluenceUtils {
private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(ConfluenceUtils));
namespace Greenshot.Plugin.Confluence
{
/// <summary>
/// Description of ConfluenceUtils.
/// </summary>
public class ConfluenceUtils
{
private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(ConfluenceUtils));
public static List<Page> GetCurrentPages() {
List<Page> pages = new List<Page>();
Regex pageIdRegex = new Regex(@"pageId=(\d+)");
Regex spacePageRegex = new Regex(@"\/display\/([^\/]+)\/([^#]+)");
foreach(string browserurl in GetBrowserUrls()) {
string url;
try {
url = Uri.UnescapeDataString(browserurl).Replace("+", " ");
} catch {
LOG.WarnFormat("Error processing URL: {0}", browserurl);
continue;
}
MatchCollection pageIdMatch = pageIdRegex.Matches(url);
if (pageIdMatch != null && pageIdMatch.Count > 0) {
long pageId = long.Parse(pageIdMatch[0].Groups[1].Value);
try {
bool pageDouble = false;
foreach(Page page in pages) {
if (page.Id == pageId) {
pageDouble = true;
LOG.DebugFormat("Skipping double page with ID {0}", pageId);
break;
}
}
if (!pageDouble) {
Page page = ConfluencePlugin.ConfluenceConnector.GetPage(pageId);
LOG.DebugFormat("Adding page {0}", page.Title);
pages.Add(page);
}
continue;
} catch (Exception ex) {
// Preventing security problems
LOG.DebugFormat("Couldn't get page details for PageID {0}", pageId);
LOG.Warn(ex);
}
}
MatchCollection spacePageMatch = spacePageRegex.Matches(url);
if (spacePageMatch != null && spacePageMatch.Count > 0) {
if (spacePageMatch[0].Groups.Count >= 2) {
string space = spacePageMatch[0].Groups[1].Value;
string title = spacePageMatch[0].Groups[2].Value;
if (string.IsNullOrEmpty(title) || string.IsNullOrEmpty(space)) {
continue;
}
if (title.EndsWith("#")) {
title = title.Substring(0, title.Length-1);
}
try {
bool pageDouble = false;
foreach(Page page in pages) {
if (page.Title.Equals(title)) {
LOG.DebugFormat("Skipping double page with title {0}", title);
pageDouble = true;
break;
}
}
if (!pageDouble) {
Page page = ConfluencePlugin.ConfluenceConnector.GetPage(space, title);
LOG.DebugFormat("Adding page {0}", page.Title);
pages.Add(page);
public static List<Page> GetCurrentPages()
{
List<Page> pages = new List<Page>();
Regex pageIdRegex = new Regex(@"pageId=(\d+)");
Regex spacePageRegex = new Regex(@"\/display\/([^\/]+)\/([^#]+)");
foreach (string browserurl in GetBrowserUrls())
{
string url;
try
{
url = Uri.UnescapeDataString(browserurl).Replace("+", " ");
}
catch
{
LOG.WarnFormat("Error processing URL: {0}", browserurl);
continue;
}
}
} catch (Exception ex) {
// Preventing security problems
LOG.DebugFormat("Couldn't get page details for space {0} / title {1}", space, title);
LOG.Warn(ex);
}
}
}
}
return pages;
}
MatchCollection pageIdMatch = pageIdRegex.Matches(url);
if (pageIdMatch != null && pageIdMatch.Count > 0)
{
long pageId = long.Parse(pageIdMatch[0].Groups[1].Value);
try
{
bool pageDouble = false;
foreach (Page page in pages)
{
if (page.Id == pageId)
{
pageDouble = true;
LOG.DebugFormat("Skipping double page with ID {0}", pageId);
break;
}
}
private static IEnumerable<string> GetBrowserUrls() {
HashSet<string> urls = new HashSet<string>();
if (!pageDouble)
{
Page page = ConfluencePlugin.ConfluenceConnector.GetPage(pageId);
LOG.DebugFormat("Adding page {0}", page.Title);
pages.Add(page);
}
// FireFox
foreach (WindowDetails window in WindowDetails.GetAllWindows("MozillaWindowClass")) {
if (window.Text.Length == 0) {
continue;
}
AutomationElement currentElement = AutomationElement.FromHandle(window.Handle);
Condition conditionCustom = new AndCondition(new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Custom), new PropertyCondition(AutomationElement.IsOffscreenProperty, false));
for (int i = 5; i > 0 && currentElement != null; i--) {
currentElement = currentElement.FindFirst(TreeScope.Children, conditionCustom);
}
if (currentElement == null) {
continue;
}
continue;
}
catch (Exception ex)
{
// Preventing security problems
LOG.DebugFormat("Couldn't get page details for PageID {0}", pageId);
LOG.Warn(ex);
}
}
Condition conditionDocument = new AndCondition(new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Document), new PropertyCondition(AutomationElement.IsOffscreenProperty, false));
AutomationElement docElement = currentElement.FindFirst(TreeScope.Children, conditionDocument);
if (docElement == null) {
continue;
}
foreach (AutomationPattern pattern in docElement.GetSupportedPatterns()) {
if (pattern.ProgrammaticName != "ValuePatternIdentifiers.Pattern") {
continue;
}
string url = (docElement.GetCurrentPattern(pattern) as ValuePattern).Current.Value;
if (!string.IsNullOrEmpty(url)) {
urls.Add(url);
break;
}
}
}
MatchCollection spacePageMatch = spacePageRegex.Matches(url);
if (spacePageMatch != null && spacePageMatch.Count > 0)
{
if (spacePageMatch[0].Groups.Count >= 2)
{
string space = spacePageMatch[0].Groups[1].Value;
string title = spacePageMatch[0].Groups[2].Value;
if (string.IsNullOrEmpty(title) || string.IsNullOrEmpty(space))
{
continue;
}
foreach(string url in IEHelper.GetIEUrls().Distinct()) {
urls.Add(url);
}
if (title.EndsWith("#"))
{
title = title.Substring(0, title.Length - 1);
}
return urls;
}
try
{
bool pageDouble = false;
foreach (Page page in pages)
{
if (page.Title.Equals(title))
{
LOG.DebugFormat("Skipping double page with title {0}", title);
pageDouble = true;
break;
}
}
}
if (!pageDouble)
{
Page page = ConfluencePlugin.ConfluenceConnector.GetPage(space, title);
LOG.DebugFormat("Adding page {0}", page.Title);
pages.Add(page);
}
}
catch (Exception ex)
{
// Preventing security problems
LOG.DebugFormat("Couldn't get page details for space {0} / title {1}", space, title);
LOG.Warn(ex);
}
}
}
}
return pages;
}
private static IEnumerable<string> GetBrowserUrls()
{
HashSet<string> urls = new HashSet<string>();
// FireFox
foreach (WindowDetails window in WindowDetails.GetAllWindows("MozillaWindowClass"))
{
if (window.Text.Length == 0)
{
continue;
}
AutomationElement currentElement = AutomationElement.FromHandle(window.Handle);
Condition conditionCustom = new AndCondition(new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Custom),
new PropertyCondition(AutomationElement.IsOffscreenProperty, false));
for (int i = 5; i > 0 && currentElement != null; i--)
{
currentElement = currentElement.FindFirst(TreeScope.Children, conditionCustom);
}
if (currentElement == null)
{
continue;
}
Condition conditionDocument = new AndCondition(new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Document),
new PropertyCondition(AutomationElement.IsOffscreenProperty, false));
AutomationElement docElement = currentElement.FindFirst(TreeScope.Children, conditionDocument);
if (docElement == null)
{
continue;
}
foreach (AutomationPattern pattern in docElement.GetSupportedPatterns())
{
if (pattern.ProgrammaticName != "ValuePatternIdentifiers.Pattern")
{
continue;
}
string url = (docElement.GetCurrentPattern(pattern) as ValuePattern).Current.Value;
if (!string.IsNullOrEmpty(url))
{
urls.Add(url);
break;
}
}
}
foreach (string url in IEHelper.GetIEUrls().Distinct())
{
urls.Add(url);
}
return urls;
}
}
}

View file

@ -28,70 +28,87 @@ using System.Reflection;
using System.Windows.Data;
using GreenshotPlugin.Core;
namespace Greenshot.Plugin.Confluence {
public class EnumDisplayer : IValueConverter {
private Type _type;
private IDictionary _displayValues;
private IDictionary _reverseValues;
namespace Greenshot.Plugin.Confluence
{
public class EnumDisplayer : IValueConverter
{
private Type _type;
private IDictionary _displayValues;
private IDictionary _reverseValues;
public Type Type {
get { return _type; }
set {
if (!value.IsEnum) {
throw new ArgumentException("parameter is not an Enumerated type", nameof(value));
}
_type = value;
}
}
public Type Type
{
get { return _type; }
set
{
if (!value.IsEnum)
{
throw new ArgumentException("parameter is not an Enumerated type", nameof(value));
}
public ReadOnlyCollection<string> DisplayNames {
get {
var genericTypeDefinition = typeof(Dictionary<,>).GetGenericTypeDefinition();
if (genericTypeDefinition != null)
{
_reverseValues = (IDictionary) Activator.CreateInstance(genericTypeDefinition.MakeGenericType(typeof(string),_type));
}
_type = value;
}
}
var typeDefinition = typeof(Dictionary<,>).GetGenericTypeDefinition();
if (typeDefinition != null)
{
_displayValues = (IDictionary)Activator.CreateInstance(typeDefinition.MakeGenericType(_type, typeof(string)));
}
public ReadOnlyCollection<string> DisplayNames
{
get
{
var genericTypeDefinition = typeof(Dictionary<,>).GetGenericTypeDefinition();
if (genericTypeDefinition != null)
{
_reverseValues = (IDictionary) Activator.CreateInstance(genericTypeDefinition.MakeGenericType(typeof(string), _type));
}
var fields = _type.GetFields(BindingFlags.Public | BindingFlags.Static);
foreach (var field in fields) {
DisplayKeyAttribute[] a = (DisplayKeyAttribute[])field.GetCustomAttributes(typeof(DisplayKeyAttribute), false);
var typeDefinition = typeof(Dictionary<,>).GetGenericTypeDefinition();
if (typeDefinition != null)
{
_displayValues = (IDictionary) Activator.CreateInstance(typeDefinition.MakeGenericType(_type, typeof(string)));
}
string displayKey = GetDisplayKeyValue(a);
object enumValue = field.GetValue(null);
var fields = _type.GetFields(BindingFlags.Public | BindingFlags.Static);
foreach (var field in fields)
{
DisplayKeyAttribute[] a = (DisplayKeyAttribute[]) field.GetCustomAttributes(typeof(DisplayKeyAttribute), false);
string displayString;
if (displayKey != null && Language.HasKey(displayKey)) {
displayString = Language.GetString(displayKey);
}
displayString = displayKey ?? enumValue.ToString();
string displayKey = GetDisplayKeyValue(a);
object enumValue = field.GetValue(null);
_displayValues.Add(enumValue, displayString);
_reverseValues.Add(displayString, enumValue);
}
return new List<string>((IEnumerable<string>)_displayValues.Values).AsReadOnly();
}
}
string displayString;
if (displayKey != null && Language.HasKey(displayKey))
{
displayString = Language.GetString(displayKey);
}
private static string GetDisplayKeyValue(DisplayKeyAttribute[] a) {
if (a == null || a.Length == 0) {
return null;
}
DisplayKeyAttribute dka = a[0];
return dka.Value;
}
displayString = displayKey ?? enumValue.ToString();
object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture) {
return _displayValues[value];
}
_displayValues.Add(enumValue, displayString);
_reverseValues.Add(displayString, enumValue);
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
return _reverseValues[value];
}
}
return new List<string>((IEnumerable<string>) _displayValues.Values).AsReadOnly();
}
}
private static string GetDisplayKeyValue(DisplayKeyAttribute[] a)
{
if (a == null || a.Length == 0)
{
return null;
}
DisplayKeyAttribute dka = a[0];
return dka.Value;
}
object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return _displayValues[value];
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return _reverseValues[value];
}
}
}

View file

@ -21,21 +21,25 @@
using System.Windows;
namespace Greenshot.Plugin.Confluence.Forms {
/// <summary>
/// Interaction logic for ConfluenceConfigurationForm.xaml
/// </summary>
public partial class ConfluenceConfigurationForm : Window {
public ConfluenceConfiguration Config { get; }
namespace Greenshot.Plugin.Confluence.Forms
{
/// <summary>
/// Interaction logic for ConfluenceConfigurationForm.xaml
/// </summary>
public partial class ConfluenceConfigurationForm : Window
{
public ConfluenceConfiguration Config { get; }
public ConfluenceConfigurationForm(ConfluenceConfiguration config) {
DataContext = config;
Config = config;
InitializeComponent();
}
public ConfluenceConfigurationForm(ConfluenceConfiguration config)
{
DataContext = config;
Config = config;
InitializeComponent();
}
private void Button_OK_Click(object sender, RoutedEventArgs e) {
DialogResult = true;
}
}
private void Button_OK_Click(object sender, RoutedEventArgs e)
{
DialogResult = true;
}
}
}

View file

@ -21,36 +21,44 @@
using System.Collections.Generic;
namespace Greenshot.Plugin.Confluence.Forms {
/// <summary>
/// Interaction logic for ConfluencePagePicker.xaml
/// </summary>
public partial class ConfluencePagePicker
{
private readonly ConfluenceUpload _confluenceUpload;
namespace Greenshot.Plugin.Confluence.Forms
{
/// <summary>
/// Interaction logic for ConfluencePagePicker.xaml
/// </summary>
public partial class ConfluencePagePicker
{
private readonly ConfluenceUpload _confluenceUpload;
public ConfluencePagePicker(ConfluenceUpload confluenceUpload, List<Page> pagesToPick) {
_confluenceUpload = confluenceUpload;
DataContext = pagesToPick;
InitializeComponent();
}
public ConfluencePagePicker(ConfluenceUpload confluenceUpload, List<Page> pagesToPick)
{
_confluenceUpload = confluenceUpload;
DataContext = pagesToPick;
InitializeComponent();
}
private void PageListView_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) {
SelectionChanged();
}
private void PageListView_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
SelectionChanged();
}
private void SelectionChanged() {
if (PageListView.HasItems && PageListView.SelectedItems.Count > 0) {
_confluenceUpload.SelectedPage = (Page)PageListView.SelectedItem;
// Make sure the uploader knows we selected an already opened page
_confluenceUpload.IsOpenPageSelected = true;
} else {
_confluenceUpload.SelectedPage = null;
}
}
private void SelectionChanged()
{
if (PageListView.HasItems && PageListView.SelectedItems.Count > 0)
{
_confluenceUpload.SelectedPage = (Page) PageListView.SelectedItem;
// Make sure the uploader knows we selected an already opened page
_confluenceUpload.IsOpenPageSelected = true;
}
else
{
_confluenceUpload.SelectedPage = null;
}
}
private void Page_Loaded(object sender, System.Windows.RoutedEventArgs e) {
SelectionChanged();
}
}
private void Page_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
SelectionChanged();
}
}
}

View file

@ -25,69 +25,88 @@ using System.Linq;
using System.Windows;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.Confluence.Forms {
public partial class ConfluenceSearch
{
private static readonly ConfluenceConfiguration ConfluenceConfig = IniConfig.GetIniSection<ConfluenceConfiguration>();
private readonly ConfluenceUpload _confluenceUpload;
namespace Greenshot.Plugin.Confluence.Forms
{
public partial class ConfluenceSearch
{
private static readonly ConfluenceConfiguration ConfluenceConfig = IniConfig.GetIniSection<ConfluenceConfiguration>();
private readonly ConfluenceUpload _confluenceUpload;
public IEnumerable<Space> Spaces => _confluenceUpload.Spaces;
public IEnumerable<Space> Spaces => _confluenceUpload.Spaces;
public ObservableCollection<Page> Pages { get; } = new ObservableCollection<Page>();
public ObservableCollection<Page> Pages { get; } = new ObservableCollection<Page>();
public ConfluenceSearch(ConfluenceUpload confluenceUpload) {
_confluenceUpload = confluenceUpload;
DataContext = this;
InitializeComponent();
if (ConfluenceConfig.SearchSpaceKey == null) {
SpaceComboBox.SelectedItem = Spaces.FirstOrDefault();
} else {
foreach(var space in Spaces) {
if (space.Key.Equals(ConfluenceConfig.SearchSpaceKey)) {
SpaceComboBox.SelectedItem = space;
}
}
}
}
public ConfluenceSearch(ConfluenceUpload confluenceUpload)
{
_confluenceUpload = confluenceUpload;
DataContext = this;
InitializeComponent();
if (ConfluenceConfig.SearchSpaceKey == null)
{
SpaceComboBox.SelectedItem = Spaces.FirstOrDefault();
}
else
{
foreach (var space in Spaces)
{
if (space.Key.Equals(ConfluenceConfig.SearchSpaceKey))
{
SpaceComboBox.SelectedItem = space;
}
}
}
}
private void PageListView_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) {
SelectionChanged();
}
private void PageListView_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
SelectionChanged();
}
private void SelectionChanged() {
if (PageListView.HasItems && PageListView.SelectedItems.Count > 0) {
_confluenceUpload.SelectedPage = (Page)PageListView.SelectedItem;
} else {
_confluenceUpload.SelectedPage = null;
}
}
private void SelectionChanged()
{
if (PageListView.HasItems && PageListView.SelectedItems.Count > 0)
{
_confluenceUpload.SelectedPage = (Page) PageListView.SelectedItem;
}
else
{
_confluenceUpload.SelectedPage = null;
}
}
private void Search_Click(object sender, RoutedEventArgs e) {
DoSearch();
}
private void Search_Click(object sender, RoutedEventArgs e)
{
DoSearch();
}
private void DoSearch() {
string spaceKey = (string)SpaceComboBox.SelectedValue;
ConfluenceConfig.SearchSpaceKey = spaceKey;
Pages.Clear();
foreach(var page in ConfluencePlugin.ConfluenceConnector.SearchPages(searchText.Text, spaceKey).OrderBy(p => p.Title)) {
Pages.Add(page);
}
}
private void DoSearch()
{
string spaceKey = (string) SpaceComboBox.SelectedValue;
ConfluenceConfig.SearchSpaceKey = spaceKey;
Pages.Clear();
foreach (var page in ConfluencePlugin.ConfluenceConnector.SearchPages(searchText.Text, spaceKey).OrderBy(p => p.Title))
{
Pages.Add(page);
}
}
private void SearchText_KeyDown(object sender, System.Windows.Input.KeyEventArgs e) {
if (e.Key == System.Windows.Input.Key.Return && Search.IsEnabled) {
DoSearch();
e.Handled = true;
}
}
private void SearchText_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == System.Windows.Input.Key.Return && Search.IsEnabled)
{
DoSearch();
e.Handled = true;
}
}
private void Page_Loaded(object sender, RoutedEventArgs e) {
SelectionChanged();
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
SelectionChanged();
}
private void SearchText_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e) {
Search.IsEnabled = !string.IsNullOrEmpty(searchText.Text);
}
}
private void SearchText_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
Search.IsEnabled = !string.IsNullOrEmpty(searchText.Text);
}
}
}

View file

@ -27,101 +27,131 @@ using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
namespace Greenshot.Plugin.Confluence.Forms {
/// <summary>
/// Interaction logic for ConfluenceTreePicker.xaml
/// </summary>
public partial class ConfluenceTreePicker
{
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ConfluenceTreePicker));
private readonly ConfluenceConnector _confluenceConnector;
private readonly ConfluenceUpload _confluenceUpload;
private bool _isInitDone;
namespace Greenshot.Plugin.Confluence.Forms
{
/// <summary>
/// Interaction logic for ConfluenceTreePicker.xaml
/// </summary>
public partial class ConfluenceTreePicker
{
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ConfluenceTreePicker));
private readonly ConfluenceConnector _confluenceConnector;
private readonly ConfluenceUpload _confluenceUpload;
private bool _isInitDone;
public ConfluenceTreePicker(ConfluenceUpload confluenceUpload) {
_confluenceConnector = ConfluencePlugin.ConfluenceConnector;
_confluenceUpload = confluenceUpload;
InitializeComponent();
}
public ConfluenceTreePicker(ConfluenceUpload confluenceUpload)
{
_confluenceConnector = ConfluencePlugin.ConfluenceConnector;
_confluenceUpload = confluenceUpload;
InitializeComponent();
}
private void PageTreeViewItem_DoubleClick(object sender, MouseButtonEventArgs eventArgs) {
Log.Debug("spaceTreeViewItem_MouseLeftButtonDown is called!");
TreeViewItem clickedItem = eventArgs.Source as TreeViewItem;
if (clickedItem?.Tag is not Page page) {
return;
}
if (clickedItem.HasItems)
{
return;
}
Log.Debug("Loading pages for page: " + page.Title);
new Thread(() => {
Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)(() => {ShowBusy.Visibility = Visibility.Visible;}));
var pages = _confluenceConnector.GetPageChildren(page).OrderBy(p => p.Title);
Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)(() => {
foreach(var childPage in pages) {
Log.Debug("Adding page: " + childPage.Title);
var pageTreeViewItem = new TreeViewItem
{
Header = childPage.Title,
Tag = childPage
};
clickedItem.Items.Add(pageTreeViewItem);
pageTreeViewItem.PreviewMouseDoubleClick += PageTreeViewItem_DoubleClick;
pageTreeViewItem.PreviewMouseLeftButtonDown += PageTreeViewItem_Click;
}
ShowBusy.Visibility = Visibility.Collapsed;
}));
}) { Name = "Loading childpages for confluence page " + page.Title }.Start();
}
private void PageTreeViewItem_DoubleClick(object sender, MouseButtonEventArgs eventArgs)
{
Log.Debug("spaceTreeViewItem_MouseLeftButtonDown is called!");
TreeViewItem clickedItem = eventArgs.Source as TreeViewItem;
if (clickedItem?.Tag is not Page page)
{
return;
}
private void PageTreeViewItem_Click(object sender, MouseButtonEventArgs eventArgs) {
Log.Debug("pageTreeViewItem_PreviewMouseDoubleClick is called!");
if (eventArgs.Source is not TreeViewItem clickedItem) {
return;
}
Page page = clickedItem.Tag as Page;
_confluenceUpload.SelectedPage = page;
if (page != null) {
Log.Debug("Page selected: " + page.Title);
}
}
if (clickedItem.HasItems)
{
return;
}
private void Page_Loaded(object sender, RoutedEventArgs e) {
_confluenceUpload.SelectedPage = null;
if (_isInitDone) {
return;
}
ShowBusy.Visibility = Visibility.Visible;
new Thread(() => {
Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)(() => {
foreach (Space space in _confluenceUpload.Spaces) {
TreeViewItem spaceTreeViewItem = new TreeViewItem
{
Header = space.Name,
Tag = space
};
Log.Debug("Loading pages for page: " + page.Title);
new Thread(() =>
{
Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart) (() => { ShowBusy.Visibility = Visibility.Visible; }));
var pages = _confluenceConnector.GetPageChildren(page).OrderBy(p => p.Title);
Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart) (() =>
{
foreach (var childPage in pages)
{
Log.Debug("Adding page: " + childPage.Title);
var pageTreeViewItem = new TreeViewItem
{
Header = childPage.Title,
Tag = childPage
};
clickedItem.Items.Add(pageTreeViewItem);
pageTreeViewItem.PreviewMouseDoubleClick += PageTreeViewItem_DoubleClick;
pageTreeViewItem.PreviewMouseLeftButtonDown += PageTreeViewItem_Click;
}
// Get homepage
try {
Page page = _confluenceConnector.GetSpaceHomepage(space);
TreeViewItem pageTreeViewItem = new TreeViewItem
{
Header = page.Title,
Tag = page
};
pageTreeViewItem.PreviewMouseDoubleClick += PageTreeViewItem_DoubleClick;
pageTreeViewItem.PreviewMouseLeftButtonDown += PageTreeViewItem_Click;
spaceTreeViewItem.Items.Add(pageTreeViewItem);
ConfluenceTreeView.Items.Add(spaceTreeViewItem);
} catch (Exception ex) {
Log.Error("Can't get homepage for space : " + space.Name + " (" + ex.Message + ")");
}
}
ShowBusy.Visibility = Visibility.Collapsed;
_isInitDone = true;
}));
}) { Name = "Loading spaces for confluence"}.Start();
}
}
ShowBusy.Visibility = Visibility.Collapsed;
}));
})
{
Name = "Loading childpages for confluence page " + page.Title
}.Start();
}
private void PageTreeViewItem_Click(object sender, MouseButtonEventArgs eventArgs)
{
Log.Debug("pageTreeViewItem_PreviewMouseDoubleClick is called!");
if (eventArgs.Source is not TreeViewItem clickedItem)
{
return;
}
Page page = clickedItem.Tag as Page;
_confluenceUpload.SelectedPage = page;
if (page != null)
{
Log.Debug("Page selected: " + page.Title);
}
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
_confluenceUpload.SelectedPage = null;
if (_isInitDone)
{
return;
}
ShowBusy.Visibility = Visibility.Visible;
new Thread(() =>
{
Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart) (() =>
{
foreach (Space space in _confluenceUpload.Spaces)
{
TreeViewItem spaceTreeViewItem = new TreeViewItem
{
Header = space.Name,
Tag = space
};
// Get homepage
try
{
Page page = _confluenceConnector.GetSpaceHomepage(space);
TreeViewItem pageTreeViewItem = new TreeViewItem
{
Header = page.Title,
Tag = page
};
pageTreeViewItem.PreviewMouseDoubleClick += PageTreeViewItem_DoubleClick;
pageTreeViewItem.PreviewMouseLeftButtonDown += PageTreeViewItem_Click;
spaceTreeViewItem.Items.Add(pageTreeViewItem);
ConfluenceTreeView.Items.Add(spaceTreeViewItem);
}
catch (Exception ex)
{
Log.Error("Can't get homepage for space : " + space.Name + " (" + ex.Message + ")");
}
}
ShowBusy.Visibility = Visibility.Collapsed;
_isInitDone = true;
}));
})
{
Name = "Loading spaces for confluence"
}.Start();
}
}
}

View file

@ -25,92 +25,117 @@ using System.Linq;
using System.Threading;
using System.Windows;
namespace Greenshot.Plugin.Confluence.Forms {
/// <summary>
/// Interaction logic for ConfluenceUpload.xaml
/// </summary>
public partial class ConfluenceUpload : Window {
private System.Windows.Controls.Page _pickerPage;
public System.Windows.Controls.Page PickerPage {
get {
if (_pickerPage == null) {
List<Page> pages = ConfluenceUtils.GetCurrentPages();
if (pages != null && pages.Count > 0) {
_pickerPage = new ConfluencePagePicker(this, pages);
}
}
return _pickerPage;
}
}
namespace Greenshot.Plugin.Confluence.Forms
{
/// <summary>
/// Interaction logic for ConfluenceUpload.xaml
/// </summary>
public partial class ConfluenceUpload : Window
{
private System.Windows.Controls.Page _pickerPage;
private System.Windows.Controls.Page _searchPage;
public System.Windows.Controls.Page SearchPage {
get { return _searchPage ??= new ConfluenceSearch(this); }
}
public System.Windows.Controls.Page PickerPage
{
get
{
if (_pickerPage == null)
{
List<Page> pages = ConfluenceUtils.GetCurrentPages();
if (pages != null && pages.Count > 0)
{
_pickerPage = new ConfluencePagePicker(this, pages);
}
}
private System.Windows.Controls.Page _browsePage;
public System.Windows.Controls.Page BrowsePage {
get { return _browsePage ??= new ConfluenceTreePicker(this); }
}
return _pickerPage;
}
}
private Page _selectedPage;
public Page SelectedPage {
get => _selectedPage;
set {
_selectedPage = value;
Upload.IsEnabled = _selectedPage != null;
IsOpenPageSelected = false;
}
}
private System.Windows.Controls.Page _searchPage;
public bool IsOpenPageSelected {
get;
set;
}
public string Filename {
get;
set;
}
public System.Windows.Controls.Page SearchPage
{
get { return _searchPage ??= new ConfluenceSearch(this); }
}
private static DateTime _lastLoad = DateTime.Now;
private static IList<Space> _spaces;
public IList<Space> Spaces {
get {
UpdateSpaces();
while (_spaces == null) {
Thread.Sleep(300);
}
return _spaces;
}
}
private System.Windows.Controls.Page _browsePage;
public ConfluenceUpload(string filename) {
Filename = filename;
InitializeComponent();
DataContext = this;
UpdateSpaces();
if (PickerPage == null) {
PickerTab.Visibility = Visibility.Collapsed;
SearchTab.IsSelected = true;
}
}
public System.Windows.Controls.Page BrowsePage
{
get { return _browsePage ??= new ConfluenceTreePicker(this); }
}
private void UpdateSpaces() {
if (_spaces != null && DateTime.Now.AddMinutes(-60).CompareTo(_lastLoad) > 0) {
// Reset
_spaces = null;
}
// Check if load is needed
if (_spaces == null) {
(new Thread(() => {
_spaces = ConfluencePlugin.ConfluenceConnector.GetSpaceSummaries().OrderBy(s => s.Name).ToList();
_lastLoad = DateTime.Now;
}) { Name = "Loading spaces for confluence"}).Start();
}
}
private Page _selectedPage;
private void Upload_Click(object sender, RoutedEventArgs e) {
DialogResult = true;
}
}
public Page SelectedPage
{
get => _selectedPage;
set
{
_selectedPage = value;
Upload.IsEnabled = _selectedPage != null;
IsOpenPageSelected = false;
}
}
public bool IsOpenPageSelected { get; set; }
public string Filename { get; set; }
private static DateTime _lastLoad = DateTime.Now;
private static IList<Space> _spaces;
public IList<Space> Spaces
{
get
{
UpdateSpaces();
while (_spaces == null)
{
Thread.Sleep(300);
}
return _spaces;
}
}
public ConfluenceUpload(string filename)
{
Filename = filename;
InitializeComponent();
DataContext = this;
UpdateSpaces();
if (PickerPage == null)
{
PickerTab.Visibility = Visibility.Collapsed;
SearchTab.IsSelected = true;
}
}
private void UpdateSpaces()
{
if (_spaces != null && DateTime.Now.AddMinutes(-60).CompareTo(_lastLoad) > 0)
{
// Reset
_spaces = null;
}
// Check if load is needed
if (_spaces == null)
{
(new Thread(() =>
{
_spaces = ConfluencePlugin.ConfluenceConnector.GetSpaceSummaries().OrderBy(s => s.Name).ToList();
_lastLoad = DateTime.Now;
})
{
Name = "Loading spaces for confluence"
}).Start();
}
}
private void Upload_Click(object sender, RoutedEventArgs e)
{
DialogResult = true;
}
}
}

View file

@ -19,10 +19,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Greenshot.Plugin.Confluence {
public enum LangKey {
login_error,
upload_menu_item,
communication_wait
}
namespace Greenshot.Plugin.Confluence
{
public enum LangKey
{
login_error,
upload_menu_item,
communication_wait
}
}

View file

@ -1,5 +1,7 @@
namespace Greenshot.Plugin.Confluence.Support {
public interface ITranslationProvider {
namespace Greenshot.Plugin.Confluence.Support
{
public interface ITranslationProvider
{
/// <summary>
/// Translates the specified key.
/// </summary>

View file

@ -22,13 +22,13 @@ namespace Greenshot.Plugin.Confluence.Support
protected override void StartListening(object source)
{
var manager = (TranslationManager)source;
var manager = (TranslationManager) source;
manager.LanguageChanged += OnLanguageChanged;
}
protected override void StopListening(object source)
{
var manager = (TranslationManager)source;
var manager = (TranslationManager) source;
manager.LanguageChanged -= OnLanguageChanged;
}
@ -37,15 +37,15 @@ namespace Greenshot.Plugin.Confluence.Support
get
{
Type managerType = typeof(LanguageChangedEventManager);
var manager = (LanguageChangedEventManager)GetCurrentManager(managerType);
var manager = (LanguageChangedEventManager) GetCurrentManager(managerType);
if (manager == null)
{
manager = new LanguageChangedEventManager();
SetCurrentManager(managerType, manager);
}
return manager;
}
}
}
}

View file

@ -1,18 +1,23 @@
using GreenshotPlugin.Core;
namespace Greenshot.Plugin.Confluence.Support {
/// <summary>
///
/// </summary>
public class LanguageXMLTranslationProvider : ITranslationProvider {
namespace Greenshot.Plugin.Confluence.Support
{
/// <summary>
///
/// </summary>
public class LanguageXMLTranslationProvider : ITranslationProvider
{
/// <summary>
/// See <see cref="ITranslationProvider.Translate" />
/// </summary>
public object Translate(string key) {
if (Language.HasKey("confluence", key)) {
return Language.GetString("confluence", key);
}
return key;
}
/// See <see cref="ITranslationProvider.Translate" />
/// </summary>
public object Translate(string key)
{
if (Language.HasKey("confluence", key))
{
return Language.GetString("confluence", key);
}
return key;
}
}
}

View file

@ -25,7 +25,7 @@ namespace Greenshot.Plugin.Confluence.Support
public string Key
{
get { return _key; }
set { _key = value;}
set { _key = value; }
}
/// <summary>
@ -34,9 +34,9 @@ namespace Greenshot.Plugin.Confluence.Support
public override object ProvideValue(IServiceProvider serviceProvider)
{
var binding = new Binding("Value")
{
Source = new TranslationData(_key)
};
{
Source = new TranslationData(_key)
};
return binding.ProvideValue(serviceProvider);
}
}

View file

@ -2,43 +2,48 @@
using System.ComponentModel;
using System.Windows;
namespace Greenshot.Plugin.Confluence.Support {
public class TranslationData : IWeakEventListener, INotifyPropertyChanged {
namespace Greenshot.Plugin.Confluence.Support
{
public class TranslationData : IWeakEventListener, INotifyPropertyChanged
{
private readonly string _key;
/// <summary>
/// Initializes a new instance of the <see cref="TranslationData"/> class.
/// </summary>
/// <param name="key">The key.</param>
public TranslationData( string key) {
_key = key;
LanguageChangedEventManager.AddListener(TranslationManager.Instance, this);
}
/// Initializes a new instance of the <see cref="TranslationData"/> class.
/// </summary>
/// <param name="key">The key.</param>
public TranslationData(string key)
{
_key = key;
LanguageChangedEventManager.AddListener(TranslationManager.Instance, this);
}
/// <summary>
/// Releases unmanaged resources and performs other cleanup operations before the
/// <see cref="TranslationData"/> is reclaimed by garbage collection.
/// </summary>
~TranslationData() {
LanguageChangedEventManager.RemoveListener(TranslationManager.Instance, this);
}
/// <summary>
/// Releases unmanaged resources and performs other cleanup operations before the
/// <see cref="TranslationData"/> is reclaimed by garbage collection.
/// </summary>
~TranslationData()
{
LanguageChangedEventManager.RemoveListener(TranslationManager.Instance, this);
}
public object Value => TranslationManager.Instance.Translate(_key);
public object Value => TranslationManager.Instance.Translate(_key);
public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
{
if (managerType == typeof(LanguageChangedEventManager))
{
OnLanguageChanged(sender, e);
return true;
}
return false;
}
{
if (managerType == typeof(LanguageChangedEventManager))
{
OnLanguageChanged(sender, e);
return true;
}
private void OnLanguageChanged(object sender, EventArgs e)
{
PropertyChanged?.Invoke( this, new PropertyChangedEventArgs("Value"));
}
return false;
}
private void OnLanguageChanged(object sender, EventArgs e)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value"));
}
public event PropertyChangedEventHandler PropertyChanged;
}

View file

@ -1,40 +1,45 @@
using System;
namespace Greenshot.Plugin.Confluence.Support {
public class TranslationManager {
private static TranslationManager _translationManager;
namespace Greenshot.Plugin.Confluence.Support
{
public class TranslationManager
{
private static TranslationManager _translationManager;
public event EventHandler LanguageChanged;
public event EventHandler LanguageChanged;
/*public CultureInfo CurrentLanguage {
get { return Thread.CurrentThread.CurrentUICulture; }
set {
if( value != Thread.CurrentThread.CurrentUICulture) {
Thread.CurrentThread.CurrentUICulture = value;
OnLanguageChanged();
}
}
}
/*public CultureInfo CurrentLanguage {
get { return Thread.CurrentThread.CurrentUICulture; }
set {
if( value != Thread.CurrentThread.CurrentUICulture) {
Thread.CurrentThread.CurrentUICulture = value;
OnLanguageChanged();
}
}
}
public IEnumerable<CultureInfo> Languages {
get {
if( TranslationProvider != null) {
return TranslationProvider.Languages;
}
return Enumerable.Empty<CultureInfo>();
}
}*/
public IEnumerable<CultureInfo> Languages {
get {
if( TranslationProvider != null) {
return TranslationProvider.Languages;
}
return Enumerable.Empty<CultureInfo>();
}
}*/
public static TranslationManager Instance => _translationManager ??= new TranslationManager();
public static TranslationManager Instance => _translationManager ??= new TranslationManager();
public ITranslationProvider TranslationProvider { get; set; }
public ITranslationProvider TranslationProvider { get; set; }
public object Translate(string key) {
object translatedValue = TranslationProvider?.Translate(key);
if( translatedValue != null) {
return translatedValue;
}
return $"!{key}!";
}
}
public object Translate(string key)
{
object translatedValue = TranslationProvider?.Translate(key);
if (translatedValue != null)
{
return translatedValue;
}
return $"!{key}!";
}
}
}

View file

@ -25,38 +25,48 @@ using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
namespace Greenshot.Plugin.Dropbox {
internal class DropboxDestination : AbstractDestination {
private static readonly DropboxPluginConfiguration DropboxConfig = IniConfig.GetIniSection<DropboxPluginConfiguration>();
namespace Greenshot.Plugin.Dropbox
{
internal class DropboxDestination : AbstractDestination
{
private static readonly DropboxPluginConfiguration DropboxConfig = IniConfig.GetIniSection<DropboxPluginConfiguration>();
private readonly DropboxPlugin _plugin;
public DropboxDestination(DropboxPlugin plugin) {
_plugin = plugin;
}
private readonly DropboxPlugin _plugin;
public override string Designation => "Dropbox";
public DropboxDestination(DropboxPlugin plugin)
{
_plugin = plugin;
}
public override string Description => Language.GetString("dropbox", LangKey.upload_menu_item);
public override string Designation => "Dropbox";
public override Image DisplayIcon {
get {
ComponentResourceManager resources = new ComponentResourceManager(typeof(DropboxPlugin));
return (Image)resources.GetObject("Dropbox");
}
}
public override string Description => Language.GetString("dropbox", LangKey.upload_menu_item);
public override ExportInformation ExportCapture(bool manually, ISurface surface, ICaptureDetails captureDetails) {
ExportInformation exportInformation = new ExportInformation(Designation, Description);
bool uploaded = _plugin.Upload(captureDetails, surface, out var uploadUrl);
if (uploaded) {
exportInformation.Uri = uploadUrl;
exportInformation.ExportMade = true;
if (DropboxConfig.AfterUploadLinkToClipBoard) {
ClipboardHelper.SetClipboardData(uploadUrl);
}
}
ProcessExport(exportInformation, surface);
return exportInformation;
}
}
public override Image DisplayIcon
{
get
{
ComponentResourceManager resources = new ComponentResourceManager(typeof(DropboxPlugin));
return (Image) resources.GetObject("Dropbox");
}
}
public override ExportInformation ExportCapture(bool manually, ISurface surface, ICaptureDetails captureDetails)
{
ExportInformation exportInformation = new ExportInformation(Designation, Description);
bool uploaded = _plugin.Upload(captureDetails, surface, out var uploadUrl);
if (uploaded)
{
exportInformation.Uri = uploadUrl;
exportInformation.ExportMade = true;
if (DropboxConfig.AfterUploadLinkToClipBoard)
{
ClipboardHelper.SetClipboardData(uploadUrl);
}
}
ProcessExport(exportInformation, surface);
return exportInformation;
}
}
}

View file

@ -29,93 +29,101 @@ using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
namespace Greenshot.Plugin.Dropbox {
/// <summary>
/// This is the Dropbox base code
/// </summary>
namespace Greenshot.Plugin.Dropbox
{
/// <summary>
/// This is the Dropbox base code
/// </summary>
[Plugin("Dropbox", true)]
public class DropboxPlugin : IGreenshotPlugin {
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(DropboxPlugin));
private static DropboxPluginConfiguration _config;
private ComponentResourceManager _resources;
private ToolStripMenuItem _itemPlugInConfig;
public class DropboxPlugin : IGreenshotPlugin
{
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(DropboxPlugin));
private static DropboxPluginConfiguration _config;
private ComponentResourceManager _resources;
private ToolStripMenuItem _itemPlugInConfig;
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!disposing) return;
if (_itemPlugInConfig == null) return;
_itemPlugInConfig.Dispose();
_itemPlugInConfig = null;
}
private void Dispose(bool disposing)
{
if (!disposing) return;
if (_itemPlugInConfig == null) return;
_itemPlugInConfig.Dispose();
_itemPlugInConfig = null;
}
/// <summary>
/// Implementation of the IGreenshotPlugin.Initialize
/// </summary>
public bool Initialize() {
// Register configuration (don't need the configuration itself)
_config = IniConfig.GetIniSection<DropboxPluginConfiguration>();
_resources = new ComponentResourceManager(typeof(DropboxPlugin));
/// <summary>
/// Implementation of the IGreenshotPlugin.Initialize
/// </summary>
public bool Initialize()
{
// Register configuration (don't need the configuration itself)
_config = IniConfig.GetIniSection<DropboxPluginConfiguration>();
_resources = new ComponentResourceManager(typeof(DropboxPlugin));
SimpleServiceProvider.Current.AddService<IDestination>(new DropboxDestination(this));
_itemPlugInConfig = new ToolStripMenuItem
{
Text = Language.GetString("dropbox", LangKey.Configure),
Image = (Image)_resources.GetObject("Dropbox")
};
_itemPlugInConfig.Click += ConfigMenuClick;
_itemPlugInConfig = new ToolStripMenuItem
{
Text = Language.GetString("dropbox", LangKey.Configure),
Image = (Image) _resources.GetObject("Dropbox")
};
_itemPlugInConfig.Click += ConfigMenuClick;
PluginUtils.AddToContextMenu(_itemPlugInConfig);
Language.LanguageChanged += OnLanguageChanged;
return true;
}
PluginUtils.AddToContextMenu(_itemPlugInConfig);
Language.LanguageChanged += OnLanguageChanged;
return true;
}
public void OnLanguageChanged(object sender, EventArgs e) {
if (_itemPlugInConfig != null) {
_itemPlugInConfig.Text = Language.GetString("dropbox", LangKey.Configure);
}
}
public void OnLanguageChanged(object sender, EventArgs e)
{
if (_itemPlugInConfig != null)
{
_itemPlugInConfig.Text = Language.GetString("dropbox", LangKey.Configure);
}
}
public void Shutdown() {
Log.Debug("Dropbox Plugin shutdown.");
}
public void Shutdown()
{
Log.Debug("Dropbox Plugin shutdown.");
}
/// <summary>
/// Implementation of the IPlugin.Configure
/// </summary>
public void Configure() {
_config.ShowConfigDialog();
}
/// <summary>
/// Implementation of the IPlugin.Configure
/// </summary>
public void Configure()
{
_config.ShowConfigDialog();
}
public void ConfigMenuClick(object sender, EventArgs eventArgs) {
_config.ShowConfigDialog();
}
public void ConfigMenuClick(object sender, EventArgs eventArgs)
{
_config.ShowConfigDialog();
}
/// <summary>
/// This will be called when the menu item in the Editor is clicked
/// </summary>
public bool Upload(ICaptureDetails captureDetails, ISurface surfaceToUpload, out string uploadUrl) {
uploadUrl = null;
SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(_config.UploadFormat, _config.UploadJpegQuality, false);
try
/// <summary>
/// This will be called when the menu item in the Editor is clicked
/// </summary>
public bool Upload(ICaptureDetails captureDetails, ISurface surfaceToUpload, out string uploadUrl)
{
uploadUrl = null;
SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(_config.UploadFormat, _config.UploadJpegQuality, false);
try
{
bool result = false;
new PleaseWaitForm().ShowAndWait("Dropbox", Language.GetString("dropbox", LangKey.communication_wait),
delegate
{
result = DropboxUtils.UploadToDropbox(surfaceToUpload, outputSettings, captureDetails);
}
);
new PleaseWaitForm().ShowAndWait("Dropbox", Language.GetString("dropbox", LangKey.communication_wait),
delegate { result = DropboxUtils.UploadToDropbox(surfaceToUpload, outputSettings, captureDetails); }
);
return result;
} catch (Exception e) {
Log.Error(e);
MessageBox.Show(Language.GetString("dropbox", LangKey.upload_failure) + " " + e.Message);
return false;
}
}
}
}
catch (Exception e)
{
Log.Error(e);
MessageBox.Show(Language.GetString("dropbox", LangKey.upload_failure) + " " + e.Message);
return false;
}
}
}
}

View file

@ -25,25 +25,27 @@ using Greenshot.Plugin.Dropbox.Forms;
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.Dropbox {
/// <summary>
/// Description of ImgurConfiguration.
/// </summary>
[IniSection("Dropbox", Description = "Greenshot Dropbox Plugin configuration")]
public class DropboxPluginConfiguration : IniSection {
[IniProperty("UploadFormat", Description="What file type to use for uploading", DefaultValue="png")]
public OutputFormat UploadFormat { get; set; }
namespace Greenshot.Plugin.Dropbox
{
/// <summary>
/// Description of ImgurConfiguration.
/// </summary>
[IniSection("Dropbox", Description = "Greenshot Dropbox Plugin configuration")]
public class DropboxPluginConfiguration : IniSection
{
[IniProperty("UploadFormat", Description = "What file type to use for uploading", DefaultValue = "png")]
public OutputFormat UploadFormat { get; set; }
[IniProperty("UploadJpegQuality", Description="JPEG file save quality in %.", DefaultValue="80")]
public int UploadJpegQuality { get; set; }
[IniProperty("UploadJpegQuality", Description = "JPEG file save quality in %.", DefaultValue = "80")]
public int UploadJpegQuality { get; set; }
[IniProperty("AfterUploadLinkToClipBoard", Description = "After upload send Dropbox link to clipboard.", DefaultValue = "true")]
public bool AfterUploadLinkToClipBoard { get; set; }
[IniProperty("AfterUploadLinkToClipBoard", Description = "After upload send Dropbox link to clipboard.", DefaultValue = "true")]
public bool AfterUploadLinkToClipBoard { get; set; }
[IniProperty("RefreshToken", Description = "Dropbox refresh Token", Encrypted = true, ExcludeIfNull = true)]
public string RefreshToken { get; set; }
/// <summary>
/// <summary>
/// AccessToken, not stored
/// </summary>
public string AccessToken { get; set; }
@ -53,16 +55,19 @@ namespace Greenshot.Plugin.Dropbox {
/// </summary>
public DateTimeOffset AccessTokenExpires { get; set; }
/// <summary>
/// A form for token
/// </summary>
/// <returns>bool true if OK was pressed, false if cancel</returns>
public bool ShowConfigDialog() {
DialogResult result = new SettingsForm().ShowDialog();
if (result == DialogResult.OK) {
return true;
}
return false;
}
}
/// <summary>
/// A form for token
/// </summary>
/// <returns>bool true if OK was pressed, false if cancel</returns>
public bool ShowConfigDialog()
{
DialogResult result = new SettingsForm().ShowDialog();
if (result == DialogResult.OK)
{
return true;
}
return false;
}
}
}

View file

@ -29,19 +29,22 @@ using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
using Newtonsoft.Json;
namespace Greenshot.Plugin.Dropbox {
/// <summary>
/// Description of DropboxUtils.
/// </summary>
public class DropboxUtils {
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(DropboxUtils));
private static readonly DropboxPluginConfiguration DropboxConfig = IniConfig.GetIniSection<DropboxPluginConfiguration>();
namespace Greenshot.Plugin.Dropbox
{
/// <summary>
/// Description of DropboxUtils.
/// </summary>
public class DropboxUtils
{
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(DropboxUtils));
private static readonly DropboxPluginConfiguration DropboxConfig = IniConfig.GetIniSection<DropboxPluginConfiguration>();
private DropboxUtils() {
}
private DropboxUtils()
{
}
public static bool UploadToDropbox(ISurface surfaceToUpload, SurfaceOutputSettings outputSettings, ICaptureDetails captureDetails)
{
public static bool UploadToDropbox(ISurface surfaceToUpload, SurfaceOutputSettings outputSettings, ICaptureDetails captureDetails)
{
var oauth2Settings = new OAuth2Settings
{
AuthUrlPattern = "https://api.dropbox.com/oauth2/authorize?response_type=token&client_id={ClientId}&state={State}&redirect_uri={RedirectUrl}",
@ -51,24 +54,32 @@ namespace Greenshot.Plugin.Dropbox {
ClientId = DropBoxCredentials.CONSUMER_KEY,
ClientSecret = DropBoxCredentials.CONSUMER_SECRET,
AuthorizeMode = OAuth2AuthorizeMode.JsonReceiver,
RefreshToken = DropboxConfig.RefreshToken,
RefreshToken = DropboxConfig.RefreshToken,
AccessToken = DropboxConfig.AccessToken,
AccessTokenExpires = DropboxConfig.AccessTokenExpires
};
};
try
{
{
string filename = Path.GetFileName(FilenameHelper.GetFilename(DropboxConfig.UploadFormat, captureDetails));
SurfaceContainer image = new SurfaceContainer(surfaceToUpload, outputSettings, filename);
IDictionary<string, object> arguments = new Dictionary<string, object>
{
{ "autorename", true },
{ "mute", true },
{ "path", "/" + filename.Replace(Path.DirectorySeparatorChar, '\\')}
{
"autorename", true
},
{
"mute", true
},
{
"path", "/" + filename.Replace(Path.DirectorySeparatorChar, '\\')
}
};
IDictionary<string, object> headers = new Dictionary<string, object>
{
{ "Dropbox-API-Arg", JsonConvert.SerializeObject(arguments)}
{
"Dropbox-API-Arg", JsonConvert.SerializeObject(arguments)
}
};
var webRequest = OAuth2Helper.CreateOAuth2WebRequest(HTTPMethod.POST, "https://content.dropboxapi.com/2/files/upload", oauth2Settings);
@ -78,16 +89,19 @@ namespace Greenshot.Plugin.Dropbox {
var response = JsonConvert.DeserializeObject<IDictionary<string, string>>(responseString);
return response.ContainsKey("id");
}
catch (Exception ex) {
Log.Error("Upload error: ", ex);
throw;
} finally {
catch (Exception ex)
{
Log.Error("Upload error: ", ex);
throw;
}
finally
{
DropboxConfig.RefreshToken = oauth2Settings.RefreshToken;
DropboxConfig.AccessToken = oauth2Settings.AccessToken;
DropboxConfig.AccessTokenExpires = oauth2Settings.AccessTokenExpires;
DropboxConfig.IsDirty = true;
IniConfig.Save();
}
}
}
}
}
}

View file

@ -21,7 +21,9 @@
using GreenshotPlugin.Controls;
namespace Greenshot.Plugin.Dropbox.Forms {
public class DropboxForm : GreenshotForm {
}
namespace Greenshot.Plugin.Dropbox.Forms
{
public class DropboxForm : GreenshotForm
{
}
}

View file

@ -19,19 +19,21 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Greenshot.Plugin.Dropbox.Forms {
/// <summary>
/// Description of PasswordRequestForm.
/// </summary>
public partial class SettingsForm : DropboxForm {
public SettingsForm() {
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
AcceptButton = buttonOK;
CancelButton = buttonCancel;
}
}
namespace Greenshot.Plugin.Dropbox.Forms
{
/// <summary>
/// Description of PasswordRequestForm.
/// </summary>
public partial class SettingsForm : DropboxForm
{
public SettingsForm()
{
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
AcceptButton = buttonOK;
CancelButton = buttonCancel;
}
}
}

View file

@ -18,11 +18,14 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Greenshot.Plugin.Dropbox {
public enum LangKey {
upload_menu_item,
upload_failure,
communication_wait,
Configure
namespace Greenshot.Plugin.Dropbox
{
public enum LangKey
{
upload_menu_item,
upload_failure,
communication_wait,
Configure
}
}

View file

@ -25,122 +25,140 @@ using System.IO;
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.ExternalCommand {
/// <summary>
/// Description of FlickrConfiguration.
/// </summary>
[IniSection("ExternalCommand", Description="Greenshot ExternalCommand Plugin configuration")]
public class ExternalCommandConfiguration : IniSection {
[IniProperty("Commands", Description="The commands that are available.")]
public List<string> Commands { get; set; }
namespace Greenshot.Plugin.ExternalCommand
{
/// <summary>
/// Description of FlickrConfiguration.
/// </summary>
[IniSection("ExternalCommand", Description = "Greenshot ExternalCommand Plugin configuration")]
public class ExternalCommandConfiguration : IniSection
{
[IniProperty("Commands", Description = "The commands that are available.")]
public List<string> Commands { get; set; }
[IniProperty("RedirectStandardError", Description = "Redirect the standard error of all external commands, used to output as warning to the greenshot.log.", DefaultValue = "true")]
public bool RedirectStandardError { get; set; }
[IniProperty("RedirectStandardError", Description = "Redirect the standard error of all external commands, used to output as warning to the greenshot.log.",
DefaultValue = "true")]
public bool RedirectStandardError { get; set; }
[IniProperty("RedirectStandardOutput", Description = "Redirect the standard output of all external commands, used for different other functions (more below).", DefaultValue = "true")]
public bool RedirectStandardOutput { get; set; }
[IniProperty("RedirectStandardOutput", Description = "Redirect the standard output of all external commands, used for different other functions (more below).",
DefaultValue = "true")]
public bool RedirectStandardOutput { get; set; }
[IniProperty("ShowStandardOutputInLog", Description = "Depends on 'RedirectStandardOutput': Show standard output of all external commands to the Greenshot log, this can be usefull for debugging.", DefaultValue = "false")]
public bool ShowStandardOutputInLog { get; set; }
[IniProperty("ShowStandardOutputInLog",
Description = "Depends on 'RedirectStandardOutput': Show standard output of all external commands to the Greenshot log, this can be usefull for debugging.",
DefaultValue = "false")]
public bool ShowStandardOutputInLog { get; set; }
[IniProperty("ParseForUri", Description = "Depends on 'RedirectStandardOutput': Parse the output and take the first found URI, if a URI is found than clicking on the notify bubble goes there.", DefaultValue = "true")]
public bool ParseOutputForUri { get; set; }
[IniProperty("ParseForUri",
Description = "Depends on 'RedirectStandardOutput': Parse the output and take the first found URI, if a URI is found than clicking on the notify bubble goes there.",
DefaultValue = "true")]
public bool ParseOutputForUri { get; set; }
[IniProperty("OutputToClipboard", Description = "Depends on 'RedirectStandardOutput': Place the standard output on the clipboard.", DefaultValue = "false")]
public bool OutputToClipboard { get; set; }
[IniProperty("OutputToClipboard", Description = "Depends on 'RedirectStandardOutput': Place the standard output on the clipboard.", DefaultValue = "false")]
public bool OutputToClipboard { get; set; }
[IniProperty("UriToClipboard", Description = "Depends on 'RedirectStandardOutput' & 'ParseForUri': If an URI is found in the standard input, place it on the clipboard. (This overwrites the output from OutputToClipboard setting.)", DefaultValue = "true")]
public bool UriToClipboard { get; set; }
[IniProperty("UriToClipboard",
Description =
"Depends on 'RedirectStandardOutput' & 'ParseForUri': If an URI is found in the standard input, place it on the clipboard. (This overwrites the output from OutputToClipboard setting.)",
DefaultValue = "true")]
public bool UriToClipboard { get; set; }
[IniProperty("Commandline", Description="The commandline for the output command.")]
public Dictionary<string, string> Commandline { get; set; }
[IniProperty("Commandline", Description = "The commandline for the output command.")]
public Dictionary<string, string> Commandline { get; set; }
[IniProperty("Argument", Description="The arguments for the output command.")]
public Dictionary<string, string> Argument { get; set; }
[IniProperty("Argument", Description = "The arguments for the output command.")]
public Dictionary<string, string> Argument { get; set; }
[IniProperty("RunInbackground", Description = "Should the command be started in the background.")]
public Dictionary<string, bool> RunInbackground { get; set; }
[IniProperty("RunInbackground", Description = "Should the command be started in the background.")]
public Dictionary<string, bool> RunInbackground { get; set; }
[IniProperty("DeletedBuildInCommands", Description = "If a build in command was deleted manually, it should not be recreated.")]
public List<string> DeletedBuildInCommands { get; set; }
[IniProperty("DeletedBuildInCommands", Description = "If a build in command was deleted manually, it should not be recreated.")]
public List<string> DeletedBuildInCommands { get; set; }
private const string MsPaint = "MS Paint";
private static readonly string PaintPath;
private static readonly bool HasPaint;
private const string MsPaint = "MS Paint";
private static readonly string PaintPath;
private static readonly bool HasPaint;
private const string PaintDotNet = "Paint.NET";
private static readonly string PaintDotNetPath;
private static readonly bool HasPaintDotNet;
static ExternalCommandConfiguration() {
try {
PaintPath = PluginUtils.GetExePath("pbrush.exe");
HasPaint = !string.IsNullOrEmpty(PaintPath) && File.Exists(PaintPath);
} catch {
// Ignore
}
try
{
PaintDotNetPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), @"Paint.NET\PaintDotNet.exe");
HasPaintDotNet = !string.IsNullOrEmpty(PaintDotNetPath) && File.Exists(PaintDotNetPath);
}
catch
{
// Ignore
}
}
private const string PaintDotNet = "Paint.NET";
private static readonly string PaintDotNetPath;
private static readonly bool HasPaintDotNet;
/// <summary>
/// Delete the configuration for the specified command
/// </summary>
/// <param name="command">string with command</param>
public void Delete(string command)
{
if (string.IsNullOrEmpty(command))
{
return;
}
Commands.Remove(command);
Commandline.Remove(command);
Argument.Remove(command);
RunInbackground.Remove(command);
if (MsPaint.Equals(command) || PaintDotNet.Equals(command))
{
if (!DeletedBuildInCommands.Contains(command))
{
DeletedBuildInCommands.Add(command);
}
}
}
static ExternalCommandConfiguration()
{
try
{
PaintPath = PluginUtils.GetExePath("pbrush.exe");
HasPaint = !string.IsNullOrEmpty(PaintPath) && File.Exists(PaintPath);
}
catch
{
// Ignore
}
public override void AfterLoad()
{
base.AfterLoad();
try
{
PaintDotNetPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), @"Paint.NET\PaintDotNet.exe");
HasPaintDotNet = !string.IsNullOrEmpty(PaintDotNetPath) && File.Exists(PaintDotNetPath);
}
catch
{
// Ignore
}
}
// Check if we need to add MsPaint
if (HasPaint && !Commands.Contains(MsPaint) && !DeletedBuildInCommands.Contains(MsPaint))
{
Commands.Add(MsPaint);
Commandline.Add(MsPaint, PaintPath);
Argument.Add(MsPaint, "\"{0}\"");
RunInbackground.Add(MsPaint, true);
}
/// <summary>
/// Delete the configuration for the specified command
/// </summary>
/// <param name="command">string with command</param>
public void Delete(string command)
{
if (string.IsNullOrEmpty(command))
{
return;
}
// Check if we need to add Paint.NET
if (HasPaintDotNet && !Commands.Contains(PaintDotNet) && !DeletedBuildInCommands.Contains(PaintDotNet))
{
Commands.Add(PaintDotNet);
Commandline.Add(PaintDotNet, PaintDotNetPath);
Argument.Add(PaintDotNet, "\"{0}\"");
RunInbackground.Add(PaintDotNet, true);
}
}
Commands.Remove(command);
Commandline.Remove(command);
Argument.Remove(command);
RunInbackground.Remove(command);
if (MsPaint.Equals(command) || PaintDotNet.Equals(command))
{
if (!DeletedBuildInCommands.Contains(command))
{
DeletedBuildInCommands.Add(command);
}
}
}
/// <summary>
/// Supply values we can't put as defaults
/// </summary>
/// <param name="property">The property to return a default for</param>
/// <returns>object with the default value for the supplied property</returns>
public override object GetDefault(string property) =>
public override void AfterLoad()
{
base.AfterLoad();
// Check if we need to add MsPaint
if (HasPaint && !Commands.Contains(MsPaint) && !DeletedBuildInCommands.Contains(MsPaint))
{
Commands.Add(MsPaint);
Commandline.Add(MsPaint, PaintPath);
Argument.Add(MsPaint, "\"{0}\"");
RunInbackground.Add(MsPaint, true);
}
// Check if we need to add Paint.NET
if (HasPaintDotNet && !Commands.Contains(PaintDotNet) && !DeletedBuildInCommands.Contains(PaintDotNet))
{
Commands.Add(PaintDotNet);
Commandline.Add(PaintDotNet, PaintDotNetPath);
Argument.Add(PaintDotNet, "\"{0}\"");
RunInbackground.Add(PaintDotNet, true);
}
}
/// <summary>
/// Supply values we can't put as defaults
/// </summary>
/// <param name="property">The property to return a default for</param>
/// <returns>object with the default value for the supplied property</returns>
public override object GetDefault(string property) =>
property switch
{
nameof(DeletedBuildInCommands) => (object) new List<string>(),

View file

@ -31,146 +31,184 @@ using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
namespace Greenshot.Plugin.ExternalCommand {
/// <summary>
/// Description of OCRDestination.
/// </summary>
public class ExternalCommandDestination : AbstractDestination {
private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(ExternalCommandDestination));
private static readonly Regex URI_REGEXP = new Regex(@"((([A-Za-z]{3,9}:(?:\/\/)?)(?:[\-;:&=\+\$,\w]+@)?[A-Za-z0-9\.\-]+|(?:www\.|[\-;:&=\+\$,\w]+@)[A-Za-z0-9\.\-]+)((?:\/[\+~%\/\.\w\-_]*)?\??(?:[\-\+=&;%@\.\w_]*)#?(?:[\.\!\/\\\w]*))?)");
private static readonly ExternalCommandConfiguration config = IniConfig.GetIniSection<ExternalCommandConfiguration>();
private readonly string _presetCommand;
namespace Greenshot.Plugin.ExternalCommand
{
/// <summary>
/// Description of OCRDestination.
/// </summary>
public class ExternalCommandDestination : AbstractDestination
{
private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(ExternalCommandDestination));
public ExternalCommandDestination(string commando) {
_presetCommand = commando;
}
private static readonly Regex URI_REGEXP =
new Regex(
@"((([A-Za-z]{3,9}:(?:\/\/)?)(?:[\-;:&=\+\$,\w]+@)?[A-Za-z0-9\.\-]+|(?:www\.|[\-;:&=\+\$,\w]+@)[A-Za-z0-9\.\-]+)((?:\/[\+~%\/\.\w\-_]*)?\??(?:[\-\+=&;%@\.\w_]*)#?(?:[\.\!\/\\\w]*))?)");
public override string Designation => "External " + _presetCommand.Replace(',','_');
private static readonly ExternalCommandConfiguration config = IniConfig.GetIniSection<ExternalCommandConfiguration>();
private readonly string _presetCommand;
public override string Description => _presetCommand;
public ExternalCommandDestination(string commando)
{
_presetCommand = commando;
}
public override IEnumerable<IDestination> DynamicDestinations() {
yield break;
}
public override string Designation => "External " + _presetCommand.Replace(',', '_');
public override Image DisplayIcon => IconCache.IconForCommand(_presetCommand);
public override string Description => _presetCommand;
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) {
ExportInformation exportInformation = new ExportInformation(Designation, Description);
SurfaceOutputSettings outputSettings = new SurfaceOutputSettings();
outputSettings.PreventGreenshotFormat();
public override IEnumerable<IDestination> DynamicDestinations()
{
yield break;
}
if (_presetCommand != null) {
if (!config.RunInbackground.ContainsKey(_presetCommand)) {
config.RunInbackground.Add(_presetCommand, true);
}
bool runInBackground = config.RunInbackground[_presetCommand];
string fullPath = captureDetails.Filename ?? ImageOutput.SaveNamedTmpFile(surface, captureDetails, outputSettings);
public override Image DisplayIcon => IconCache.IconForCommand(_presetCommand);
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails)
{
ExportInformation exportInformation = new ExportInformation(Designation, Description);
SurfaceOutputSettings outputSettings = new SurfaceOutputSettings();
outputSettings.PreventGreenshotFormat();
if (_presetCommand != null)
{
if (!config.RunInbackground.ContainsKey(_presetCommand))
{
config.RunInbackground.Add(_presetCommand, true);
}
bool runInBackground = config.RunInbackground[_presetCommand];
string fullPath = captureDetails.Filename ?? ImageOutput.SaveNamedTmpFile(surface, captureDetails, outputSettings);
string output;
string error;
if (runInBackground) {
Thread commandThread = new Thread(delegate()
{
CallExternalCommand(exportInformation, fullPath, out output, out error);
ProcessExport(exportInformation, surface);
})
{
Name = "Running " + _presetCommand,
IsBackground = true
};
commandThread.SetApartmentState(ApartmentState.STA);
commandThread.Start();
exportInformation.ExportMade = true;
} else {
CallExternalCommand(exportInformation, fullPath, out output, out error);
ProcessExport(exportInformation, surface);
}
}
return exportInformation;
}
string error;
if (runInBackground)
{
Thread commandThread = new Thread(delegate()
{
CallExternalCommand(exportInformation, fullPath, out output, out error);
ProcessExport(exportInformation, surface);
})
{
Name = "Running " + _presetCommand,
IsBackground = true
};
commandThread.SetApartmentState(ApartmentState.STA);
commandThread.Start();
exportInformation.ExportMade = true;
}
else
{
CallExternalCommand(exportInformation, fullPath, out output, out error);
ProcessExport(exportInformation, surface);
}
}
/// <summary>
/// Wrapper method for the background and normal call, this does all the logic:
/// Call the external command, parse for URI, place to clipboard and set the export information
/// </summary>
/// <param name="exportInformation"></param>
/// <param name="fullPath"></param>
/// <param name="output"></param>
/// <param name="error"></param>
private void CallExternalCommand(ExportInformation exportInformation, string fullPath, out string output, out string error) {
output = null;
error = null;
try {
if (CallExternalCommand(_presetCommand, fullPath, out output, out error) == 0) {
exportInformation.ExportMade = true;
if (!string.IsNullOrEmpty(output)) {
MatchCollection uriMatches = URI_REGEXP.Matches(output);
// Place output on the clipboard before the URI, so if one is found this overwrites
if (config.OutputToClipboard) {
ClipboardHelper.SetClipboardData(output);
}
if (uriMatches.Count > 0) {
exportInformation.Uri = uriMatches[0].Groups[1].Value;
LOG.InfoFormat("Got URI : {0} ", exportInformation.Uri);
if (config.UriToClipboard) {
ClipboardHelper.SetClipboardData(exportInformation.Uri);
}
}
}
} else {
LOG.WarnFormat("Error calling external command: {0} ", output);
exportInformation.ExportMade = false;
exportInformation.ErrorMessage = error;
}
} catch (Exception ex) {
exportInformation.ExportMade = false;
exportInformation.ErrorMessage = ex.Message;
LOG.WarnFormat("Error calling external command: {0} ", exportInformation.ErrorMessage);
}
}
return exportInformation;
}
/// <summary>
/// Wrapper to retry with a runas
/// </summary>
/// <param name="commando"></param>
/// <param name="fullPath"></param>
/// <param name="output"></param>
/// <param name="error"></param>
/// <returns></returns>
private int CallExternalCommand(string commando, string fullPath, out string output, out string error) {
try {
return CallExternalCommand(commando, fullPath, null, out output, out error);
} catch (Win32Exception w32Ex) {
try {
return CallExternalCommand(commando, fullPath, "runas", out output, out error);
} catch {
w32Ex.Data.Add("commandline", config.Commandline[_presetCommand]);
w32Ex.Data.Add("arguments", config.Argument[_presetCommand]);
throw;
}
} catch (Exception ex) {
ex.Data.Add("commandline", config.Commandline[_presetCommand]);
ex.Data.Add("arguments", config.Argument[_presetCommand]);
throw;
}
}
/// <summary>
/// Wrapper method for the background and normal call, this does all the logic:
/// Call the external command, parse for URI, place to clipboard and set the export information
/// </summary>
/// <param name="exportInformation"></param>
/// <param name="fullPath"></param>
/// <param name="output"></param>
/// <param name="error"></param>
private void CallExternalCommand(ExportInformation exportInformation, string fullPath, out string output, out string error)
{
output = null;
error = null;
try
{
if (CallExternalCommand(_presetCommand, fullPath, out output, out error) == 0)
{
exportInformation.ExportMade = true;
if (!string.IsNullOrEmpty(output))
{
MatchCollection uriMatches = URI_REGEXP.Matches(output);
// Place output on the clipboard before the URI, so if one is found this overwrites
if (config.OutputToClipboard)
{
ClipboardHelper.SetClipboardData(output);
}
/// <summary>
/// The actual executing code for the external command
/// </summary>
/// <param name="commando"></param>
/// <param name="fullPath"></param>
/// <param name="verb"></param>
/// <param name="output"></param>
/// <param name="error"></param>
/// <returns></returns>
private int CallExternalCommand(string commando, string fullPath, string verb, out string output, out string error) {
string commandline = config.Commandline[commando];
string arguments = config.Argument[commando];
output = null;
error = null;
if (!string.IsNullOrEmpty(commandline))
if (uriMatches.Count > 0)
{
exportInformation.Uri = uriMatches[0].Groups[1].Value;
LOG.InfoFormat("Got URI : {0} ", exportInformation.Uri);
if (config.UriToClipboard)
{
ClipboardHelper.SetClipboardData(exportInformation.Uri);
}
}
}
}
else
{
LOG.WarnFormat("Error calling external command: {0} ", output);
exportInformation.ExportMade = false;
exportInformation.ErrorMessage = error;
}
}
catch (Exception ex)
{
exportInformation.ExportMade = false;
exportInformation.ErrorMessage = ex.Message;
LOG.WarnFormat("Error calling external command: {0} ", exportInformation.ErrorMessage);
}
}
/// <summary>
/// Wrapper to retry with a runas
/// </summary>
/// <param name="commando"></param>
/// <param name="fullPath"></param>
/// <param name="output"></param>
/// <param name="error"></param>
/// <returns></returns>
private int CallExternalCommand(string commando, string fullPath, out string output, out string error)
{
try
{
return CallExternalCommand(commando, fullPath, null, out output, out error);
}
catch (Win32Exception w32Ex)
{
try
{
return CallExternalCommand(commando, fullPath, "runas", out output, out error);
}
catch
{
w32Ex.Data.Add("commandline", config.Commandline[_presetCommand]);
w32Ex.Data.Add("arguments", config.Argument[_presetCommand]);
throw;
}
}
catch (Exception ex)
{
ex.Data.Add("commandline", config.Commandline[_presetCommand]);
ex.Data.Add("arguments", config.Argument[_presetCommand]);
throw;
}
}
/// <summary>
/// The actual executing code for the external command
/// </summary>
/// <param name="commando"></param>
/// <param name="fullPath"></param>
/// <param name="verb"></param>
/// <param name="output"></param>
/// <param name="error"></param>
/// <returns></returns>
private int CallExternalCommand(string commando, string fullPath, string verb, out string output, out string error)
{
string commandline = config.Commandline[commando];
string arguments = config.Argument[commando];
output = null;
error = null;
if (!string.IsNullOrEmpty(commandline))
{
using Process process = new Process();
// Fix variables
@ -183,39 +221,52 @@ namespace Greenshot.Plugin.ExternalCommand {
process.StartInfo.FileName = FilenameHelper.FillCmdVariables(commandline, true);
process.StartInfo.Arguments = FormatArguments(arguments, fullPath);
process.StartInfo.UseShellExecute = false;
if (config.RedirectStandardOutput) {
if (config.RedirectStandardOutput)
{
process.StartInfo.RedirectStandardOutput = true;
}
if (config.RedirectStandardError) {
if (config.RedirectStandardError)
{
process.StartInfo.RedirectStandardError = true;
}
if (verb != null) {
if (verb != null)
{
process.StartInfo.Verb = verb;
}
LOG.InfoFormat("Starting : {0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
process.Start();
process.WaitForExit();
if (config.RedirectStandardOutput) {
if (config.RedirectStandardOutput)
{
output = process.StandardOutput.ReadToEnd();
if (config.ShowStandardOutputInLog && output.Trim().Length > 0) {
if (config.ShowStandardOutputInLog && output.Trim().Length > 0)
{
LOG.InfoFormat("Output:\n{0}", output);
}
}
if (config.RedirectStandardError) {
if (config.RedirectStandardError)
{
error = process.StandardError.ReadToEnd();
if (error.Trim().Length > 0) {
if (error.Trim().Length > 0)
{
LOG.WarnFormat("Error:\n{0}", error);
}
}
LOG.InfoFormat("Finished : {0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
return process.ExitCode;
}
return -1;
}
public static string FormatArguments(string arguments, string fullpath)
{
return string.Format(arguments, fullpath);
}
}
return -1;
}
public static string FormatArguments(string arguments, string fullpath)
{
return string.Format(arguments, fullpath);
}
}
}

View file

@ -21,10 +21,12 @@
using GreenshotPlugin.Controls;
namespace Greenshot.Plugin.ExternalCommand {
/// <summary>
/// This class is needed for design-time resolving of the language files
/// </summary>
public class ExternalCommandForm : GreenshotForm {
}
namespace Greenshot.Plugin.ExternalCommand
{
/// <summary>
/// This class is needed for design-time resolving of the language files
/// </summary>
public class ExternalCommandForm : GreenshotForm
{
}
}

View file

@ -55,6 +55,7 @@ namespace Greenshot.Plugin.ExternalCommand
_itemPlugInRoot.Dispose();
_itemPlugInRoot = null;
}
private IEnumerable<IDestination> Destinations()
{
foreach (string command in ExternalCommandConfig.Commands)
@ -77,17 +78,20 @@ namespace Greenshot.Plugin.ExternalCommand
// Fix it
ExternalCommandConfig.RunInbackground.Add(command, true);
}
if (!ExternalCommandConfig.Argument.ContainsKey(command))
{
Log.WarnFormat("Found missing argument for {0}", command);
// Fix it
ExternalCommandConfig.Argument.Add(command, "{0}");
}
if (!ExternalCommandConfig.Commandline.ContainsKey(command))
{
Log.WarnFormat("Found missing commandline for {0}", command);
return false;
}
string commandline = FilenameHelper.FillVariables(ExternalCommandConfig.Commandline[command], true);
commandline = FilenameHelper.FillCmdVariables(commandline, true);
@ -96,8 +100,10 @@ namespace Greenshot.Plugin.ExternalCommand
Log.WarnFormat("Found 'invalid' commandline {0} for command {1}", ExternalCommandConfig.Commandline[command], command);
return false;
}
return true;
}
/// <summary>
/// Implementation of the IGreenshotPlugin.Initialize
/// </summary>
@ -114,11 +120,13 @@ namespace Greenshot.Plugin.ExternalCommand
commandsToDelete.Add(command);
}
}
// cleanup
foreach (string command in commandsToDelete)
{
ExternalCommandConfig.Delete(command);
}
SimpleServiceProvider.Current.AddService(Destinations());
_itemPlugInRoot = new ToolStripMenuItem();

View file

@ -25,23 +25,32 @@ using System.IO;
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.ExternalCommand {
public static class IconCache {
private static readonly ExternalCommandConfiguration config = IniConfig.GetIniSection<ExternalCommandConfiguration>();
private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(IconCache));
namespace Greenshot.Plugin.ExternalCommand
{
public static class IconCache
{
private static readonly ExternalCommandConfiguration config = IniConfig.GetIniSection<ExternalCommandConfiguration>();
private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(IconCache));
public static Image IconForCommand(string commandName) {
Image icon = null;
if (commandName != null) {
if (config.Commandline.ContainsKey(commandName) && File.Exists(config.Commandline[commandName])) {
try {
icon = PluginUtils.GetCachedExeIcon(config.Commandline[commandName], 0);
} catch (Exception ex) {
LOG.Warn("Problem loading icon for " + config.Commandline[commandName], ex);
}
}
}
return icon;
}
}
public static Image IconForCommand(string commandName)
{
Image icon = null;
if (commandName != null)
{
if (config.Commandline.ContainsKey(commandName) && File.Exists(config.Commandline[commandName]))
{
try
{
icon = PluginUtils.GetCachedExeIcon(config.Commandline[commandName], 0);
}
catch (Exception ex)
{
LOG.Warn("Problem loading icon for " + config.Commandline[commandName], ex);
}
}
}
return icon;
}
}
}

View file

@ -24,103 +24,129 @@ using System.Drawing;
using System.Windows.Forms;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.ExternalCommand {
/// <summary>
/// Description of SettingsForm.
/// </summary>
public partial class SettingsForm : ExternalCommandForm {
private static readonly ExternalCommandConfiguration ExternalCommandConfig = IniConfig.GetIniSection<ExternalCommandConfiguration>();
namespace Greenshot.Plugin.ExternalCommand
{
/// <summary>
/// Description of SettingsForm.
/// </summary>
public partial class SettingsForm : ExternalCommandForm
{
private static readonly ExternalCommandConfiguration ExternalCommandConfig = IniConfig.GetIniSection<ExternalCommandConfiguration>();
public SettingsForm() {
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
AcceptButton = buttonOk;
CancelButton = buttonCancel;
UpdateView();
}
public SettingsForm()
{
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
AcceptButton = buttonOk;
CancelButton = buttonCancel;
UpdateView();
}
private void ButtonOkClick(object sender, EventArgs e) {
IniConfig.Save();
}
private void ButtonOkClick(object sender, EventArgs e)
{
IniConfig.Save();
}
private void ButtonAddClick(object sender, EventArgs e) {
var form = new SettingsFormDetail(null);
form.ShowDialog();
private void ButtonAddClick(object sender, EventArgs e)
{
var form = new SettingsFormDetail(null);
form.ShowDialog();
UpdateView();
}
UpdateView();
}
private void ButtonDeleteClick(object sender, EventArgs e) {
foreach(ListViewItem item in listView1.SelectedItems) {
string commando = item.Tag as string;
private void ButtonDeleteClick(object sender, EventArgs e)
{
foreach (ListViewItem item in listView1.SelectedItems)
{
string commando = item.Tag as string;
ExternalCommandConfig.Delete(commando);
}
UpdateView();
}
ExternalCommandConfig.Delete(commando);
}
private void UpdateView() {
listView1.Items.Clear();
if(ExternalCommandConfig.Commands != null) {
listView1.ListViewItemSorter = new ListviewComparer();
ImageList imageList = new ImageList();
listView1.SmallImageList = imageList;
int imageNr = 0;
foreach(string commando in ExternalCommandConfig.Commands) {
ListViewItem item;
Image iconForExe = IconCache.IconForCommand(commando);
if(iconForExe != null) {
imageList.Images.Add(iconForExe);
item = new ListViewItem(commando, imageNr++);
} else {
item = new ListViewItem(commando);
}
item.Tag = commando;
listView1.Items.Add(item);
}
}
// Fix for bug #1484, getting an ArgumentOutOfRangeException as there is nothing selected but the edit button was still active.
button_edit.Enabled = listView1.SelectedItems.Count > 0;
}
UpdateView();
}
private void ListView1ItemSelectionChanged(object sender, EventArgs e) {
button_edit.Enabled = listView1.SelectedItems.Count > 0;
}
private void UpdateView()
{
listView1.Items.Clear();
if (ExternalCommandConfig.Commands != null)
{
listView1.ListViewItemSorter = new ListviewComparer();
ImageList imageList = new ImageList();
listView1.SmallImageList = imageList;
int imageNr = 0;
foreach (string commando in ExternalCommandConfig.Commands)
{
ListViewItem item;
Image iconForExe = IconCache.IconForCommand(commando);
if (iconForExe != null)
{
imageList.Images.Add(iconForExe);
item = new ListViewItem(commando, imageNr++);
}
else
{
item = new ListViewItem(commando);
}
private void ButtonEditClick(object sender, EventArgs e) {
ListView1DoubleClick(sender, e);
}
item.Tag = commando;
listView1.Items.Add(item);
}
}
private void ListView1DoubleClick(object sender, EventArgs e) {
// Safety check for bug #1484
bool selectionActive = listView1.SelectedItems.Count > 0;
if(!selectionActive) {
button_edit.Enabled = false;
return;
}
string commando = listView1.SelectedItems[0].Tag as string;
// Fix for bug #1484, getting an ArgumentOutOfRangeException as there is nothing selected but the edit button was still active.
button_edit.Enabled = listView1.SelectedItems.Count > 0;
}
var form = new SettingsFormDetail(commando);
form.ShowDialog();
private void ListView1ItemSelectionChanged(object sender, EventArgs e)
{
button_edit.Enabled = listView1.SelectedItems.Count > 0;
}
UpdateView();
}
}
private void ButtonEditClick(object sender, EventArgs e)
{
ListView1DoubleClick(sender, e);
}
public class ListviewComparer : System.Collections.IComparer {
public int Compare(object x, object y) {
if(!(x is ListViewItem)) {
return (0);
}
if(!(y is ListViewItem)) {
return (0);
}
private void ListView1DoubleClick(object sender, EventArgs e)
{
// Safety check for bug #1484
bool selectionActive = listView1.SelectedItems.Count > 0;
if (!selectionActive)
{
button_edit.Enabled = false;
return;
}
var l1 = (ListViewItem)x;
var l2 = (ListViewItem)y;
return string.Compare(l1.Text, l2.Text, StringComparison.Ordinal);
}
}
string commando = listView1.SelectedItems[0].Tag as string;
var form = new SettingsFormDetail(commando);
form.ShowDialog();
UpdateView();
}
}
public class ListviewComparer : System.Collections.IComparer
{
public int Compare(object x, object y)
{
if (!(x is ListViewItem))
{
return (0);
}
if (!(y is ListViewItem))
{
return (0);
}
var l1 = (ListViewItem) x;
var l2 = (ListViewItem) y;
return string.Compare(l1.Text, l2.Text, StringComparison.Ordinal);
}
}
}

View file

@ -26,141 +26,167 @@ using System.Windows.Forms;
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.ExternalCommand {
/// <summary>
/// Description of SettingsFormDetail.
/// </summary>
public partial class SettingsFormDetail : ExternalCommandForm {
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(SettingsFormDetail));
private static readonly ExternalCommandConfiguration ExternalCommandConfig = IniConfig.GetIniSection<ExternalCommandConfiguration>();
namespace Greenshot.Plugin.ExternalCommand
{
/// <summary>
/// Description of SettingsFormDetail.
/// </summary>
public partial class SettingsFormDetail : ExternalCommandForm
{
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(SettingsFormDetail));
private static readonly ExternalCommandConfiguration ExternalCommandConfig = IniConfig.GetIniSection<ExternalCommandConfiguration>();
private readonly string _commando;
private readonly int _commandIndex;
private readonly string _commando;
private readonly int _commandIndex;
public SettingsFormDetail(string commando) {
InitializeComponent();
AcceptButton = buttonOk;
CancelButton = buttonCancel;
_commando = commando;
public SettingsFormDetail(string commando)
{
InitializeComponent();
AcceptButton = buttonOk;
CancelButton = buttonCancel;
_commando = commando;
if(commando != null) {
textBox_name.Text = commando;
textBox_commandline.Text = ExternalCommandConfig.Commandline[commando];
textBox_arguments.Text = ExternalCommandConfig.Argument[commando];
_commandIndex = ExternalCommandConfig.Commands.FindIndex(s => s == commando);
} else {
textBox_arguments.Text = "\"{0}\"";
}
OkButtonState();
}
if (commando != null)
{
textBox_name.Text = commando;
textBox_commandline.Text = ExternalCommandConfig.Commandline[commando];
textBox_arguments.Text = ExternalCommandConfig.Argument[commando];
_commandIndex = ExternalCommandConfig.Commands.FindIndex(s => s == commando);
}
else
{
textBox_arguments.Text = "\"{0}\"";
}
private void ButtonOkClick(object sender, EventArgs e) {
string commandName = textBox_name.Text;
string commandLine = textBox_commandline.Text;
string arguments = textBox_arguments.Text;
if(_commando != null) {
ExternalCommandConfig.Commands[_commandIndex] = commandName;
ExternalCommandConfig.Commandline.Remove(_commando);
ExternalCommandConfig.Commandline.Add(commandName, commandLine);
ExternalCommandConfig.Argument.Remove(_commando);
ExternalCommandConfig.Argument.Add(commandName, arguments);
} else {
ExternalCommandConfig.Commands.Add(commandName);
ExternalCommandConfig.Commandline.Add(commandName, commandLine);
ExternalCommandConfig.Argument.Add(commandName, arguments);
}
}
OkButtonState();
}
private void Button3Click(object sender, EventArgs e) {
var openFileDialog = new OpenFileDialog
{
Filter = "Executables (*.exe, *.bat, *.com)|*.exe; *.bat; *.com|All files (*)|*",
FilterIndex = 1,
CheckFileExists = true,
Multiselect = false
};
string initialPath = null;
try
{
initialPath = Path.GetDirectoryName(textBox_commandline.Text);
}
catch (Exception ex)
{
Log.WarnFormat("Can't get the initial path via {0}", textBox_commandline.Text);
Log.Warn("Exception: ", ex);
}
if(initialPath != null && Directory.Exists(initialPath)) {
openFileDialog.InitialDirectory = initialPath;
} else {
initialPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
openFileDialog.InitialDirectory = initialPath;
}
Log.DebugFormat("Starting OpenFileDialog at {0}", initialPath);
if(openFileDialog.ShowDialog() == DialogResult.OK) {
textBox_commandline.Text = openFileDialog.FileName;
}
}
private void ButtonOkClick(object sender, EventArgs e)
{
string commandName = textBox_name.Text;
string commandLine = textBox_commandline.Text;
string arguments = textBox_arguments.Text;
if (_commando != null)
{
ExternalCommandConfig.Commands[_commandIndex] = commandName;
ExternalCommandConfig.Commandline.Remove(_commando);
ExternalCommandConfig.Commandline.Add(commandName, commandLine);
ExternalCommandConfig.Argument.Remove(_commando);
ExternalCommandConfig.Argument.Add(commandName, arguments);
}
else
{
ExternalCommandConfig.Commands.Add(commandName);
ExternalCommandConfig.Commandline.Add(commandName, commandLine);
ExternalCommandConfig.Argument.Add(commandName, arguments);
}
}
private void OkButtonState() {
// Assume OK
buttonOk.Enabled = true;
textBox_name.BackColor = Color.White;
textBox_commandline.BackColor = Color.White;
textBox_arguments.BackColor = Color.White;
// Is there a text in the name field
if(string.IsNullOrEmpty(textBox_name.Text)) {
buttonOk.Enabled = false;
}
// Check if commandname is unique
if(_commando == null && !string.IsNullOrEmpty(textBox_name.Text) && ExternalCommandConfig.Commands.Contains(textBox_name.Text)) {
buttonOk.Enabled = false;
textBox_name.BackColor = Color.Red;
}
// Is there a text in the commandline field
if(string.IsNullOrEmpty(textBox_commandline.Text)) {
buttonOk.Enabled = false;
}
private void Button3Click(object sender, EventArgs e)
{
var openFileDialog = new OpenFileDialog
{
Filter = "Executables (*.exe, *.bat, *.com)|*.exe; *.bat; *.com|All files (*)|*",
FilterIndex = 1,
CheckFileExists = true,
Multiselect = false
};
string initialPath = null;
try
{
initialPath = Path.GetDirectoryName(textBox_commandline.Text);
}
catch (Exception ex)
{
Log.WarnFormat("Can't get the initial path via {0}", textBox_commandline.Text);
Log.Warn("Exception: ", ex);
}
if (!string.IsNullOrEmpty(textBox_commandline.Text))
{
// Added this to be more flexible, using the Greenshot var format
string cmdPath = FilenameHelper.FillVariables(textBox_commandline.Text, true);
// And also replace the "DOS" Variables
cmdPath = FilenameHelper.FillCmdVariables(cmdPath, true);
// Is the command available?
if (!File.Exists(cmdPath))
{
buttonOk.Enabled = false;
textBox_commandline.BackColor = Color.Red;
}
}
// Are the arguments in a valid format?
try
{
string arguments = FilenameHelper.FillVariables(textBox_arguments.Text, false);
arguments = FilenameHelper.FillCmdVariables(arguments, false);
if (initialPath != null && Directory.Exists(initialPath))
{
openFileDialog.InitialDirectory = initialPath;
}
else
{
initialPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
openFileDialog.InitialDirectory = initialPath;
}
ExternalCommandDestination.FormatArguments(arguments, string.Empty);
}
catch
{
buttonOk.Enabled = false;
textBox_arguments.BackColor = Color.Red;
}
}
Log.DebugFormat("Starting OpenFileDialog at {0}", initialPath);
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
textBox_commandline.Text = openFileDialog.FileName;
}
}
private void textBox_name_TextChanged(object sender, EventArgs e) {
OkButtonState();
}
private void OkButtonState()
{
// Assume OK
buttonOk.Enabled = true;
textBox_name.BackColor = Color.White;
textBox_commandline.BackColor = Color.White;
textBox_arguments.BackColor = Color.White;
// Is there a text in the name field
if (string.IsNullOrEmpty(textBox_name.Text))
{
buttonOk.Enabled = false;
}
private void textBox_commandline_TextChanged(object sender, EventArgs e) {
OkButtonState();
}
// Check if commandname is unique
if (_commando == null && !string.IsNullOrEmpty(textBox_name.Text) && ExternalCommandConfig.Commands.Contains(textBox_name.Text))
{
buttonOk.Enabled = false;
textBox_name.BackColor = Color.Red;
}
private void textBox_arguments_TextChanged(object sender, EventArgs e)
{
OkButtonState();
}
// Is there a text in the commandline field
if (string.IsNullOrEmpty(textBox_commandline.Text))
{
buttonOk.Enabled = false;
}
}
if (!string.IsNullOrEmpty(textBox_commandline.Text))
{
// Added this to be more flexible, using the Greenshot var format
string cmdPath = FilenameHelper.FillVariables(textBox_commandline.Text, true);
// And also replace the "DOS" Variables
cmdPath = FilenameHelper.FillCmdVariables(cmdPath, true);
// Is the command available?
if (!File.Exists(cmdPath))
{
buttonOk.Enabled = false;
textBox_commandline.BackColor = Color.Red;
}
}
// Are the arguments in a valid format?
try
{
string arguments = FilenameHelper.FillVariables(textBox_arguments.Text, false);
arguments = FilenameHelper.FillCmdVariables(arguments, false);
ExternalCommandDestination.FormatArguments(arguments, string.Empty);
}
catch
{
buttonOk.Enabled = false;
textBox_arguments.BackColor = Color.Red;
}
}
private void textBox_name_TextChanged(object sender, EventArgs e)
{
OkButtonState();
}
private void textBox_commandline_TextChanged(object sender, EventArgs e)
{
OkButtonState();
}
private void textBox_arguments_TextChanged(object sender, EventArgs e)
{
OkButtonState();
}
}
}

View file

@ -24,59 +24,67 @@ using Greenshot.Plugin.Flickr.Forms;
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.Flickr {
public enum SafetyLevel {
Safe = 1,
Moderate = 2,
Restricted = 3
}
/// <summary>
/// Description of FlickrConfiguration.
/// </summary>
[IniSection("Flickr", Description = "Greenshot Flickr Plugin configuration")]
public class FlickrConfiguration : IniSection {
[IniProperty("flickrIsPublic", Description = "IsPublic.", DefaultValue = "true")]
public bool IsPublic { get; set; }
namespace Greenshot.Plugin.Flickr
{
public enum SafetyLevel
{
Safe = 1,
Moderate = 2,
Restricted = 3
}
[IniProperty("flickrIsFamily", Description = "IsFamily.", DefaultValue = "true")]
public bool IsFamily { get; set; }
/// <summary>
/// Description of FlickrConfiguration.
/// </summary>
[IniSection("Flickr", Description = "Greenshot Flickr Plugin configuration")]
public class FlickrConfiguration : IniSection
{
[IniProperty("flickrIsPublic", Description = "IsPublic.", DefaultValue = "true")]
public bool IsPublic { get; set; }
[IniProperty("flickrIsFriend", Description = "IsFriend.", DefaultValue = "true")]
public bool IsFriend { get; set; }
[IniProperty("flickrIsFamily", Description = "IsFamily.", DefaultValue = "true")]
public bool IsFamily { get; set; }
[IniProperty("SafetyLevel", Description = "Safety level", DefaultValue = "Safe")]
public SafetyLevel SafetyLevel { get; set; }
[IniProperty("flickrIsFriend", Description = "IsFriend.", DefaultValue = "true")]
public bool IsFriend { get; set; }
[IniProperty("HiddenFromSearch", Description = "Hidden from search", DefaultValue = "false")]
public bool HiddenFromSearch { get; set; }
[IniProperty("SafetyLevel", Description = "Safety level", DefaultValue = "Safe")]
public SafetyLevel SafetyLevel { get; set; }
[IniProperty("UploadFormat", Description="What file type to use for uploading", DefaultValue="png")]
public OutputFormat UploadFormat { get; set; }
[IniProperty("HiddenFromSearch", Description = "Hidden from search", DefaultValue = "false")]
public bool HiddenFromSearch { get; set; }
[IniProperty("UploadJpegQuality", Description="JPEG file save quality in %.", DefaultValue="80")]
public int UploadJpegQuality { get; set; }
[IniProperty("UploadFormat", Description = "What file type to use for uploading", DefaultValue = "png")]
public OutputFormat UploadFormat { get; set; }
[IniProperty("AfterUploadLinkToClipBoard", Description = "After upload send flickr link to clipboard.", DefaultValue = "true")]
public bool AfterUploadLinkToClipBoard { get; set; }
[IniProperty("UploadJpegQuality", Description = "JPEG file save quality in %.", DefaultValue = "80")]
public int UploadJpegQuality { get; set; }
[IniProperty("UsePageLink", Description = "Use pagelink instead of direct link on the clipboard", DefaultValue = "False")]
public bool UsePageLink { get; set; }
[IniProperty("AfterUploadLinkToClipBoard", Description = "After upload send flickr link to clipboard.", DefaultValue = "true")]
public bool AfterUploadLinkToClipBoard { get; set; }
[IniProperty("FlickrToken", Description = "The Flickr token", Encrypted = true, ExcludeIfNull = true)]
public string FlickrToken { get; set; }
[IniProperty("FlickrTokenSecret", Description = "The Flickr token secret", Encrypted = true, ExcludeIfNull = true)]
public string FlickrTokenSecret { get; set; }
[IniProperty("UsePageLink", Description = "Use pagelink instead of direct link on the clipboard", DefaultValue = "False")]
public bool UsePageLink { get; set; }
/// <summary>
/// A form for token
/// </summary>
/// <returns>bool true if OK was pressed, false if cancel</returns>
public bool ShowConfigDialog() {
DialogResult result = new SettingsForm().ShowDialog();
if (result == DialogResult.OK) {
return true;
}
return false;
}
}
[IniProperty("FlickrToken", Description = "The Flickr token", Encrypted = true, ExcludeIfNull = true)]
public string FlickrToken { get; set; }
[IniProperty("FlickrTokenSecret", Description = "The Flickr token secret", Encrypted = true, ExcludeIfNull = true)]
public string FlickrTokenSecret { get; set; }
/// <summary>
/// A form for token
/// </summary>
/// <returns>bool true if OK was pressed, false if cancel</returns>
public bool ShowConfigDialog()
{
DialogResult result = new SettingsForm().ShowDialog();
if (result == DialogResult.OK)
{
return true;
}
return false;
}
}
}

View file

@ -24,33 +24,42 @@ using System.Drawing;
using GreenshotPlugin.Core;
using GreenshotPlugin.Interfaces;
namespace Greenshot.Plugin.Flickr {
public class FlickrDestination : AbstractDestination {
private readonly FlickrPlugin _plugin;
public FlickrDestination(FlickrPlugin plugin) {
_plugin = plugin;
}
namespace Greenshot.Plugin.Flickr
{
public class FlickrDestination : AbstractDestination
{
private readonly FlickrPlugin _plugin;
public override string Designation => "Flickr";
public FlickrDestination(FlickrPlugin plugin)
{
_plugin = plugin;
}
public override string Description => Language.GetString("flickr", LangKey.upload_menu_item);
public override string Designation => "Flickr";
public override Image DisplayIcon {
get {
ComponentResourceManager resources = new ComponentResourceManager(typeof(FlickrPlugin));
return (Image)resources.GetObject("flickr");
}
}
public override string Description => Language.GetString("flickr", LangKey.upload_menu_item);
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) {
ExportInformation exportInformation = new ExportInformation(Designation, Description);
public override Image DisplayIcon
{
get
{
ComponentResourceManager resources = new ComponentResourceManager(typeof(FlickrPlugin));
return (Image) resources.GetObject("flickr");
}
}
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails)
{
ExportInformation exportInformation = new ExportInformation(Designation, Description);
bool uploaded = _plugin.Upload(captureDetails, surface, out var uploadUrl);
if (uploaded) {
exportInformation.ExportMade = true;
exportInformation.Uri = uploadUrl;
}
ProcessExport(exportInformation, surface);
return exportInformation;
}
}
if (uploaded)
{
exportInformation.ExportMade = true;
exportInformation.Uri = uploadUrl;
}
ProcessExport(exportInformation, surface);
return exportInformation;
}
}
}

View file

@ -33,99 +33,122 @@ using log4net;
namespace Greenshot.Plugin.Flickr
{
/// <summary>
/// This is the Flickr base code
/// </summary>
/// <summary>
/// This is the Flickr base code
/// </summary>
[Plugin("Flickr", true)]
public class FlickrPlugin : IGreenshotPlugin {
private static readonly ILog Log = LogManager.GetLogger(typeof(FlickrPlugin));
private static FlickrConfiguration _config;
private ComponentResourceManager _resources;
private ToolStripMenuItem _itemPlugInConfig;
public class FlickrPlugin : IGreenshotPlugin
{
private static readonly ILog Log = LogManager.GetLogger(typeof(FlickrPlugin));
private static FlickrConfiguration _config;
private ComponentResourceManager _resources;
private ToolStripMenuItem _itemPlugInConfig;
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(bool disposing) {
if (!disposing) {
return;
}
if (_itemPlugInConfig == null) {
return;
}
_itemPlugInConfig.Dispose();
_itemPlugInConfig = null;
}
protected void Dispose(bool disposing)
{
if (!disposing)
{
return;
}
/// <summary>
/// Implementation of the IGreenshotPlugin.Initialize
/// </summary>
public bool Initialize() {
// Register configuration (don't need the configuration itself)
_config = IniConfig.GetIniSection<FlickrConfiguration>();
_resources = new ComponentResourceManager(typeof(FlickrPlugin));
if (_itemPlugInConfig == null)
{
return;
}
_itemPlugInConfig = new ToolStripMenuItem
{
Text = Language.GetString("flickr", LangKey.Configure),
Image = (Image) _resources.GetObject("flickr")
};
_itemPlugInConfig.Click += ConfigMenuClick;
_itemPlugInConfig.Dispose();
_itemPlugInConfig = null;
}
/// <summary>
/// Implementation of the IGreenshotPlugin.Initialize
/// </summary>
public bool Initialize()
{
// Register configuration (don't need the configuration itself)
_config = IniConfig.GetIniSection<FlickrConfiguration>();
_resources = new ComponentResourceManager(typeof(FlickrPlugin));
_itemPlugInConfig = new ToolStripMenuItem
{
Text = Language.GetString("flickr", LangKey.Configure),
Image = (Image) _resources.GetObject("flickr")
};
_itemPlugInConfig.Click += ConfigMenuClick;
SimpleServiceProvider.Current.AddService<IDestination>(new FlickrDestination(this));
PluginUtils.AddToContextMenu(_itemPlugInConfig);
Language.LanguageChanged += OnLanguageChanged;
return true;
}
PluginUtils.AddToContextMenu(_itemPlugInConfig);
Language.LanguageChanged += OnLanguageChanged;
return true;
}
public void OnLanguageChanged(object sender, EventArgs e) {
if (_itemPlugInConfig != null) {
_itemPlugInConfig.Text = Language.GetString("flickr", LangKey.Configure);
}
}
public void OnLanguageChanged(object sender, EventArgs e)
{
if (_itemPlugInConfig != null)
{
_itemPlugInConfig.Text = Language.GetString("flickr", LangKey.Configure);
}
}
public void Shutdown() {
Log.Debug("Flickr Plugin shutdown.");
}
public void Shutdown()
{
Log.Debug("Flickr Plugin shutdown.");
}
/// <summary>
/// Implementation of the IPlugin.Configure
/// </summary>
public void Configure() {
_config.ShowConfigDialog();
}
/// <summary>
/// Implementation of the IPlugin.Configure
/// </summary>
public void Configure()
{
_config.ShowConfigDialog();
}
public void ConfigMenuClick(object sender, EventArgs eventArgs) {
_config.ShowConfigDialog();
}
public void ConfigMenuClick(object sender, EventArgs eventArgs)
{
_config.ShowConfigDialog();
}
public bool Upload(ICaptureDetails captureDetails, ISurface surface, out string uploadUrl) {
SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(_config.UploadFormat, _config.UploadJpegQuality, false);
uploadUrl = null;
try {
string flickrUrl = null;
new PleaseWaitForm().ShowAndWait("Flickr", Language.GetString("flickr", LangKey.communication_wait),
delegate {
string filename = Path.GetFileName(FilenameHelper.GetFilename(_config.UploadFormat, captureDetails));
flickrUrl = FlickrUtils.UploadToFlickr(surface, outputSettings, captureDetails.Title, filename);
}
);
public bool Upload(ICaptureDetails captureDetails, ISurface surface, out string uploadUrl)
{
SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(_config.UploadFormat, _config.UploadJpegQuality, false);
uploadUrl = null;
try
{
string flickrUrl = null;
new PleaseWaitForm().ShowAndWait("Flickr", Language.GetString("flickr", LangKey.communication_wait),
delegate
{
string filename = Path.GetFileName(FilenameHelper.GetFilename(_config.UploadFormat, captureDetails));
flickrUrl = FlickrUtils.UploadToFlickr(surface, outputSettings, captureDetails.Title, filename);
}
);
if (flickrUrl == null) {
return false;
}
uploadUrl = flickrUrl;
if (flickrUrl == null)
{
return false;
}
if (_config.AfterUploadLinkToClipBoard) {
ClipboardHelper.SetClipboardData(flickrUrl);
}
return true;
} catch (Exception e) {
Log.Error("Error uploading.", e);
MessageBox.Show(Language.GetString("flickr", LangKey.upload_failure) + " " + e.Message);
}
return false;
}
}
uploadUrl = flickrUrl;
if (_config.AfterUploadLinkToClipBoard)
{
ClipboardHelper.SetClipboardData(flickrUrl);
}
return true;
}
catch (Exception e)
{
Log.Error("Error uploading.", e);
MessageBox.Show(Language.GetString("flickr", LangKey.upload_failure) + " " + e.Message);
}
return false;
}
}
}

View file

@ -30,140 +30,203 @@ using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
using log4net;
namespace Greenshot.Plugin.Flickr {
/// <summary>
/// Description of FlickrUtils.
/// </summary>
public static class FlickrUtils {
private static readonly ILog LOG = LogManager.GetLogger(typeof(FlickrUtils));
private static readonly FlickrConfiguration config = IniConfig.GetIniSection<FlickrConfiguration>();
private const string FLICKR_API_BASE_URL = "https://api.flickr.com/services/";
private const string FLICKR_UPLOAD_URL = FLICKR_API_BASE_URL + "upload/";
// OAUTH
private const string FLICKR_OAUTH_BASE_URL = FLICKR_API_BASE_URL + "oauth/";
private const string FLICKR_ACCESS_TOKEN_URL = FLICKR_OAUTH_BASE_URL + "access_token";
private const string FLICKR_AUTHORIZE_URL = FLICKR_OAUTH_BASE_URL + "authorize";
private const string FLICKR_REQUEST_TOKEN_URL = FLICKR_OAUTH_BASE_URL + "request_token";
private const string FLICKR_FARM_URL = "https://farm{0}.staticflickr.com/{1}/{2}_{3}_o.{4}";
// REST
private const string FLICKR_REST_URL = FLICKR_API_BASE_URL + "rest/";
private const string FLICKR_GET_INFO_URL = FLICKR_REST_URL + "?method=flickr.photos.getInfo";
namespace Greenshot.Plugin.Flickr
{
/// <summary>
/// Description of FlickrUtils.
/// </summary>
public static class FlickrUtils
{
private static readonly ILog LOG = LogManager.GetLogger(typeof(FlickrUtils));
private static readonly FlickrConfiguration config = IniConfig.GetIniSection<FlickrConfiguration>();
private const string FLICKR_API_BASE_URL = "https://api.flickr.com/services/";
/// <summary>
/// Do the actual upload to Flickr
/// For more details on the available parameters, see: http://flickrnet.codeplex.com
/// </summary>
/// <param name="surfaceToUpload"></param>
/// <param name="outputSettings"></param>
/// <param name="title"></param>
/// <param name="filename"></param>
/// <returns>url to image</returns>
public static string UploadToFlickr(ISurface surfaceToUpload, SurfaceOutputSettings outputSettings, string title, string filename) {
var oAuth = new OAuthSession(FlickrCredentials.ConsumerKey, FlickrCredentials.ConsumerSecret)
{
BrowserSize = new Size(520, 800),
CheckVerifier = false,
AccessTokenUrl = FLICKR_ACCESS_TOKEN_URL,
AuthorizeUrl = FLICKR_AUTHORIZE_URL,
RequestTokenUrl = FLICKR_REQUEST_TOKEN_URL,
LoginTitle = "Flickr authorization",
Token = config.FlickrToken,
TokenSecret = config.FlickrTokenSecret
};
if (string.IsNullOrEmpty(oAuth.Token)) {
if (!oAuth.Authorize()) {
return null;
}
if (!string.IsNullOrEmpty(oAuth.Token)) {
config.FlickrToken = oAuth.Token;
}
if (!string.IsNullOrEmpty(oAuth.TokenSecret)) {
config.FlickrTokenSecret = oAuth.TokenSecret;
}
IniConfig.Save();
}
try {
IDictionary<string, object> signedParameters = new Dictionary<string, object>
{
{ "content_type", "2" }, // Screenshot
{ "tags", "Greenshot" },
{ "is_public", config.IsPublic ? "1" : "0" },
{ "is_friend", config.IsFriend ? "1" : "0" },
{ "is_family", config.IsFamily ? "1" : "0" },
{ "safety_level", $"{(int)config.SafetyLevel}" },
{ "hidden", config.HiddenFromSearch ? "1" : "2" }
};
IDictionary<string, object> otherParameters = new Dictionary<string, object>
{
{ "photo", new SurfaceContainer(surfaceToUpload, outputSettings, filename) }
};
string response = oAuth.MakeOAuthRequest(HTTPMethod.POST, FLICKR_UPLOAD_URL, signedParameters, otherParameters, null);
string photoId = GetPhotoId(response);
private const string FLICKR_UPLOAD_URL = FLICKR_API_BASE_URL + "upload/";
// Get Photo Info
signedParameters = new Dictionary<string, object> { { "photo_id", photoId } };
string photoInfo = oAuth.MakeOAuthRequest(HTTPMethod.POST, FLICKR_GET_INFO_URL, signedParameters, null, null);
return GetUrl(photoInfo);
} catch (Exception ex) {
LOG.Error("Upload error: ", ex);
throw;
} finally {
if (!string.IsNullOrEmpty(oAuth.Token)) {
config.FlickrToken = oAuth.Token;
}
if (!string.IsNullOrEmpty(oAuth.TokenSecret)) {
config.FlickrTokenSecret = oAuth.TokenSecret;
}
}
}
// OAUTH
private const string FLICKR_OAUTH_BASE_URL = FLICKR_API_BASE_URL + "oauth/";
private const string FLICKR_ACCESS_TOKEN_URL = FLICKR_OAUTH_BASE_URL + "access_token";
private const string FLICKR_AUTHORIZE_URL = FLICKR_OAUTH_BASE_URL + "authorize";
private const string FLICKR_REQUEST_TOKEN_URL = FLICKR_OAUTH_BASE_URL + "request_token";
private static string GetUrl(string response) {
try {
XmlDocument doc = new XmlDocument();
doc.LoadXml(response);
if (config.UsePageLink) {
XmlNodeList nodes = doc.GetElementsByTagName("url");
if (nodes.Count > 0) {
var xmlNode = nodes.Item(0);
if (xmlNode != null) {
return xmlNode.InnerText;
}
}
} else {
XmlNodeList nodes = doc.GetElementsByTagName("photo");
if (nodes.Count > 0) {
var item = nodes.Item(0);
if (item?.Attributes != null) {
string farmId = item.Attributes["farm"].Value;
string serverId = item.Attributes["server"].Value;
string photoId = item.Attributes["id"].Value;
string originalsecret = item.Attributes["originalsecret"].Value;
string originalFormat = item.Attributes["originalformat"].Value;
return string.Format(FLICKR_FARM_URL, farmId, serverId, photoId, originalsecret, originalFormat);
}
}
}
} catch (Exception ex) {
LOG.Error("Error parsing Flickr Response.", ex);
}
return null;
}
private const string FLICKR_FARM_URL = "https://farm{0}.staticflickr.com/{1}/{2}_{3}_o.{4}";
private static string GetPhotoId(string response) {
try {
XmlDocument doc = new XmlDocument();
doc.LoadXml(response);
XmlNodeList nodes = doc.GetElementsByTagName("photoid");
if (nodes.Count > 0) {
var xmlNode = nodes.Item(0);
if (xmlNode != null) {
return xmlNode.InnerText;
}
}
} catch (Exception ex) {
LOG.Error("Error parsing Flickr Response.", ex);
}
return null;
}
}
// REST
private const string FLICKR_REST_URL = FLICKR_API_BASE_URL + "rest/";
private const string FLICKR_GET_INFO_URL = FLICKR_REST_URL + "?method=flickr.photos.getInfo";
/// <summary>
/// Do the actual upload to Flickr
/// For more details on the available parameters, see: http://flickrnet.codeplex.com
/// </summary>
/// <param name="surfaceToUpload"></param>
/// <param name="outputSettings"></param>
/// <param name="title"></param>
/// <param name="filename"></param>
/// <returns>url to image</returns>
public static string UploadToFlickr(ISurface surfaceToUpload, SurfaceOutputSettings outputSettings, string title, string filename)
{
var oAuth = new OAuthSession(FlickrCredentials.ConsumerKey, FlickrCredentials.ConsumerSecret)
{
BrowserSize = new Size(520, 800),
CheckVerifier = false,
AccessTokenUrl = FLICKR_ACCESS_TOKEN_URL,
AuthorizeUrl = FLICKR_AUTHORIZE_URL,
RequestTokenUrl = FLICKR_REQUEST_TOKEN_URL,
LoginTitle = "Flickr authorization",
Token = config.FlickrToken,
TokenSecret = config.FlickrTokenSecret
};
if (string.IsNullOrEmpty(oAuth.Token))
{
if (!oAuth.Authorize())
{
return null;
}
if (!string.IsNullOrEmpty(oAuth.Token))
{
config.FlickrToken = oAuth.Token;
}
if (!string.IsNullOrEmpty(oAuth.TokenSecret))
{
config.FlickrTokenSecret = oAuth.TokenSecret;
}
IniConfig.Save();
}
try
{
IDictionary<string, object> signedParameters = new Dictionary<string, object>
{
{
"content_type", "2"
}, // Screenshot
{
"tags", "Greenshot"
},
{
"is_public", config.IsPublic ? "1" : "0"
},
{
"is_friend", config.IsFriend ? "1" : "0"
},
{
"is_family", config.IsFamily ? "1" : "0"
},
{
"safety_level", $"{(int) config.SafetyLevel}"
},
{
"hidden", config.HiddenFromSearch ? "1" : "2"
}
};
IDictionary<string, object> otherParameters = new Dictionary<string, object>
{
{
"photo", new SurfaceContainer(surfaceToUpload, outputSettings, filename)
}
};
string response = oAuth.MakeOAuthRequest(HTTPMethod.POST, FLICKR_UPLOAD_URL, signedParameters, otherParameters, null);
string photoId = GetPhotoId(response);
// Get Photo Info
signedParameters = new Dictionary<string, object>
{
{
"photo_id", photoId
}
};
string photoInfo = oAuth.MakeOAuthRequest(HTTPMethod.POST, FLICKR_GET_INFO_URL, signedParameters, null, null);
return GetUrl(photoInfo);
}
catch (Exception ex)
{
LOG.Error("Upload error: ", ex);
throw;
}
finally
{
if (!string.IsNullOrEmpty(oAuth.Token))
{
config.FlickrToken = oAuth.Token;
}
if (!string.IsNullOrEmpty(oAuth.TokenSecret))
{
config.FlickrTokenSecret = oAuth.TokenSecret;
}
}
}
private static string GetUrl(string response)
{
try
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(response);
if (config.UsePageLink)
{
XmlNodeList nodes = doc.GetElementsByTagName("url");
if (nodes.Count > 0)
{
var xmlNode = nodes.Item(0);
if (xmlNode != null)
{
return xmlNode.InnerText;
}
}
}
else
{
XmlNodeList nodes = doc.GetElementsByTagName("photo");
if (nodes.Count > 0)
{
var item = nodes.Item(0);
if (item?.Attributes != null)
{
string farmId = item.Attributes["farm"].Value;
string serverId = item.Attributes["server"].Value;
string photoId = item.Attributes["id"].Value;
string originalsecret = item.Attributes["originalsecret"].Value;
string originalFormat = item.Attributes["originalformat"].Value;
return string.Format(FLICKR_FARM_URL, farmId, serverId, photoId, originalsecret, originalFormat);
}
}
}
}
catch (Exception ex)
{
LOG.Error("Error parsing Flickr Response.", ex);
}
return null;
}
private static string GetPhotoId(string response)
{
try
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(response);
XmlNodeList nodes = doc.GetElementsByTagName("photoid");
if (nodes.Count > 0)
{
var xmlNode = nodes.Item(0);
if (xmlNode != null)
{
return xmlNode.InnerText;
}
}
}
catch (Exception ex)
{
LOG.Error("Error parsing Flickr Response.", ex);
}
return null;
}
}
}

View file

@ -21,7 +21,9 @@
using GreenshotPlugin.Controls;
namespace Greenshot.Plugin.Flickr.Forms {
public class FlickrForm : GreenshotForm {
}
namespace Greenshot.Plugin.Flickr.Forms
{
public class FlickrForm : GreenshotForm
{
}
}

View file

@ -19,19 +19,21 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Greenshot.Plugin.Flickr.Forms {
/// <summary>
/// Description of PasswordRequestForm.
/// </summary>
public partial class SettingsForm : FlickrForm {
public SettingsForm() {
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
CancelButton = buttonCancel;
AcceptButton = buttonOK;
}
}
namespace Greenshot.Plugin.Flickr.Forms
{
/// <summary>
/// Description of PasswordRequestForm.
/// </summary>
public partial class SettingsForm : FlickrForm
{
public SettingsForm()
{
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
CancelButton = buttonCancel;
AcceptButton = buttonOK;
}
}
}

View file

@ -18,11 +18,14 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Greenshot.Plugin.Flickr {
public enum LangKey {
upload_menu_item,
upload_failure,
communication_wait,
Configure
namespace Greenshot.Plugin.Flickr
{
public enum LangKey
{
upload_menu_item,
upload_failure,
communication_wait,
Configure
}
}

View file

@ -20,7 +20,9 @@
using GreenshotPlugin.Controls;
namespace Greenshot.Plugin.GooglePhotos.Forms {
public class GooglePhotosForm : GreenshotForm {
}
namespace Greenshot.Plugin.GooglePhotos.Forms
{
public class GooglePhotosForm : GreenshotForm
{
}
}

View file

@ -18,21 +18,21 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Greenshot.Plugin.GooglePhotos.Forms {
/// <summary>
/// Description of PasswordRequestForm.
/// </summary>
public partial class SettingsForm : GooglePhotosForm {
public SettingsForm()
{
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
CancelButton = buttonCancel;
AcceptButton = buttonOK;
}
}
namespace Greenshot.Plugin.GooglePhotos.Forms
{
/// <summary>
/// Description of PasswordRequestForm.
/// </summary>
public partial class SettingsForm : GooglePhotosForm
{
public SettingsForm()
{
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
CancelButton = buttonCancel;
AcceptButton = buttonOK;
}
}
}

View file

@ -24,71 +24,58 @@ using Greenshot.Plugin.GooglePhotos.Forms;
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.GooglePhotos {
/// <summary>
/// Description of GooglePhotosConfiguration.
/// </summary>
[IniSection("GooglePhotos", Description = "Greenshot GooglePhotos Plugin configuration")]
public class GooglePhotosConfiguration : IniSection {
[IniProperty("UploadFormat", Description="What file type to use for uploading", DefaultValue="png")]
public OutputFormat UploadFormat { get; set; }
namespace Greenshot.Plugin.GooglePhotos
{
/// <summary>
/// Description of GooglePhotosConfiguration.
/// </summary>
[IniSection("GooglePhotos", Description = "Greenshot GooglePhotos Plugin configuration")]
public class GooglePhotosConfiguration : IniSection
{
[IniProperty("UploadFormat", Description = "What file type to use for uploading", DefaultValue = "png")]
public OutputFormat UploadFormat { get; set; }
[IniProperty("UploadJpegQuality", Description="JPEG file save quality in %.", DefaultValue="80")]
public int UploadJpegQuality { get; set; }
[IniProperty("UploadJpegQuality", Description = "JPEG file save quality in %.", DefaultValue = "80")]
public int UploadJpegQuality { get; set; }
[IniProperty("AfterUploadLinkToClipBoard", Description = "After upload send GooglePhotos link to clipboard.", DefaultValue = "true")]
public bool AfterUploadLinkToClipBoard { get; set; }
[IniProperty("AddFilename", Description = "Is the filename passed on to GooglePhotos", DefaultValue = "False")]
public bool AddFilename {
get;
set;
}
[IniProperty("AfterUploadLinkToClipBoard", Description = "After upload send GooglePhotos link to clipboard.", DefaultValue = "true")]
public bool AfterUploadLinkToClipBoard { get; set; }
[IniProperty("UploadUser", Description = "The GooglePhotos user to upload to", DefaultValue = "default")]
public string UploadUser {
get;
set;
}
[IniProperty("AddFilename", Description = "Is the filename passed on to GooglePhotos", DefaultValue = "False")]
public bool AddFilename { get; set; }
[IniProperty("UploadAlbum", Description = "The GooglePhotos album to upload to", DefaultValue = "default")]
public string UploadAlbum {
get;
set;
}
[IniProperty("UploadUser", Description = "The GooglePhotos user to upload to", DefaultValue = "default")]
public string UploadUser { get; set; }
[IniProperty("RefreshToken", Description = "GooglePhotos authorization refresh Token", Encrypted = true)]
public string RefreshToken {
get;
set;
}
[IniProperty("UploadAlbum", Description = "The GooglePhotos album to upload to", DefaultValue = "default")]
public string UploadAlbum { get; set; }
/// <summary>
/// Not stored
/// </summary>
public string AccessToken {
get;
set;
}
[IniProperty("RefreshToken", Description = "GooglePhotos authorization refresh Token", Encrypted = true)]
public string RefreshToken { get; set; }
/// <summary>
/// Not stored
/// </summary>
public DateTimeOffset AccessTokenExpires {
get;
set;
}
/// <summary>
/// Not stored
/// </summary>
public string AccessToken { get; set; }
/// <summary>
/// A form for token
/// </summary>
/// <returns>bool true if OK was pressed, false if cancel</returns>
public bool ShowConfigDialog() {
DialogResult result = new SettingsForm().ShowDialog();
if (result == DialogResult.OK) {
return true;
}
return false;
}
/// <summary>
/// Not stored
/// </summary>
public DateTimeOffset AccessTokenExpires { get; set; }
}
/// <summary>
/// A form for token
/// </summary>
/// <returns>bool true if OK was pressed, false if cancel</returns>
public bool ShowConfigDialog()
{
DialogResult result = new SettingsForm().ShowDialog();
if (result == DialogResult.OK)
{
return true;
}
return false;
}
}
}

View file

@ -23,33 +23,42 @@ using System.Drawing;
using GreenshotPlugin.Core;
using GreenshotPlugin.Interfaces;
namespace Greenshot.Plugin.GooglePhotos {
public class GooglePhotosDestination : AbstractDestination {
private readonly GooglePhotosPlugin _plugin;
public GooglePhotosDestination(GooglePhotosPlugin plugin) {
_plugin = plugin;
}
namespace Greenshot.Plugin.GooglePhotos
{
public class GooglePhotosDestination : AbstractDestination
{
private readonly GooglePhotosPlugin _plugin;
public override string Designation => "GooglePhotos";
public GooglePhotosDestination(GooglePhotosPlugin plugin)
{
_plugin = plugin;
}
public override string Description => Language.GetString("googlephotos", LangKey.upload_menu_item);
public override string Designation => "GooglePhotos";
public override Image DisplayIcon {
get {
ComponentResourceManager resources = new ComponentResourceManager(typeof(GooglePhotosPlugin));
return (Image)resources.GetObject("GooglePhotos");
}
}
public override string Description => Language.GetString("googlephotos", LangKey.upload_menu_item);
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) {
ExportInformation exportInformation = new ExportInformation(Designation, Description);
public override Image DisplayIcon
{
get
{
ComponentResourceManager resources = new ComponentResourceManager(typeof(GooglePhotosPlugin));
return (Image) resources.GetObject("GooglePhotos");
}
}
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails)
{
ExportInformation exportInformation = new ExportInformation(Designation, Description);
bool uploaded = _plugin.Upload(captureDetails, surface, out var uploadUrl);
if (uploaded) {
exportInformation.ExportMade = true;
exportInformation.Uri = uploadUrl;
}
ProcessExport(exportInformation, surface);
return exportInformation;
}
}
if (uploaded)
{
exportInformation.ExportMade = true;
exportInformation.Uri = uploadUrl;
}
ProcessExport(exportInformation, surface);
return exportInformation;
}
}
}

View file

@ -29,97 +29,113 @@ using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
namespace Greenshot.Plugin.GooglePhotos {
/// <summary>
/// This is the GooglePhotos base code
/// </summary>
[Plugin("GooglePhotos", true)]
public class GooglePhotosPlugin : IGreenshotPlugin {
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(GooglePhotosPlugin));
private static GooglePhotosConfiguration _config;
private ComponentResourceManager _resources;
private ToolStripMenuItem _itemPlugInRoot;
namespace Greenshot.Plugin.GooglePhotos
{
/// <summary>
/// This is the GooglePhotos base code
/// </summary>
[Plugin("GooglePhotos", true)]
public class GooglePhotosPlugin : IGreenshotPlugin
{
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(GooglePhotosPlugin));
private static GooglePhotosConfiguration _config;
private ComponentResourceManager _resources;
private ToolStripMenuItem _itemPlugInRoot;
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!disposing) return;
if (_itemPlugInRoot == null) return;
_itemPlugInRoot.Dispose();
_itemPlugInRoot = null;
}
private void Dispose(bool disposing)
{
if (!disposing) return;
if (_itemPlugInRoot == null) return;
_itemPlugInRoot.Dispose();
_itemPlugInRoot = null;
}
/// <summary>
/// Implementation of the IGreenshotPlugin.Initialize
/// </summary>
public bool Initialize() {
SimpleServiceProvider.Current.AddService<IDestination>(new GooglePhotosDestination(this));
/// <summary>
/// Implementation of the IGreenshotPlugin.Initialize
/// </summary>
public bool Initialize()
{
SimpleServiceProvider.Current.AddService<IDestination>(new GooglePhotosDestination(this));
// Get configuration
_config = IniConfig.GetIniSection<GooglePhotosConfiguration>();
_resources = new ComponentResourceManager(typeof(GooglePhotosPlugin));
// Get configuration
_config = IniConfig.GetIniSection<GooglePhotosConfiguration>();
_resources = new ComponentResourceManager(typeof(GooglePhotosPlugin));
_itemPlugInRoot = new ToolStripMenuItem
{
Text = Language.GetString("googlephotos", LangKey.Configure),
Image = (Image) _resources.GetObject("GooglePhotos")
};
_itemPlugInRoot.Click += ConfigMenuClick;
PluginUtils.AddToContextMenu(_itemPlugInRoot);
Language.LanguageChanged += OnLanguageChanged;
return true;
}
_itemPlugInRoot = new ToolStripMenuItem
{
Text = Language.GetString("googlephotos", LangKey.Configure),
Image = (Image) _resources.GetObject("GooglePhotos")
};
_itemPlugInRoot.Click += ConfigMenuClick;
PluginUtils.AddToContextMenu(_itemPlugInRoot);
Language.LanguageChanged += OnLanguageChanged;
return true;
}
public void OnLanguageChanged(object sender, EventArgs e) {
if (_itemPlugInRoot != null) {
_itemPlugInRoot.Text = Language.GetString("googlephotos", LangKey.Configure);
}
}
public void OnLanguageChanged(object sender, EventArgs e)
{
if (_itemPlugInRoot != null)
{
_itemPlugInRoot.Text = Language.GetString("googlephotos", LangKey.Configure);
}
}
public void Shutdown() {
Log.Debug("GooglePhotos Plugin shutdown.");
Language.LanguageChanged -= OnLanguageChanged;
//host.OnImageEditorOpen -= new OnImageEditorOpenHandler(ImageEditorOpened);
}
public void Shutdown()
{
Log.Debug("GooglePhotos Plugin shutdown.");
Language.LanguageChanged -= OnLanguageChanged;
//host.OnImageEditorOpen -= new OnImageEditorOpenHandler(ImageEditorOpened);
}
/// <summary>
/// Implementation of the IPlugin.Configure
/// </summary>
public void Configure() {
_config.ShowConfigDialog();
}
/// <summary>
/// Implementation of the IPlugin.Configure
/// </summary>
public void Configure()
{
_config.ShowConfigDialog();
}
public void ConfigMenuClick(object sender, EventArgs eventArgs) {
Configure();
}
public void ConfigMenuClick(object sender, EventArgs eventArgs)
{
Configure();
}
public bool Upload(ICaptureDetails captureDetails, ISurface surfaceToUpload, out string uploadUrl) {
SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(_config.UploadFormat, _config.UploadJpegQuality);
try {
string url = null;
new PleaseWaitForm().ShowAndWait("GooglePhotos", Language.GetString("googlephotos", LangKey.communication_wait),
delegate
{
string filename = Path.GetFileName(FilenameHelper.GetFilename(_config.UploadFormat, captureDetails));
url = GooglePhotosUtils.UploadToGooglePhotos(surfaceToUpload, outputSettings, captureDetails.Title, filename);
}
);
uploadUrl = url;
public bool Upload(ICaptureDetails captureDetails, ISurface surfaceToUpload, out string uploadUrl)
{
SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(_config.UploadFormat, _config.UploadJpegQuality);
try
{
string url = null;
new PleaseWaitForm().ShowAndWait("GooglePhotos", Language.GetString("googlephotos", LangKey.communication_wait),
delegate
{
string filename = Path.GetFileName(FilenameHelper.GetFilename(_config.UploadFormat, captureDetails));
url = GooglePhotosUtils.UploadToGooglePhotos(surfaceToUpload, outputSettings, captureDetails.Title, filename);
}
);
uploadUrl = url;
if (uploadUrl != null && _config.AfterUploadLinkToClipBoard) {
ClipboardHelper.SetClipboardData(uploadUrl);
}
return true;
} catch (Exception e) {
Log.Error("Error uploading.", e);
MessageBox.Show(Language.GetString("googlephotos", LangKey.upload_failure) + " " + e.Message);
}
uploadUrl = null;
return false;
}
}
if (uploadUrl != null && _config.AfterUploadLinkToClipBoard)
{
ClipboardHelper.SetClipboardData(uploadUrl);
}
return true;
}
catch (Exception e)
{
Log.Error("Error uploading.", e);
MessageBox.Show(Language.GetString("googlephotos", LangKey.upload_failure) + " " + e.Message);
}
uploadUrl = null;
return false;
}
}
}

View file

@ -26,95 +26,118 @@ using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
namespace Greenshot.Plugin.GooglePhotos {
/// <summary>
/// Description of GooglePhotosUtils.
/// </summary>
public static class GooglePhotosUtils {
private const string GooglePhotosScope = "https://picasaweb.google.com/data/";
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(GooglePhotosUtils));
private static readonly GooglePhotosConfiguration Config = IniConfig.GetIniSection<GooglePhotosConfiguration>();
private const string AuthUrl = "https://accounts.google.com/o/oauth2/auth?response_type=code&client_id={ClientId}&redirect_uri={RedirectUrl}&state={State}&scope=" + GooglePhotosScope;
private const string TokenUrl = "https://www.googleapis.com/oauth2/v3/token";
private const string UploadUrl = "https://picasaweb.google.com/data/feed/api/user/{0}/albumid/{1}";
namespace Greenshot.Plugin.GooglePhotos
{
/// <summary>
/// Description of GooglePhotosUtils.
/// </summary>
public static class GooglePhotosUtils
{
private const string GooglePhotosScope = "https://picasaweb.google.com/data/";
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(GooglePhotosUtils));
private static readonly GooglePhotosConfiguration Config = IniConfig.GetIniSection<GooglePhotosConfiguration>();
/// <summary>
/// Do the actual upload to GooglePhotos
/// </summary>
/// <param name="surfaceToUpload">Image to upload</param>
/// <param name="outputSettings">SurfaceOutputSettings</param>
/// <param name="title">string</param>
/// <param name="filename">string</param>
/// <returns>GooglePhotosResponse</returns>
public static string UploadToGooglePhotos(ISurface surfaceToUpload, SurfaceOutputSettings outputSettings, string title, string filename) {
// Fill the OAuth2Settings
var settings = new OAuth2Settings
{
AuthUrlPattern = AuthUrl,
TokenUrl = TokenUrl,
CloudServiceName = "GooglePhotos",
ClientId = GooglePhotosCredentials.ClientId,
ClientSecret = GooglePhotosCredentials.ClientSecret,
AuthorizeMode = OAuth2AuthorizeMode.JsonReceiver,
RefreshToken = Config.RefreshToken,
AccessToken = Config.AccessToken,
AccessTokenExpires = Config.AccessTokenExpires
};
private const string AuthUrl = "https://accounts.google.com/o/oauth2/auth?response_type=code&client_id={ClientId}&redirect_uri={RedirectUrl}&state={State}&scope=" +
GooglePhotosScope;
// Copy the settings from the config, which is kept in memory and on the disk
private const string TokenUrl = "https://www.googleapis.com/oauth2/v3/token";
private const string UploadUrl = "https://picasaweb.google.com/data/feed/api/user/{0}/albumid/{1}";
try {
var webRequest = OAuth2Helper.CreateOAuth2WebRequest(HTTPMethod.POST, string.Format(UploadUrl, Config.UploadUser, Config.UploadAlbum), settings);
if (Config.AddFilename) {
webRequest.Headers.Add("Slug", NetworkHelper.EscapeDataString(filename));
}
SurfaceContainer container = new SurfaceContainer(surfaceToUpload, outputSettings, filename);
container.Upload(webRequest);
/// <summary>
/// Do the actual upload to GooglePhotos
/// </summary>
/// <param name="surfaceToUpload">Image to upload</param>
/// <param name="outputSettings">SurfaceOutputSettings</param>
/// <param name="title">string</param>
/// <param name="filename">string</param>
/// <returns>GooglePhotosResponse</returns>
public static string UploadToGooglePhotos(ISurface surfaceToUpload, SurfaceOutputSettings outputSettings, string title, string filename)
{
// Fill the OAuth2Settings
var settings = new OAuth2Settings
{
AuthUrlPattern = AuthUrl,
TokenUrl = TokenUrl,
CloudServiceName = "GooglePhotos",
ClientId = GooglePhotosCredentials.ClientId,
ClientSecret = GooglePhotosCredentials.ClientSecret,
AuthorizeMode = OAuth2AuthorizeMode.JsonReceiver,
RefreshToken = Config.RefreshToken,
AccessToken = Config.AccessToken,
AccessTokenExpires = Config.AccessTokenExpires
};
string response = NetworkHelper.GetResponseAsString(webRequest);
// Copy the settings from the config, which is kept in memory and on the disk
return ParseResponse(response);
} finally {
// Copy the settings back to the config, so they are stored.
Config.RefreshToken = settings.RefreshToken;
Config.AccessToken = settings.AccessToken;
Config.AccessTokenExpires = settings.AccessTokenExpires;
Config.IsDirty = true;
IniConfig.Save();
}
}
try
{
var webRequest = OAuth2Helper.CreateOAuth2WebRequest(HTTPMethod.POST, string.Format(UploadUrl, Config.UploadUser, Config.UploadAlbum), settings);
if (Config.AddFilename)
{
webRequest.Headers.Add("Slug", NetworkHelper.EscapeDataString(filename));
}
/// <summary>
/// Parse the upload URL from the response
/// </summary>
/// <param name="response"></param>
/// <returns></returns>
public static string ParseResponse(string response) {
if (response == null) {
return null;
}
try {
XmlDocument doc = new XmlDocument();
doc.LoadXml(response);
XmlNodeList nodes = doc.GetElementsByTagName("link", "*");
if(nodes.Count > 0) {
string url = null;
foreach(XmlNode node in nodes) {
if (node.Attributes != null) {
url = node.Attributes["href"].Value;
string rel = node.Attributes["rel"].Value;
// Pictures with rel="http://schemas.google.com/photos/2007#canonical" are the direct link
if (rel != null && rel.EndsWith("canonical")) {
break;
}
}
}
return url;
}
} catch(Exception e) {
Log.ErrorFormat("Could not parse GooglePhotos response due to error {0}, response was: {1}", e.Message, response);
}
return null;
}
}
SurfaceContainer container = new SurfaceContainer(surfaceToUpload, outputSettings, filename);
container.Upload(webRequest);
string response = NetworkHelper.GetResponseAsString(webRequest);
return ParseResponse(response);
}
finally
{
// Copy the settings back to the config, so they are stored.
Config.RefreshToken = settings.RefreshToken;
Config.AccessToken = settings.AccessToken;
Config.AccessTokenExpires = settings.AccessTokenExpires;
Config.IsDirty = true;
IniConfig.Save();
}
}
/// <summary>
/// Parse the upload URL from the response
/// </summary>
/// <param name="response"></param>
/// <returns></returns>
public static string ParseResponse(string response)
{
if (response == null)
{
return null;
}
try
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(response);
XmlNodeList nodes = doc.GetElementsByTagName("link", "*");
if (nodes.Count > 0)
{
string url = null;
foreach (XmlNode node in nodes)
{
if (node.Attributes != null)
{
url = node.Attributes["href"].Value;
string rel = node.Attributes["rel"].Value;
// Pictures with rel="http://schemas.google.com/photos/2007#canonical" are the direct link
if (rel != null && rel.EndsWith("canonical"))
{
break;
}
}
}
return url;
}
}
catch (Exception e)
{
Log.ErrorFormat("Could not parse GooglePhotos response due to error {0}, response was: {1}", e.Message, response);
}
return null;
}
}
}

View file

@ -18,12 +18,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Greenshot.Plugin.GooglePhotos {
public enum LangKey
{
upload_menu_item,
upload_failure,
communication_wait,
Configure
}
namespace Greenshot.Plugin.GooglePhotos
{
public enum LangKey
{
upload_menu_item,
upload_failure,
communication_wait,
Configure
}
}

View file

@ -21,10 +21,12 @@
using GreenshotPlugin.Controls;
namespace Greenshot.Plugin.Imgur.Forms {
/// <summary>
/// This class is needed for design-time resolving of the language files
/// </summary>
public class ImgurForm : GreenshotForm {
}
namespace Greenshot.Plugin.Imgur.Forms
{
/// <summary>
/// This class is needed for design-time resolving of the language files
/// </summary>
public class ImgurForm : GreenshotForm
{
}
}

View file

@ -27,195 +27,237 @@ using GreenshotPlugin.Controls;
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.Imgur.Forms {
/// <summary>
/// Imgur history form
/// </summary>
public sealed partial class ImgurHistory : ImgurForm {
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ImgurHistory));
private readonly GreenshotColumnSorter _columnSorter;
private static readonly object Lock = new object();
private static readonly ImgurConfiguration Config = IniConfig.GetIniSection<ImgurConfiguration>();
private static ImgurHistory _instance;
namespace Greenshot.Plugin.Imgur.Forms
{
/// <summary>
/// Imgur history form
/// </summary>
public sealed partial class ImgurHistory : ImgurForm
{
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ImgurHistory));
private readonly GreenshotColumnSorter _columnSorter;
private static readonly object Lock = new object();
private static readonly ImgurConfiguration Config = IniConfig.GetIniSection<ImgurConfiguration>();
private static ImgurHistory _instance;
public static void ShowHistory() {
lock (Lock)
{
if (ImgurUtils.IsHistoryLoadingNeeded())
{
// Run upload in the background
new PleaseWaitForm().ShowAndWait("Imgur " + Language.GetString("imgur", LangKey.history), Language.GetString("imgur", LangKey.communication_wait),
ImgurUtils.LoadHistory
);
}
public static void ShowHistory()
{
lock (Lock)
{
if (ImgurUtils.IsHistoryLoadingNeeded())
{
// Run upload in the background
new PleaseWaitForm().ShowAndWait("Imgur " + Language.GetString("imgur", LangKey.history), Language.GetString("imgur", LangKey.communication_wait),
ImgurUtils.LoadHistory
);
}
// Make sure the history is loaded, will be done only once
if (_instance == null)
{
_instance = new ImgurHistory();
}
if (!_instance.Visible)
{
_instance.Show();
}
_instance.Redraw();
}
}
// Make sure the history is loaded, will be done only once
if (_instance == null)
{
_instance = new ImgurHistory();
}
private ImgurHistory() {
ManualLanguageApply = true;
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
AcceptButton = finishedButton;
CancelButton = finishedButton;
// Init sorting
_columnSorter = new GreenshotColumnSorter();
listview_imgur_uploads.ListViewItemSorter = _columnSorter;
_columnSorter.SortColumn = 3;
_columnSorter.Order = SortOrder.Descending;
Redraw();
if (listview_imgur_uploads.Items.Count > 0) {
listview_imgur_uploads.Items[0].Selected = true;
}
ApplyLanguage();
if (Config.Credits > 0) {
Text = Text + " (" + Config.Credits + " credits)";
}
}
if (!_instance.Visible)
{
_instance.Show();
}
private void Redraw() {
// Should fix Bug #3378699
pictureBox1.Image = pictureBox1.ErrorImage;
listview_imgur_uploads.BeginUpdate();
listview_imgur_uploads.Items.Clear();
listview_imgur_uploads.Columns.Clear();
string[] columns = { "hash", "title", "deleteHash", "Date"};
foreach (string column in columns) {
listview_imgur_uploads.Columns.Add(column);
}
foreach (ImgurInfo imgurInfo in Config.runtimeImgurHistory.Values) {
var item = new ListViewItem(imgurInfo.Hash)
{
Tag = imgurInfo
};
item.SubItems.Add(imgurInfo.Title);
item.SubItems.Add(imgurInfo.DeleteHash);
item.SubItems.Add(imgurInfo.Timestamp.ToString("yyyy-MM-dd HH:mm:ss", DateTimeFormatInfo.InvariantInfo));
listview_imgur_uploads.Items.Add(item);
}
for (int i = 0; i < columns.Length; i++) {
listview_imgur_uploads.AutoResizeColumn(i, ColumnHeaderAutoResizeStyle.ColumnContent);
}
_instance.Redraw();
}
}
listview_imgur_uploads.EndUpdate();
listview_imgur_uploads.Refresh();
deleteButton.Enabled = false;
openButton.Enabled = false;
clipboardButton.Enabled = false;
}
private ImgurHistory()
{
ManualLanguageApply = true;
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
AcceptButton = finishedButton;
CancelButton = finishedButton;
// Init sorting
_columnSorter = new GreenshotColumnSorter();
listview_imgur_uploads.ListViewItemSorter = _columnSorter;
_columnSorter.SortColumn = 3;
_columnSorter.Order = SortOrder.Descending;
Redraw();
if (listview_imgur_uploads.Items.Count > 0)
{
listview_imgur_uploads.Items[0].Selected = true;
}
private void Listview_imgur_uploadsSelectedIndexChanged(object sender, EventArgs e) {
pictureBox1.Image = pictureBox1.ErrorImage;
if (listview_imgur_uploads.SelectedItems.Count > 0) {
deleteButton.Enabled = true;
openButton.Enabled = true;
clipboardButton.Enabled = true;
if (listview_imgur_uploads.SelectedItems.Count == 1) {
ImgurInfo imgurInfo = (ImgurInfo)listview_imgur_uploads.SelectedItems[0].Tag;
pictureBox1.Image = imgurInfo.Image;
}
} else {
pictureBox1.Image = pictureBox1.ErrorImage;
deleteButton.Enabled = false;
openButton.Enabled = false;
clipboardButton.Enabled = false;
}
}
ApplyLanguage();
if (Config.Credits > 0)
{
Text = Text + " (" + Config.Credits + " credits)";
}
}
private void DeleteButtonClick(object sender, EventArgs e) {
if (listview_imgur_uploads.SelectedItems.Count > 0) {
for (int i = 0; i < listview_imgur_uploads.SelectedItems.Count; i++) {
ImgurInfo imgurInfo = (ImgurInfo)listview_imgur_uploads.SelectedItems[i].Tag;
DialogResult result = MessageBox.Show(Language.GetFormattedString("imgur", LangKey.delete_question, imgurInfo.Title), Language.GetFormattedString("imgur", LangKey.delete_title, imgurInfo.Hash), MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (result != DialogResult.Yes)
{
continue;
}
// Should fix Bug #3378699
pictureBox1.Image = pictureBox1.ErrorImage;
try {
new PleaseWaitForm().ShowAndWait("Imgur", Language.GetString("imgur", LangKey.communication_wait),
delegate {
ImgurUtils.DeleteImgurImage(imgurInfo);
}
);
} catch (Exception ex) {
Log.Warn("Problem communicating with Imgur: ", ex);
}
private void Redraw()
{
// Should fix Bug #3378699
pictureBox1.Image = pictureBox1.ErrorImage;
listview_imgur_uploads.BeginUpdate();
listview_imgur_uploads.Items.Clear();
listview_imgur_uploads.Columns.Clear();
string[] columns =
{
"hash", "title", "deleteHash", "Date"
};
foreach (string column in columns)
{
listview_imgur_uploads.Columns.Add(column);
}
imgurInfo.Dispose();
}
}
Redraw();
}
foreach (ImgurInfo imgurInfo in Config.runtimeImgurHistory.Values)
{
var item = new ListViewItem(imgurInfo.Hash)
{
Tag = imgurInfo
};
item.SubItems.Add(imgurInfo.Title);
item.SubItems.Add(imgurInfo.DeleteHash);
item.SubItems.Add(imgurInfo.Timestamp.ToString("yyyy-MM-dd HH:mm:ss", DateTimeFormatInfo.InvariantInfo));
listview_imgur_uploads.Items.Add(item);
}
private void ClipboardButtonClick(object sender, EventArgs e) {
StringBuilder links = new StringBuilder();
if (listview_imgur_uploads.SelectedItems.Count > 0) {
for (int i = 0; i < listview_imgur_uploads.SelectedItems.Count; i++)
{
ImgurInfo imgurInfo = (ImgurInfo)listview_imgur_uploads.SelectedItems[i].Tag;
links.AppendLine(Config.UsePageLink ? imgurInfo.Page : imgurInfo.Original);
}
}
ClipboardHelper.SetClipboardData(links.ToString());
}
for (int i = 0; i < columns.Length; i++)
{
listview_imgur_uploads.AutoResizeColumn(i, ColumnHeaderAutoResizeStyle.ColumnContent);
}
private void ClearHistoryButtonClick(object sender, EventArgs e) {
DialogResult result = MessageBox.Show(Language.GetString("imgur", LangKey.clear_question), "Imgur", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (result == DialogResult.Yes) {
Config.runtimeImgurHistory.Clear();
Config.ImgurUploadHistory.Clear();
IniConfig.Save();
Redraw();
}
}
listview_imgur_uploads.EndUpdate();
listview_imgur_uploads.Refresh();
deleteButton.Enabled = false;
openButton.Enabled = false;
clipboardButton.Enabled = false;
}
private void FinishedButtonClick(object sender, EventArgs e)
{
Hide();
}
private void Listview_imgur_uploadsSelectedIndexChanged(object sender, EventArgs e)
{
pictureBox1.Image = pictureBox1.ErrorImage;
if (listview_imgur_uploads.SelectedItems.Count > 0)
{
deleteButton.Enabled = true;
openButton.Enabled = true;
clipboardButton.Enabled = true;
if (listview_imgur_uploads.SelectedItems.Count == 1)
{
ImgurInfo imgurInfo = (ImgurInfo) listview_imgur_uploads.SelectedItems[0].Tag;
pictureBox1.Image = imgurInfo.Image;
}
}
else
{
pictureBox1.Image = pictureBox1.ErrorImage;
deleteButton.Enabled = false;
openButton.Enabled = false;
clipboardButton.Enabled = false;
}
}
private void OpenButtonClick(object sender, EventArgs e) {
if (listview_imgur_uploads.SelectedItems.Count > 0) {
for (int i = 0; i < listview_imgur_uploads.SelectedItems.Count; i++) {
ImgurInfo imgurInfo = (ImgurInfo)listview_imgur_uploads.SelectedItems[i].Tag;
System.Diagnostics.Process.Start(imgurInfo.Page);
}
}
}
private void DeleteButtonClick(object sender, EventArgs e)
{
if (listview_imgur_uploads.SelectedItems.Count > 0)
{
for (int i = 0; i < listview_imgur_uploads.SelectedItems.Count; i++)
{
ImgurInfo imgurInfo = (ImgurInfo) listview_imgur_uploads.SelectedItems[i].Tag;
DialogResult result = MessageBox.Show(Language.GetFormattedString("imgur", LangKey.delete_question, imgurInfo.Title),
Language.GetFormattedString("imgur", LangKey.delete_title, imgurInfo.Hash), MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (result != DialogResult.Yes)
{
continue;
}
private void listview_imgur_uploads_ColumnClick(object sender, ColumnClickEventArgs e) {
// Determine if clicked column is already the column that is being sorted.
if (e.Column == _columnSorter.SortColumn) {
// Reverse the current sort direction for this column.
_columnSorter.Order = _columnSorter.Order == SortOrder.Ascending ? SortOrder.Descending : SortOrder.Ascending;
} else {
// Set the column number that is to be sorted; default to ascending.
_columnSorter.SortColumn = e.Column;
_columnSorter.Order = SortOrder.Ascending;
}
// Should fix Bug #3378699
pictureBox1.Image = pictureBox1.ErrorImage;
try
{
new PleaseWaitForm().ShowAndWait("Imgur", Language.GetString("imgur", LangKey.communication_wait),
delegate { ImgurUtils.DeleteImgurImage(imgurInfo); }
);
}
catch (Exception ex)
{
Log.Warn("Problem communicating with Imgur: ", ex);
}
// Perform the sort with these new sort options.
listview_imgur_uploads.Sort();
}
imgurInfo.Dispose();
}
}
Redraw();
}
private void ClipboardButtonClick(object sender, EventArgs e)
{
StringBuilder links = new StringBuilder();
if (listview_imgur_uploads.SelectedItems.Count > 0)
{
for (int i = 0; i < listview_imgur_uploads.SelectedItems.Count; i++)
{
ImgurInfo imgurInfo = (ImgurInfo) listview_imgur_uploads.SelectedItems[i].Tag;
links.AppendLine(Config.UsePageLink ? imgurInfo.Page : imgurInfo.Original);
}
}
ClipboardHelper.SetClipboardData(links.ToString());
}
private void ClearHistoryButtonClick(object sender, EventArgs e)
{
DialogResult result = MessageBox.Show(Language.GetString("imgur", LangKey.clear_question), "Imgur", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (result == DialogResult.Yes)
{
Config.runtimeImgurHistory.Clear();
Config.ImgurUploadHistory.Clear();
IniConfig.Save();
Redraw();
}
}
private void FinishedButtonClick(object sender, EventArgs e)
{
Hide();
}
private void OpenButtonClick(object sender, EventArgs e)
{
if (listview_imgur_uploads.SelectedItems.Count > 0)
{
for (int i = 0; i < listview_imgur_uploads.SelectedItems.Count; i++)
{
ImgurInfo imgurInfo = (ImgurInfo) listview_imgur_uploads.SelectedItems[i].Tag;
System.Diagnostics.Process.Start(imgurInfo.Page);
}
}
}
private void listview_imgur_uploads_ColumnClick(object sender, ColumnClickEventArgs e)
{
// Determine if clicked column is already the column that is being sorted.
if (e.Column == _columnSorter.SortColumn)
{
// Reverse the current sort direction for this column.
_columnSorter.Order = _columnSorter.Order == SortOrder.Ascending ? SortOrder.Descending : SortOrder.Ascending;
}
else
{
// Set the column number that is to be sorted; default to ascending.
_columnSorter.SortColumn = e.Column;
_columnSorter.Order = SortOrder.Ascending;
}
// Perform the sort with these new sort options.
listview_imgur_uploads.Sort();
}
private void ImgurHistoryFormClosing(object sender, FormClosingEventArgs e)
{
_instance = null;
}
}
private void ImgurHistoryFormClosing(object sender, FormClosingEventArgs e)
{
_instance = null;
}
}
}

View file

@ -21,25 +21,28 @@
using System;
namespace Greenshot.Plugin.Imgur.Forms {
/// <summary>
/// Description of PasswordRequestForm.
/// </summary>
public partial class SettingsForm : ImgurForm {
public SettingsForm()
{
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
CancelButton = buttonCancel;
AcceptButton = buttonOK;
namespace Greenshot.Plugin.Imgur.Forms
{
/// <summary>
/// Description of PasswordRequestForm.
/// </summary>
public partial class SettingsForm : ImgurForm
{
public SettingsForm()
{
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
CancelButton = buttonCancel;
AcceptButton = buttonOK;
historyButton.Enabled = ImgurUtils.IsHistoryLoadingNeeded();
}
historyButton.Enabled = ImgurUtils.IsHistoryLoadingNeeded();
}
private void ButtonHistoryClick(object sender, EventArgs e) {
ImgurHistory.ShowHistory();
}
}
private void ButtonHistoryClick(object sender, EventArgs e)
{
ImgurHistory.ShowHistory();
}
}
}

View file

@ -26,64 +26,70 @@ using Greenshot.Plugin.Imgur.Forms;
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.Imgur {
/// <summary>
/// Description of ImgurConfiguration.
/// </summary>
[IniSection("Imgur", Description="Greenshot Imgur Plugin configuration")]
public class ImgurConfiguration : IniSection {
[IniProperty("ImgurApi3Url", Description = "Url to Imgur system.", DefaultValue = "https://api.imgur.com/3")]
public string ImgurApi3Url { get; set; }
namespace Greenshot.Plugin.Imgur
{
/// <summary>
/// Description of ImgurConfiguration.
/// </summary>
[IniSection("Imgur", Description = "Greenshot Imgur Plugin configuration")]
public class ImgurConfiguration : IniSection
{
[IniProperty("ImgurApi3Url", Description = "Url to Imgur system.", DefaultValue = "https://api.imgur.com/3")]
public string ImgurApi3Url { get; set; }
[IniProperty("UploadFormat", Description="What file type to use for uploading", DefaultValue="png")]
public OutputFormat UploadFormat { get; set; }
[IniProperty("UploadJpegQuality", Description="JPEG file save quality in %.", DefaultValue="80")]
public int UploadJpegQuality { get; set; }
[IniProperty("UploadReduceColors", Description="Reduce color amount of the uploaded image to 256", DefaultValue="False")]
public bool UploadReduceColors { get; set; }
[IniProperty("CopyLinkToClipboard", Description = "Copy the link, which one is controlled by the UsePageLink, on the clipboard", DefaultValue = "True")]
public bool CopyLinkToClipboard { get; set; }
[IniProperty("UsePageLink", Description = "Use pagelink instead of direct link on the clipboard", DefaultValue = "False")]
public bool UsePageLink { get; set; }
[IniProperty("AnonymousAccess", Description = "Use anonymous access to Imgur", DefaultValue="true")]
public bool AnonymousAccess { get; set; }
[IniProperty("UploadFormat", Description = "What file type to use for uploading", DefaultValue = "png")]
public OutputFormat UploadFormat { get; set; }
[IniProperty("RefreshToken", Description = "Imgur refresh Token", Encrypted = true, ExcludeIfNull = true)]
public string RefreshToken { get; set; }
[IniProperty("UploadJpegQuality", Description = "JPEG file save quality in %.", DefaultValue = "80")]
public int UploadJpegQuality { get; set; }
/// <summary>
/// AccessToken, not stored
/// </summary>
public string AccessToken { get; set; }
[IniProperty("UploadReduceColors", Description = "Reduce color amount of the uploaded image to 256", DefaultValue = "False")]
public bool UploadReduceColors { get; set; }
/// <summary>
/// AccessTokenExpires, not stored
/// </summary>
public DateTimeOffset AccessTokenExpires { get; set; }
[IniProperty("CopyLinkToClipboard", Description = "Copy the link, which one is controlled by the UsePageLink, on the clipboard", DefaultValue = "True")]
public bool CopyLinkToClipboard { get; set; }
[IniProperty("AddTitle", Description = "Is the title passed on to Imgur", DefaultValue = "False")]
public bool AddTitle { get; set; }
[IniProperty("AddFilename", Description = "Is the filename passed on to Imgur", DefaultValue = "False")]
public bool AddFilename { get; set; }
[IniProperty("FilenamePattern", Description = "Filename for the Imgur upload", DefaultValue = "${capturetime:d\"yyyyMMdd-HHmm\"}")]
public string FilenamePattern { get; set; }
[IniProperty("UsePageLink", Description = "Use pagelink instead of direct link on the clipboard", DefaultValue = "False")]
public bool UsePageLink { get; set; }
[IniProperty("ImgurUploadHistory", Description="Imgur upload history (ImgurUploadHistory.hash=deleteHash)")]
public Dictionary<string, string> ImgurUploadHistory { get; set; }
[IniProperty("AnonymousAccess", Description = "Use anonymous access to Imgur", DefaultValue = "true")]
public bool AnonymousAccess { get; set; }
// Not stored, only run-time!
public Dictionary<string, ImgurInfo> runtimeImgurHistory = new Dictionary<string, ImgurInfo>();
public int Credits {
get;
set;
}
[IniProperty("RefreshToken", Description = "Imgur refresh Token", Encrypted = true, ExcludeIfNull = true)]
public string RefreshToken { get; set; }
/// <summary>
/// Supply values we can't put as defaults
/// </summary>
/// <param name="property">The property to return a default for</param>
/// <returns>object with the default value for the supplied property</returns>
public override object GetDefault(string property) =>
/// <summary>
/// AccessToken, not stored
/// </summary>
public string AccessToken { get; set; }
/// <summary>
/// AccessTokenExpires, not stored
/// </summary>
public DateTimeOffset AccessTokenExpires { get; set; }
[IniProperty("AddTitle", Description = "Is the title passed on to Imgur", DefaultValue = "False")]
public bool AddTitle { get; set; }
[IniProperty("AddFilename", Description = "Is the filename passed on to Imgur", DefaultValue = "False")]
public bool AddFilename { get; set; }
[IniProperty("FilenamePattern", Description = "Filename for the Imgur upload", DefaultValue = "${capturetime:d\"yyyyMMdd-HHmm\"}")]
public string FilenamePattern { get; set; }
[IniProperty("ImgurUploadHistory", Description = "Imgur upload history (ImgurUploadHistory.hash=deleteHash)")]
public Dictionary<string, string> ImgurUploadHistory { get; set; }
// Not stored, only run-time!
public Dictionary<string, ImgurInfo> runtimeImgurHistory = new Dictionary<string, ImgurInfo>();
public int Credits { get; set; }
/// <summary>
/// Supply values we can't put as defaults
/// </summary>
/// <param name="property">The property to return a default for</param>
/// <returns>object with the default value for the supplied property</returns>
public override object GetDefault(string property) =>
property switch
{
"ImgurUploadHistory" => new Dictionary<string, string>(),
@ -91,13 +97,14 @@ namespace Greenshot.Plugin.Imgur {
};
/// <summary>
/// A form for username/password
/// </summary>
/// <returns>bool true if OK was pressed, false if cancel</returns>
public bool ShowConfigDialog() {
SettingsForm settingsForm = new SettingsForm();
DialogResult result = settingsForm.ShowDialog();
return result == DialogResult.OK;
}
}
/// A form for username/password
/// </summary>
/// <returns>bool true if OK was pressed, false if cancel</returns>
public bool ShowConfigDialog()
{
SettingsForm settingsForm = new SettingsForm();
DialogResult result = settingsForm.ShowDialog();
return result == DialogResult.OK;
}
}
}

View file

@ -24,35 +24,42 @@ using System.Drawing;
using GreenshotPlugin.Core;
using GreenshotPlugin.Interfaces;
namespace Greenshot.Plugin.Imgur {
/// <summary>
/// Description of ImgurDestination.
/// </summary>
public class ImgurDestination : AbstractDestination {
private readonly ImgurPlugin _plugin;
namespace Greenshot.Plugin.Imgur
{
/// <summary>
/// Description of ImgurDestination.
/// </summary>
public class ImgurDestination : AbstractDestination
{
private readonly ImgurPlugin _plugin;
public ImgurDestination(ImgurPlugin plugin) {
_plugin = plugin;
}
public ImgurDestination(ImgurPlugin plugin)
{
_plugin = plugin;
}
public override string Designation => "Imgur";
public override string Designation => "Imgur";
public override string Description => Language.GetString("imgur", LangKey.upload_menu_item);
public override string Description => Language.GetString("imgur", LangKey.upload_menu_item);
public override Image DisplayIcon {
get {
ComponentResourceManager resources = new ComponentResourceManager(typeof(ImgurPlugin));
return (Image)resources.GetObject("Imgur");
}
}
public override Image DisplayIcon
{
get
{
ComponentResourceManager resources = new ComponentResourceManager(typeof(ImgurPlugin));
return (Image) resources.GetObject("Imgur");
}
}
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) {
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails)
{
ExportInformation exportInformation = new ExportInformation(Designation, Description)
{
ExportMade = _plugin.Upload(captureDetails, surface, out var uploadUrl), Uri = uploadUrl
ExportMade = _plugin.Upload(captureDetails, surface, out var uploadUrl),
Uri = uploadUrl
};
ProcessExport(exportInformation, surface);
return exportInformation;
}
}
return exportInformation;
}
}
}

View file

@ -25,194 +25,172 @@ using System.Xml;
namespace Greenshot.Plugin.Imgur
{
/// <summary>
/// Description of ImgurInfo.
/// </summary>
public class ImgurInfo : IDisposable
{
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ImgurInfo));
/// <summary>
/// Description of ImgurInfo.
/// </summary>
public class ImgurInfo : IDisposable
{
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ImgurInfo));
public string Hash
{
get;
set;
}
public string Hash { get; set; }
private string _deleteHash;
public string DeleteHash
{
get { return _deleteHash; }
set
{
_deleteHash = value;
DeletePage = "https://imgur.com/delete/" + value;
}
}
private string _deleteHash;
public string Title
{
get;
set;
}
public string DeleteHash
{
get { return _deleteHash; }
set
{
_deleteHash = value;
DeletePage = "https://imgur.com/delete/" + value;
}
}
public string ImageType
{
get;
set;
}
public string Title { get; set; }
public DateTime Timestamp
{
get;
set;
}
public string ImageType { get; set; }
public string Original
{
get;
set;
}
public DateTime Timestamp { get; set; }
public string Page
{
get;
set;
}
public string Original { get; set; }
public string SmallSquare
{
get;
set;
}
public string Page { get; set; }
public string LargeThumbnail
{
get;
set;
}
public string SmallSquare { get; set; }
public string DeletePage
{
get;
set;
}
public string LargeThumbnail { get; set; }
private Image _image;
public Image Image
{
get { return _image; }
set
{
_image?.Dispose();
_image = value;
}
}
public string DeletePage { get; set; }
/// <summary>
/// The public accessible Dispose
/// Will call the GarbageCollector to SuppressFinalize, preventing being cleaned twice
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private Image _image;
/// <summary>
/// This Dispose is called from the Dispose and the Destructor.
/// When disposing==true all non-managed resources should be freed too!
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_image?.Dispose();
}
_image = null;
}
public static ImgurInfo ParseResponse(string response)
{
Log.Debug(response);
// This is actually a hack for BUG-1695
// The problem is the (C) sign, we send it HTML encoded "&reg;" to Imgur and get it HTML encoded in the XML back
// Added all the encodings I found quickly, I guess these are not all... but it should fix the issue for now.
response = response.Replace("&cent;", "&#162;");
response = response.Replace("&pound;", "&#163;");
response = response.Replace("&yen;", "&#165;");
response = response.Replace("&euro;", "&#8364;");
response = response.Replace("&copy;", "&#169;");
response = response.Replace("&reg;", "&#174;");
public Image Image
{
get { return _image; }
set
{
_image?.Dispose();
_image = value;
}
}
ImgurInfo imgurInfo = new ImgurInfo();
try
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(response);
XmlNodeList nodes = doc.GetElementsByTagName("id");
if (nodes.Count > 0)
{
imgurInfo.Hash = nodes.Item(0)?.InnerText;
}
nodes = doc.GetElementsByTagName("hash");
if (nodes.Count > 0)
{
imgurInfo.Hash = nodes.Item(0)?.InnerText;
}
nodes = doc.GetElementsByTagName("deletehash");
if (nodes.Count > 0)
{
imgurInfo.DeleteHash = nodes.Item(0)?.InnerText;
}
nodes = doc.GetElementsByTagName("type");
if (nodes.Count > 0)
{
imgurInfo.ImageType = nodes.Item(0)?.InnerText;
}
nodes = doc.GetElementsByTagName("title");
if (nodes.Count > 0)
{
imgurInfo.Title = nodes.Item(0)?.InnerText;
}
nodes = doc.GetElementsByTagName("datetime");
if (nodes.Count > 0)
{
// Version 3 has seconds since Epoch
/// <summary>
/// The public accessible Dispose
/// Will call the GarbageCollector to SuppressFinalize, preventing being cleaned twice
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// This Dispose is called from the Dispose and the Destructor.
/// When disposing==true all non-managed resources should be freed too!
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_image?.Dispose();
}
_image = null;
}
public static ImgurInfo ParseResponse(string response)
{
Log.Debug(response);
// This is actually a hack for BUG-1695
// The problem is the (C) sign, we send it HTML encoded "&reg;" to Imgur and get it HTML encoded in the XML back
// Added all the encodings I found quickly, I guess these are not all... but it should fix the issue for now.
response = response.Replace("&cent;", "&#162;");
response = response.Replace("&pound;", "&#163;");
response = response.Replace("&yen;", "&#165;");
response = response.Replace("&euro;", "&#8364;");
response = response.Replace("&copy;", "&#169;");
response = response.Replace("&reg;", "&#174;");
ImgurInfo imgurInfo = new ImgurInfo();
try
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(response);
XmlNodeList nodes = doc.GetElementsByTagName("id");
if (nodes.Count > 0)
{
imgurInfo.Hash = nodes.Item(0)?.InnerText;
}
nodes = doc.GetElementsByTagName("hash");
if (nodes.Count > 0)
{
imgurInfo.Hash = nodes.Item(0)?.InnerText;
}
nodes = doc.GetElementsByTagName("deletehash");
if (nodes.Count > 0)
{
imgurInfo.DeleteHash = nodes.Item(0)?.InnerText;
}
nodes = doc.GetElementsByTagName("type");
if (nodes.Count > 0)
{
imgurInfo.ImageType = nodes.Item(0)?.InnerText;
}
nodes = doc.GetElementsByTagName("title");
if (nodes.Count > 0)
{
imgurInfo.Title = nodes.Item(0)?.InnerText;
}
nodes = doc.GetElementsByTagName("datetime");
if (nodes.Count > 0)
{
// Version 3 has seconds since Epoch
if (double.TryParse(nodes.Item(0)?.InnerText, out var secondsSince))
{
var epoch = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
imgurInfo.Timestamp = epoch.AddSeconds(secondsSince).DateTime;
}
}
nodes = doc.GetElementsByTagName("original");
if (nodes.Count > 0)
{
imgurInfo.Original = nodes.Item(0)?.InnerText.Replace("http:", "https:");
}
// Version 3 API only has Link
nodes = doc.GetElementsByTagName("link");
if (nodes.Count > 0)
{
imgurInfo.Original = nodes.Item(0)?.InnerText.Replace("http:", "https:");
}
nodes = doc.GetElementsByTagName("imgur_page");
if (nodes.Count > 0)
{
imgurInfo.Page = nodes.Item(0)?.InnerText.Replace("http:", "https:");
}
else
{
// Version 3 doesn't have a page link in the response
imgurInfo.Page = $"https://imgur.com/{imgurInfo.Hash}";
}
nodes = doc.GetElementsByTagName("small_square");
imgurInfo.SmallSquare = nodes.Count > 0 ? nodes.Item(0)?.InnerText : $"http://i.imgur.com/{imgurInfo.Hash}s.png";
}
catch (Exception e)
{
Log.ErrorFormat("Could not parse Imgur response due to error {0}, response was: {1}", e.Message, response);
}
return imgurInfo;
}
}
{
var epoch = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
imgurInfo.Timestamp = epoch.AddSeconds(secondsSince).DateTime;
}
}
nodes = doc.GetElementsByTagName("original");
if (nodes.Count > 0)
{
imgurInfo.Original = nodes.Item(0)?.InnerText.Replace("http:", "https:");
}
// Version 3 API only has Link
nodes = doc.GetElementsByTagName("link");
if (nodes.Count > 0)
{
imgurInfo.Original = nodes.Item(0)?.InnerText.Replace("http:", "https:");
}
nodes = doc.GetElementsByTagName("imgur_page");
if (nodes.Count > 0)
{
imgurInfo.Page = nodes.Item(0)?.InnerText.Replace("http:", "https:");
}
else
{
// Version 3 doesn't have a page link in the response
imgurInfo.Page = $"https://imgur.com/{imgurInfo.Hash}";
}
nodes = doc.GetElementsByTagName("small_square");
imgurInfo.SmallSquare = nodes.Count > 0 ? nodes.Item(0)?.InnerText : $"http://i.imgur.com/{imgurInfo.Hash}s.png";
}
catch (Exception e)
{
Log.ErrorFormat("Could not parse Imgur response due to error {0}, response was: {1}", e.Message, response);
}
return imgurInfo;
}
}
}

View file

@ -32,186 +32,216 @@ using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
namespace Greenshot.Plugin.Imgur {
/// <summary>
/// This is the ImgurPlugin code
/// </summary>
namespace Greenshot.Plugin.Imgur
{
/// <summary>
/// This is the ImgurPlugin code
/// </summary>
[Plugin("Imgur", true)]
public class ImgurPlugin : IGreenshotPlugin {
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ImgurPlugin));
private static ImgurConfiguration _config;
private ComponentResourceManager _resources;
private ToolStripMenuItem _historyMenuItem;
private ToolStripMenuItem _itemPlugInConfig;
public class ImgurPlugin : IGreenshotPlugin
{
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ImgurPlugin));
private static ImgurConfiguration _config;
private ComponentResourceManager _resources;
private ToolStripMenuItem _historyMenuItem;
private ToolStripMenuItem _itemPlugInConfig;
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing) {
if (disposing) {
if (_historyMenuItem != null) {
_historyMenuItem.Dispose();
_historyMenuItem = null;
}
if (_itemPlugInConfig != null) {
_itemPlugInConfig.Dispose();
_itemPlugInConfig = null;
}
}
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (_historyMenuItem != null)
{
_historyMenuItem.Dispose();
_historyMenuItem = null;
}
private IEnumerable<IDestination> Destinations() {
yield return new ImgurDestination(this);
}
if (_itemPlugInConfig != null)
{
_itemPlugInConfig.Dispose();
_itemPlugInConfig = null;
}
}
}
/// <summary>
/// Implementation of the IGreenshotPlugin.Initialize
/// </summary>
/// <returns>true if plugin is initialized, false if not (doesn't show)</returns>
public bool Initialize() {
// Get configuration
_config = IniConfig.GetIniSection<ImgurConfiguration>();
_resources = new ComponentResourceManager(typeof(ImgurPlugin));
private IEnumerable<IDestination> Destinations()
{
yield return new ImgurDestination(this);
}
ToolStripMenuItem itemPlugInRoot = new ToolStripMenuItem("Imgur")
{
Image = (Image) _resources.GetObject("Imgur")
};
/// <summary>
/// Implementation of the IGreenshotPlugin.Initialize
/// </summary>
/// <returns>true if plugin is initialized, false if not (doesn't show)</returns>
public bool Initialize()
{
// Get configuration
_config = IniConfig.GetIniSection<ImgurConfiguration>();
_resources = new ComponentResourceManager(typeof(ImgurPlugin));
ToolStripMenuItem itemPlugInRoot = new ToolStripMenuItem("Imgur")
{
Image = (Image) _resources.GetObject("Imgur")
};
// Provide the IDestination
SimpleServiceProvider.Current.AddService(Destinations());
_historyMenuItem = new ToolStripMenuItem(Language.GetString("imgur", LangKey.history));
_historyMenuItem.Click += delegate {
ImgurHistory.ShowHistory();
};
itemPlugInRoot.DropDownItems.Add(_historyMenuItem);
_historyMenuItem.Click += delegate { ImgurHistory.ShowHistory(); };
itemPlugInRoot.DropDownItems.Add(_historyMenuItem);
_itemPlugInConfig = new ToolStripMenuItem(Language.GetString("imgur", LangKey.configure));
_itemPlugInConfig.Click += delegate {
_config.ShowConfigDialog();
};
itemPlugInRoot.DropDownItems.Add(_itemPlugInConfig);
_itemPlugInConfig.Click += delegate { _config.ShowConfigDialog(); };
itemPlugInRoot.DropDownItems.Add(_itemPlugInConfig);
PluginUtils.AddToContextMenu(itemPlugInRoot);
Language.LanguageChanged += OnLanguageChanged;
PluginUtils.AddToContextMenu(itemPlugInRoot);
Language.LanguageChanged += OnLanguageChanged;
// Enable history if there are items available
UpdateHistoryMenuItem();
return true;
}
// Enable history if there are items available
UpdateHistoryMenuItem();
return true;
}
public void OnLanguageChanged(object sender, EventArgs e) {
if (_itemPlugInConfig != null) {
_itemPlugInConfig.Text = Language.GetString("imgur", LangKey.configure);
}
if (_historyMenuItem != null) {
_historyMenuItem.Text = Language.GetString("imgur", LangKey.history);
}
}
public void OnLanguageChanged(object sender, EventArgs e)
{
if (_itemPlugInConfig != null)
{
_itemPlugInConfig.Text = Language.GetString("imgur", LangKey.configure);
}
private void UpdateHistoryMenuItem() {
if (_historyMenuItem == null)
{
return;
}
try
if (_historyMenuItem != null)
{
_historyMenuItem.Text = Language.GetString("imgur", LangKey.history);
}
}
private void UpdateHistoryMenuItem()
{
if (_historyMenuItem == null)
{
return;
}
try
{
var form = SimpleServiceProvider.Current.GetInstance<Form>();
form.BeginInvoke((MethodInvoker)delegate
form.BeginInvoke((MethodInvoker) delegate
{
var historyMenuItem = _historyMenuItem;
if (historyMenuItem == null)
if (historyMenuItem == null)
{
return;
}
if (_config?.ImgurUploadHistory != null && _config.ImgurUploadHistory.Count > 0) {
if (_config?.ImgurUploadHistory != null && _config.ImgurUploadHistory.Count > 0)
{
historyMenuItem.Enabled = true;
} else {
}
else
{
historyMenuItem.Enabled = false;
}
});
} catch (Exception ex) {
Log.Error("Error loading history", ex);
}
}
}
});
}
catch (Exception ex)
{
Log.Error("Error loading history", ex);
}
}
public virtual void Shutdown() {
Log.Debug("Imgur Plugin shutdown.");
Language.LanguageChanged -= OnLanguageChanged;
}
public virtual void Shutdown()
{
Log.Debug("Imgur Plugin shutdown.");
Language.LanguageChanged -= OnLanguageChanged;
}
/// <summary>
/// Implementation of the IPlugin.Configure
/// </summary>
public virtual void Configure() {
_config.ShowConfigDialog();
}
/// <summary>
/// Implementation of the IPlugin.Configure
/// </summary>
public virtual void Configure()
{
_config.ShowConfigDialog();
}
/// <summary>
/// Upload the capture to imgur
/// </summary>
/// <param name="captureDetails">ICaptureDetails</param>
/// <param name="surfaceToUpload">ISurface</param>
/// <param name="uploadUrl">out string for the url</param>
/// <returns>true if the upload succeeded</returns>
public bool Upload(ICaptureDetails captureDetails, ISurface surfaceToUpload, out string uploadUrl) {
SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(_config.UploadFormat, _config.UploadJpegQuality, _config.UploadReduceColors);
try {
string filename = Path.GetFileName(FilenameHelper.GetFilenameFromPattern(_config.FilenamePattern, _config.UploadFormat, captureDetails));
ImgurInfo imgurInfo = null;
/// <summary>
/// Upload the capture to imgur
/// </summary>
/// <param name="captureDetails">ICaptureDetails</param>
/// <param name="surfaceToUpload">ISurface</param>
/// <param name="uploadUrl">out string for the url</param>
/// <returns>true if the upload succeeded</returns>
public bool Upload(ICaptureDetails captureDetails, ISurface surfaceToUpload, out string uploadUrl)
{
SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(_config.UploadFormat, _config.UploadJpegQuality, _config.UploadReduceColors);
try
{
string filename = Path.GetFileName(FilenameHelper.GetFilenameFromPattern(_config.FilenamePattern, _config.UploadFormat, captureDetails));
ImgurInfo imgurInfo = null;
// Run upload in the background
new PleaseWaitForm().ShowAndWait("Imgur plug-in", Language.GetString("imgur", LangKey.communication_wait),
delegate
{
imgurInfo = ImgurUtils.UploadToImgur(surfaceToUpload, outputSettings, captureDetails.Title, filename);
if (imgurInfo != null && _config.AnonymousAccess) {
Log.InfoFormat("Storing imgur upload for hash {0} and delete hash {1}", imgurInfo.Hash, imgurInfo.DeleteHash);
_config.ImgurUploadHistory.Add(imgurInfo.Hash, imgurInfo.DeleteHash);
_config.runtimeImgurHistory.Add(imgurInfo.Hash, imgurInfo);
UpdateHistoryMenuItem();
}
}
);
// Run upload in the background
new PleaseWaitForm().ShowAndWait("Imgur plug-in", Language.GetString("imgur", LangKey.communication_wait),
delegate
{
imgurInfo = ImgurUtils.UploadToImgur(surfaceToUpload, outputSettings, captureDetails.Title, filename);
if (imgurInfo != null && _config.AnonymousAccess)
{
Log.InfoFormat("Storing imgur upload for hash {0} and delete hash {1}", imgurInfo.Hash, imgurInfo.DeleteHash);
_config.ImgurUploadHistory.Add(imgurInfo.Hash, imgurInfo.DeleteHash);
_config.runtimeImgurHistory.Add(imgurInfo.Hash, imgurInfo);
UpdateHistoryMenuItem();
}
}
);
if (imgurInfo != null) {
// TODO: Optimize a second call for export
using (Image tmpImage = surfaceToUpload.GetImageForExport()) {
imgurInfo.Image = ImageHelper.CreateThumbnail(tmpImage, 90, 90);
}
IniConfig.Save();
if (imgurInfo != null)
{
// TODO: Optimize a second call for export
using (Image tmpImage = surfaceToUpload.GetImageForExport())
{
imgurInfo.Image = ImageHelper.CreateThumbnail(tmpImage, 90, 90);
}
if (_config.UsePageLink)
{
uploadUrl = imgurInfo.Page;
}
else
{
uploadUrl = imgurInfo.Original;
}
if (!string.IsNullOrEmpty(uploadUrl) && _config.CopyLinkToClipboard)
{
try
{
ClipboardHelper.SetClipboardData(uploadUrl);
IniConfig.Save();
}
catch (Exception ex)
{
Log.Error("Can't write to clipboard: ", ex);
uploadUrl = null;
}
}
return true;
}
} catch (Exception e) {
Log.Error("Error uploading.", e);
MessageBox.Show(Language.GetString("imgur", LangKey.upload_failure) + " " + e.Message);
}
uploadUrl = null;
return false;
}
}
if (_config.UsePageLink)
{
uploadUrl = imgurInfo.Page;
}
else
{
uploadUrl = imgurInfo.Original;
}
if (!string.IsNullOrEmpty(uploadUrl) && _config.CopyLinkToClipboard)
{
try
{
ClipboardHelper.SetClipboardData(uploadUrl);
}
catch (Exception ex)
{
Log.Error("Can't write to clipboard: ", ex);
uploadUrl = null;
}
}
return true;
}
}
catch (Exception e)
{
Log.Error("Error uploading.", e);
MessageBox.Show(Language.GetString("imgur", LangKey.upload_failure) + " " + e.Message);
}
uploadUrl = null;
return false;
}
}
}

View file

@ -30,127 +30,157 @@ using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
namespace Greenshot.Plugin.Imgur {
/// <summary>
/// A collection of Imgur helper methods
/// </summary>
public static class ImgurUtils {
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ImgurUtils));
private const string SmallUrlPattern = "http://i.imgur.com/{0}s.jpg";
private static readonly ImgurConfiguration Config = IniConfig.GetIniSection<ImgurConfiguration>();
namespace Greenshot.Plugin.Imgur
{
/// <summary>
/// A collection of Imgur helper methods
/// </summary>
public static class ImgurUtils
{
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ImgurUtils));
private const string SmallUrlPattern = "http://i.imgur.com/{0}s.jpg";
private static readonly ImgurConfiguration Config = IniConfig.GetIniSection<ImgurConfiguration>();
/// <summary>
/// Check if we need to load the history
/// </summary>
/// <returns></returns>
public static bool IsHistoryLoadingNeeded()
{
Log.InfoFormat("Checking if imgur cache loading needed, configuration has {0} imgur hashes, loaded are {1} hashes.", Config.ImgurUploadHistory.Count, Config.runtimeImgurHistory.Count);
return Config.runtimeImgurHistory.Count != Config.ImgurUploadHistory.Count;
}
/// Check if we need to load the history
/// </summary>
/// <returns></returns>
public static bool IsHistoryLoadingNeeded()
{
Log.InfoFormat("Checking if imgur cache loading needed, configuration has {0} imgur hashes, loaded are {1} hashes.", Config.ImgurUploadHistory.Count,
Config.runtimeImgurHistory.Count);
return Config.runtimeImgurHistory.Count != Config.ImgurUploadHistory.Count;
}
/// <summary>
/// Load the complete history of the imgur uploads, with the corresponding information
/// </summary>
public static void LoadHistory() {
if (!IsHistoryLoadingNeeded())
{
return;
}
/// <summary>
/// Load the complete history of the imgur uploads, with the corresponding information
/// </summary>
public static void LoadHistory()
{
if (!IsHistoryLoadingNeeded())
{
return;
}
bool saveNeeded = false;
bool saveNeeded = false;
// Load the ImUr history
foreach (string hash in Config.ImgurUploadHistory.Keys.ToList()) {
if (Config.runtimeImgurHistory.ContainsKey(hash)) {
// Already loaded
continue;
}
// Load the ImUr history
foreach (string hash in Config.ImgurUploadHistory.Keys.ToList())
{
if (Config.runtimeImgurHistory.ContainsKey(hash))
{
// Already loaded
continue;
}
try
{
var deleteHash = Config.ImgurUploadHistory[hash];
ImgurInfo imgurInfo = RetrieveImgurInfo(hash, deleteHash);
if (imgurInfo != null) {
RetrieveImgurThumbnail(imgurInfo);
Config.runtimeImgurHistory[hash] = imgurInfo;
} else {
Log.InfoFormat("Deleting unknown ImgUr {0} from config, delete hash was {1}.", hash, deleteHash);
Config.ImgurUploadHistory.Remove(hash);
Config.runtimeImgurHistory.Remove(hash);
saveNeeded = true;
}
} catch (WebException wE) {
bool redirected = false;
if (wE.Status == WebExceptionStatus.ProtocolError) {
HttpWebResponse response = (HttpWebResponse)wE.Response;
try
{
var deleteHash = Config.ImgurUploadHistory[hash];
ImgurInfo imgurInfo = RetrieveImgurInfo(hash, deleteHash);
if (imgurInfo != null)
{
RetrieveImgurThumbnail(imgurInfo);
Config.runtimeImgurHistory[hash] = imgurInfo;
}
else
{
Log.InfoFormat("Deleting unknown ImgUr {0} from config, delete hash was {1}.", hash, deleteHash);
Config.ImgurUploadHistory.Remove(hash);
Config.runtimeImgurHistory.Remove(hash);
saveNeeded = true;
}
}
catch (WebException wE)
{
bool redirected = false;
if (wE.Status == WebExceptionStatus.ProtocolError)
{
HttpWebResponse response = (HttpWebResponse) wE.Response;
if (response.StatusCode == HttpStatusCode.Forbidden)
{
Log.Error("Imgur loading forbidden", wE);
break;
}
// Image no longer available?
if (response.StatusCode == HttpStatusCode.Redirect) {
Log.InfoFormat("ImgUr image for hash {0} is no longer available, removing it from the history", hash);
Config.ImgurUploadHistory.Remove(hash);
Config.runtimeImgurHistory.Remove(hash);
redirected = true;
}
}
if (!redirected) {
Log.Error("Problem loading ImgUr history for hash " + hash, wE);
}
} catch (Exception e) {
Log.Error("Problem loading ImgUr history for hash " + hash, e);
}
}
if (saveNeeded) {
// Save needed changes
IniConfig.Save();
}
}
if (response.StatusCode == HttpStatusCode.Forbidden)
{
Log.Error("Imgur loading forbidden", wE);
break;
}
/// <summary>
/// Use this to make sure Imgur knows from where the upload comes.
/// </summary>
/// <param name="webRequest"></param>
private static void SetClientId(HttpWebRequest webRequest) {
webRequest.Headers.Add("Authorization", "Client-ID " + ImgurCredentials.CONSUMER_KEY);
}
// Image no longer available?
if (response.StatusCode == HttpStatusCode.Redirect)
{
Log.InfoFormat("ImgUr image for hash {0} is no longer available, removing it from the history", hash);
Config.ImgurUploadHistory.Remove(hash);
Config.runtimeImgurHistory.Remove(hash);
redirected = true;
}
}
/// <summary>
/// Do the actual upload to Imgur
/// For more details on the available parameters, see: http://api.imgur.com/resources_anon
/// </summary>
/// <param name="surfaceToUpload">ISurface to upload</param>
/// <param name="outputSettings">OutputSettings for the image file format</param>
/// <param name="title">Title</param>
/// <param name="filename">Filename</param>
/// <returns>ImgurInfo with details</returns>
public static ImgurInfo UploadToImgur(ISurface surfaceToUpload, SurfaceOutputSettings outputSettings, string title, string filename) {
IDictionary<string, object> otherParameters = new Dictionary<string, object>();
// add title
if (title != null && Config.AddTitle) {
otherParameters["title"]= title;
}
// add filename
if (filename != null && Config.AddFilename) {
otherParameters["name"] = filename;
}
string responseString = null;
if (Config.AnonymousAccess) {
// add key, we only use the other parameters for the AnonymousAccess
//otherParameters.Add("key", IMGUR_ANONYMOUS_API_KEY);
HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(Config.ImgurApi3Url + "/upload.xml?" + NetworkHelper.GenerateQueryParameters(otherParameters), HTTPMethod.POST);
webRequest.ContentType = "image/" + outputSettings.Format;
webRequest.ServicePoint.Expect100Continue = false;
if (!redirected)
{
Log.Error("Problem loading ImgUr history for hash " + hash, wE);
}
}
catch (Exception e)
{
Log.Error("Problem loading ImgUr history for hash " + hash, e);
}
}
SetClientId(webRequest);
try {
using (var requestStream = webRequest.GetRequestStream()) {
ImageOutput.SaveToStream(surfaceToUpload, requestStream, outputSettings);
}
if (saveNeeded)
{
// Save needed changes
IniConfig.Save();
}
}
/// <summary>
/// Use this to make sure Imgur knows from where the upload comes.
/// </summary>
/// <param name="webRequest"></param>
private static void SetClientId(HttpWebRequest webRequest)
{
webRequest.Headers.Add("Authorization", "Client-ID " + ImgurCredentials.CONSUMER_KEY);
}
/// <summary>
/// Do the actual upload to Imgur
/// For more details on the available parameters, see: http://api.imgur.com/resources_anon
/// </summary>
/// <param name="surfaceToUpload">ISurface to upload</param>
/// <param name="outputSettings">OutputSettings for the image file format</param>
/// <param name="title">Title</param>
/// <param name="filename">Filename</param>
/// <returns>ImgurInfo with details</returns>
public static ImgurInfo UploadToImgur(ISurface surfaceToUpload, SurfaceOutputSettings outputSettings, string title, string filename)
{
IDictionary<string, object> otherParameters = new Dictionary<string, object>();
// add title
if (title != null && Config.AddTitle)
{
otherParameters["title"] = title;
}
// add filename
if (filename != null && Config.AddFilename)
{
otherParameters["name"] = filename;
}
string responseString = null;
if (Config.AnonymousAccess)
{
// add key, we only use the other parameters for the AnonymousAccess
//otherParameters.Add("key", IMGUR_ANONYMOUS_API_KEY);
HttpWebRequest webRequest =
NetworkHelper.CreateWebRequest(Config.ImgurApi3Url + "/upload.xml?" + NetworkHelper.GenerateQueryParameters(otherParameters), HTTPMethod.POST);
webRequest.ContentType = "image/" + outputSettings.Format;
webRequest.ServicePoint.Expect100Continue = false;
SetClientId(webRequest);
try
{
using (var requestStream = webRequest.GetRequestStream())
{
ImageOutput.SaveToStream(surfaceToUpload, requestStream, outputSettings);
}
using WebResponse response = webRequest.GetResponse();
LogRateLimitInfo(response);
@ -160,68 +190,76 @@ namespace Greenshot.Plugin.Imgur {
using StreamReader reader = new StreamReader(responseStream, true);
responseString = reader.ReadToEnd();
}
} catch (Exception ex) {
Log.Error("Upload to imgur gave an exception: ", ex);
throw;
}
} else {
}
catch (Exception ex)
{
Log.Error("Upload to imgur gave an exception: ", ex);
throw;
}
}
else
{
var oauth2Settings = new OAuth2Settings
{
AuthUrlPattern = "https://api.imgur.com/oauth2/authorize?response_type=token&client_id={ClientId}&state={State}",
TokenUrl = "https://api.imgur.com/oauth2/token",
RedirectUrl = "https://getgreenshot.org/authorize/imgur",
CloudServiceName = "Imgur",
ClientId = ImgurCredentials.CONSUMER_KEY,
ClientSecret = ImgurCredentials.CONSUMER_SECRET,
AuthorizeMode = OAuth2AuthorizeMode.JsonReceiver,
RefreshToken = Config.RefreshToken,
AccessToken = Config.AccessToken,
AccessTokenExpires = Config.AccessTokenExpires
};
var oauth2Settings = new OAuth2Settings
{
AuthUrlPattern = "https://api.imgur.com/oauth2/authorize?response_type=token&client_id={ClientId}&state={State}",
TokenUrl = "https://api.imgur.com/oauth2/token",
RedirectUrl = "https://getgreenshot.org/authorize/imgur",
CloudServiceName = "Imgur",
ClientId = ImgurCredentials.CONSUMER_KEY,
ClientSecret = ImgurCredentials.CONSUMER_SECRET,
AuthorizeMode = OAuth2AuthorizeMode.JsonReceiver,
RefreshToken = Config.RefreshToken,
AccessToken = Config.AccessToken,
AccessTokenExpires = Config.AccessTokenExpires
};
// Copy the settings from the config, which is kept in memory and on the disk
// Copy the settings from the config, which is kept in memory and on the disk
try
{
var webRequest = OAuth2Helper.CreateOAuth2WebRequest(HTTPMethod.POST, Config.ImgurApi3Url + "/upload.xml", oauth2Settings);
otherParameters["image"] = new SurfaceContainer(surfaceToUpload, outputSettings, filename);
try
{
var webRequest = OAuth2Helper.CreateOAuth2WebRequest(HTTPMethod.POST, Config.ImgurApi3Url + "/upload.xml", oauth2Settings);
otherParameters["image"] = new SurfaceContainer(surfaceToUpload, outputSettings, filename);
NetworkHelper.WriteMultipartFormData(webRequest, otherParameters);
NetworkHelper.WriteMultipartFormData(webRequest, otherParameters);
responseString = NetworkHelper.GetResponseAsString(webRequest);
}
finally
{
// Copy the settings back to the config, so they are stored.
Config.RefreshToken = oauth2Settings.RefreshToken;
Config.AccessToken = oauth2Settings.AccessToken;
Config.AccessTokenExpires = oauth2Settings.AccessTokenExpires;
Config.IsDirty = true;
IniConfig.Save();
}
}
responseString = NetworkHelper.GetResponseAsString(webRequest);
}
finally
{
// Copy the settings back to the config, so they are stored.
Config.RefreshToken = oauth2Settings.RefreshToken;
Config.AccessToken = oauth2Settings.AccessToken;
Config.AccessTokenExpires = oauth2Settings.AccessTokenExpires;
Config.IsDirty = true;
IniConfig.Save();
}
}
if (string.IsNullOrEmpty(responseString))
{
return null;
}
return ImgurInfo.ParseResponse(responseString);
}
if (string.IsNullOrEmpty(responseString))
{
return null;
}
/// <summary>
/// Retrieve the thumbnail of an imgur image
/// </summary>
/// <param name="imgurInfo"></param>
public static void RetrieveImgurThumbnail(ImgurInfo imgurInfo) {
if (imgurInfo.SmallSquare == null) {
Log.Warn("Imgur URL was null, not retrieving thumbnail.");
return;
}
Log.InfoFormat("Retrieving Imgur image for {0} with url {1}", imgurInfo.Hash, imgurInfo.SmallSquare);
HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(string.Format(SmallUrlPattern, imgurInfo.Hash), HTTPMethod.GET);
webRequest.ServicePoint.Expect100Continue = false;
// Not for getting the thumbnail, in anonymous mode
//SetClientId(webRequest);
return ImgurInfo.ParseResponse(responseString);
}
/// <summary>
/// Retrieve the thumbnail of an imgur image
/// </summary>
/// <param name="imgurInfo"></param>
public static void RetrieveImgurThumbnail(ImgurInfo imgurInfo)
{
if (imgurInfo.SmallSquare == null)
{
Log.Warn("Imgur URL was null, not retrieving thumbnail.");
return;
}
Log.InfoFormat("Retrieving Imgur image for {0} with url {1}", imgurInfo.Hash, imgurInfo.SmallSquare);
HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(string.Format(SmallUrlPattern, imgurInfo.Hash), HTTPMethod.GET);
webRequest.ServicePoint.Expect100Continue = false;
// Not for getting the thumbnail, in anonymous mode
//SetClientId(webRequest);
using WebResponse response = webRequest.GetResponse();
LogRateLimitInfo(response);
Stream responseStream = response.GetResponseStream();
@ -231,20 +269,21 @@ namespace Greenshot.Plugin.Imgur {
}
}
/// <summary>
/// Retrieve information on an imgur image
/// </summary>
/// <param name="hash"></param>
/// <param name="deleteHash"></param>
/// <returns>ImgurInfo</returns>
public static ImgurInfo RetrieveImgurInfo(string hash, string deleteHash) {
string url = Config.ImgurApi3Url + "/image/" + hash + ".xml";
Log.InfoFormat("Retrieving Imgur info for {0} with url {1}", hash, url);
HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(url, HTTPMethod.GET);
webRequest.ServicePoint.Expect100Continue = false;
SetClientId(webRequest);
string responseString = null;
try
/// <summary>
/// Retrieve information on an imgur image
/// </summary>
/// <param name="hash"></param>
/// <param name="deleteHash"></param>
/// <returns>ImgurInfo</returns>
public static ImgurInfo RetrieveImgurInfo(string hash, string deleteHash)
{
string url = Config.ImgurApi3Url + "/image/" + hash + ".xml";
Log.InfoFormat("Retrieving Imgur info for {0} with url {1}", hash, url);
HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(url, HTTPMethod.GET);
webRequest.ServicePoint.Expect100Continue = false;
SetClientId(webRequest);
string responseString = null;
try
{
using WebResponse response = webRequest.GetResponse();
LogRateLimitInfo(response);
@ -254,95 +293,118 @@ namespace Greenshot.Plugin.Imgur {
using StreamReader reader = new StreamReader(responseStream, true);
responseString = reader.ReadToEnd();
}
} catch (WebException wE) {
if (wE.Status == WebExceptionStatus.ProtocolError) {
if (((HttpWebResponse)wE.Response).StatusCode == HttpStatusCode.NotFound) {
return null;
}
}
throw;
}
ImgurInfo imgurInfo = null;
if (responseString != null)
{
Log.Debug(responseString);
imgurInfo = ImgurInfo.ParseResponse(responseString);
imgurInfo.DeleteHash = deleteHash;
}
return imgurInfo;
}
}
catch (WebException wE)
{
if (wE.Status == WebExceptionStatus.ProtocolError)
{
if (((HttpWebResponse) wE.Response).StatusCode == HttpStatusCode.NotFound)
{
return null;
}
}
/// <summary>
/// Delete an imgur image, this is done by specifying the delete hash
/// </summary>
/// <param name="imgurInfo"></param>
public static void DeleteImgurImage(ImgurInfo imgurInfo) {
Log.InfoFormat("Deleting Imgur image for {0}", imgurInfo.DeleteHash);
throw;
}
try {
string url = Config.ImgurApi3Url + "/image/" + imgurInfo.DeleteHash + ".xml";
HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(url, HTTPMethod.DELETE);
webRequest.ServicePoint.Expect100Continue = false;
SetClientId(webRequest);
string responseString = null;
using (WebResponse response = webRequest.GetResponse()) {
LogRateLimitInfo(response);
var responseStream = response.GetResponseStream();
if (responseStream != null)
ImgurInfo imgurInfo = null;
if (responseString != null)
{
Log.Debug(responseString);
imgurInfo = ImgurInfo.ParseResponse(responseString);
imgurInfo.DeleteHash = deleteHash;
}
return imgurInfo;
}
/// <summary>
/// Delete an imgur image, this is done by specifying the delete hash
/// </summary>
/// <param name="imgurInfo"></param>
public static void DeleteImgurImage(ImgurInfo imgurInfo)
{
Log.InfoFormat("Deleting Imgur image for {0}", imgurInfo.DeleteHash);
try
{
string url = Config.ImgurApi3Url + "/image/" + imgurInfo.DeleteHash + ".xml";
HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(url, HTTPMethod.DELETE);
webRequest.ServicePoint.Expect100Continue = false;
SetClientId(webRequest);
string responseString = null;
using (WebResponse response = webRequest.GetResponse())
{
LogRateLimitInfo(response);
var responseStream = response.GetResponseStream();
if (responseStream != null)
{
using StreamReader reader = new StreamReader(responseStream, true);
responseString = reader.ReadToEnd();
}
}
Log.InfoFormat("Delete result: {0}", responseString);
} catch (WebException wE) {
// Allow "Bad request" this means we already deleted it
if (wE.Status == WebExceptionStatus.ProtocolError) {
if (((HttpWebResponse)wE.Response).StatusCode != HttpStatusCode.BadRequest) {
throw ;
}
}
}
// Make sure we remove it from the history, if no error occurred
Config.runtimeImgurHistory.Remove(imgurInfo.Hash);
Config.ImgurUploadHistory.Remove(imgurInfo.Hash);
imgurInfo.Image = null;
}
}
/// <summary>
/// Helper for logging
/// </summary>
/// <param name="nameValues"></param>
/// <param name="key"></param>
private static void LogHeader(IDictionary<string, string> nameValues, string key) {
if (nameValues.ContainsKey(key)) {
Log.InfoFormat("{0}={1}", key, nameValues[key]);
}
}
Log.InfoFormat("Delete result: {0}", responseString);
}
catch (WebException wE)
{
// Allow "Bad request" this means we already deleted it
if (wE.Status == WebExceptionStatus.ProtocolError)
{
if (((HttpWebResponse) wE.Response).StatusCode != HttpStatusCode.BadRequest)
{
throw;
}
}
}
/// <summary>
/// Log the current rate-limit information
/// </summary>
/// <param name="response"></param>
private static void LogRateLimitInfo(WebResponse response) {
IDictionary<string, string> nameValues = new Dictionary<string, string>();
foreach (string key in response.Headers.AllKeys) {
if (!nameValues.ContainsKey(key)) {
nameValues.Add(key, response.Headers[key]);
}
}
LogHeader(nameValues, "X-RateLimit-Limit");
LogHeader(nameValues, "X-RateLimit-Remaining");
LogHeader(nameValues, "X-RateLimit-UserLimit");
LogHeader(nameValues, "X-RateLimit-UserRemaining");
LogHeader(nameValues, "X-RateLimit-UserReset");
LogHeader(nameValues, "X-RateLimit-ClientLimit");
LogHeader(nameValues, "X-RateLimit-ClientRemaining");
// Make sure we remove it from the history, if no error occurred
Config.runtimeImgurHistory.Remove(imgurInfo.Hash);
Config.ImgurUploadHistory.Remove(imgurInfo.Hash);
imgurInfo.Image = null;
}
// Update the credits in the config, this is shown in a form
if (nameValues.ContainsKey("X-RateLimit-Remaining") && int.TryParse(nameValues["X-RateLimit-Remaining"], out var credits)) {
Config.Credits = credits;
}
}
}
/// <summary>
/// Helper for logging
/// </summary>
/// <param name="nameValues"></param>
/// <param name="key"></param>
private static void LogHeader(IDictionary<string, string> nameValues, string key)
{
if (nameValues.ContainsKey(key))
{
Log.InfoFormat("{0}={1}", key, nameValues[key]);
}
}
/// <summary>
/// Log the current rate-limit information
/// </summary>
/// <param name="response"></param>
private static void LogRateLimitInfo(WebResponse response)
{
IDictionary<string, string> nameValues = new Dictionary<string, string>();
foreach (string key in response.Headers.AllKeys)
{
if (!nameValues.ContainsKey(key))
{
nameValues.Add(key, response.Headers[key]);
}
}
LogHeader(nameValues, "X-RateLimit-Limit");
LogHeader(nameValues, "X-RateLimit-Remaining");
LogHeader(nameValues, "X-RateLimit-UserLimit");
LogHeader(nameValues, "X-RateLimit-UserRemaining");
LogHeader(nameValues, "X-RateLimit-UserReset");
LogHeader(nameValues, "X-RateLimit-ClientLimit");
LogHeader(nameValues, "X-RateLimit-ClientRemaining");
// Update the credits in the config, this is shown in a form
if (nameValues.ContainsKey("X-RateLimit-Remaining") && int.TryParse(nameValues["X-RateLimit-Remaining"], out var credits))
{
Config.Credits = credits;
}
}
}
}

View file

@ -19,15 +19,17 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Greenshot.Plugin.Imgur {
public enum LangKey {
upload_menu_item,
upload_failure,
communication_wait,
delete_question,
clear_question,
delete_title,
history,
configure
}
namespace Greenshot.Plugin.Imgur
{
public enum LangKey
{
upload_menu_item,
upload_failure,
communication_wait,
delete_question,
clear_question,
delete_title,
history,
configure
}
}

View file

@ -27,164 +27,165 @@ using Dapplo.Log;
namespace Greenshot.Plugin.Jira
{
/// <summary>
/// This abstract class builds a base for a simple async memory cache.
/// </summary>
/// <typeparam name="TKey">Type for the key</typeparam>
/// <typeparam name="TResult">Type for the stored value</typeparam>
public abstract class AsyncMemoryCache<TKey, TResult> where TResult : class
{
private static readonly Task<TResult> EmptyValueTask = Task.FromResult<TResult>(null);
private readonly SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(1, 1);
private readonly MemoryCache _cache = new MemoryCache(Guid.NewGuid().ToString());
private readonly LogSource _log = new LogSource();
/// <summary>
/// Set the timespan for items to expire.
/// </summary>
public TimeSpan? ExpireTimeSpan { get; set; }
/// <summary>
/// Set the timespan for items to slide.
/// </summary>
public TimeSpan? SlidingTimeSpan { get; set; }
/// <summary>
/// Specifies if the RemovedCallback needs to be called
/// If this is active, ActivateUpdateCallback should be false
/// </summary>
protected bool ActivateRemovedCallback { get; set; } = true;
/// <summary>
/// Specifies if the UpdateCallback needs to be called.
/// If this is active, ActivateRemovedCallback should be false
/// </summary>
protected bool ActivateUpdateCallback { get; set; } = false;
/// <summary>
/// Implement this method, it should create an instance of TResult via the supplied TKey.
/// </summary>
/// <param name="key">TKey</param>
/// <param name="cancellationToken">CancellationToken</param>
/// <returns>TResult</returns>
protected abstract Task<TResult> CreateAsync(TKey key, CancellationToken cancellationToken = default);
/// <summary>
/// Creates a key under which the object is stored or retrieved, default is a toString on the object.
/// </summary>
/// <param name="keyObject">TKey</param>
/// <returns>string</returns>
protected virtual string CreateKey(TKey keyObject)
{
return keyObject.ToString();
}
/// <summary>
/// This abstract class builds a base for a simple async memory cache.
/// </summary>
/// <typeparam name="TKey">Type for the key</typeparam>
/// <typeparam name="TResult">Type for the stored value</typeparam>
public abstract class AsyncMemoryCache<TKey, TResult> where TResult : class
{
private static readonly Task<TResult> EmptyValueTask = Task.FromResult<TResult>(null);
private readonly SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(1, 1);
private readonly MemoryCache _cache = new MemoryCache(Guid.NewGuid().ToString());
private readonly LogSource _log = new LogSource();
/// <summary>
/// Get a task element from the cache, if this is not available call the create function.
/// </summary>
/// <param name="keyObject">object for the key</param>
/// <param name="cancellationToken">CancellationToken</param>
/// <returns>Task with TResult</returns>
public Task<TResult> GetOrCreateAsync(TKey keyObject, CancellationToken cancellationToken = default)
{
var key = CreateKey(keyObject);
return _cache.Get(key) as Task<TResult> ?? GetOrCreateInternalAsync(keyObject, null, cancellationToken);
}
/// Set the timespan for items to expire.
/// </summary>
public TimeSpan? ExpireTimeSpan { get; set; }
/// <summary>
/// This takes care of the real async part of the code.
/// </summary>
/// <param name="keyObject"></param>
/// <param name="cacheItemPolicy">CacheItemPolicy for when you want more control over the item</param>
/// <param name="cancellationToken">CancellationToken</param>
/// <returns>TResult</returns>
private async Task<TResult> GetOrCreateInternalAsync(TKey keyObject, CacheItemPolicy cacheItemPolicy = null, CancellationToken cancellationToken = default)
{
var key = CreateKey(keyObject);
var completionSource = new TaskCompletionSource<TResult>();
/// Set the timespan for items to slide.
/// </summary>
public TimeSpan? SlidingTimeSpan { get; set; }
if (cacheItemPolicy == null)
{
cacheItemPolicy = new CacheItemPolicy
{
AbsoluteExpiration = ExpireTimeSpan.HasValue ? DateTimeOffset.Now.Add(ExpireTimeSpan.Value) : ObjectCache.InfiniteAbsoluteExpiration,
SlidingExpiration = SlidingTimeSpan ?? ObjectCache.NoSlidingExpiration
};
if (ActivateUpdateCallback)
{
cacheItemPolicy.UpdateCallback = UpdateCallback;
}
if (ActivateRemovedCallback)
{
cacheItemPolicy.RemovedCallback = RemovedCallback;
}
}
/// <summary>
/// Specifies if the RemovedCallback needs to be called
/// If this is active, ActivateUpdateCallback should be false
/// </summary>
protected bool ActivateRemovedCallback { get; set; } = true;
/// <summary>
/// Specifies if the UpdateCallback needs to be called.
/// If this is active, ActivateRemovedCallback should be false
/// </summary>
protected bool ActivateUpdateCallback { get; set; } = false;
/// <summary>
/// Implement this method, it should create an instance of TResult via the supplied TKey.
/// </summary>
/// <param name="key">TKey</param>
/// <param name="cancellationToken">CancellationToken</param>
/// <returns>TResult</returns>
protected abstract Task<TResult> CreateAsync(TKey key, CancellationToken cancellationToken = default);
/// <summary>
/// Creates a key under which the object is stored or retrieved, default is a toString on the object.
/// </summary>
/// <param name="keyObject">TKey</param>
/// <returns>string</returns>
protected virtual string CreateKey(TKey keyObject)
{
return keyObject.ToString();
}
/// <summary>
/// Get a task element from the cache, if this is not available call the create function.
/// </summary>
/// <param name="keyObject">object for the key</param>
/// <param name="cancellationToken">CancellationToken</param>
/// <returns>Task with TResult</returns>
public Task<TResult> GetOrCreateAsync(TKey keyObject, CancellationToken cancellationToken = default)
{
var key = CreateKey(keyObject);
return _cache.Get(key) as Task<TResult> ?? GetOrCreateInternalAsync(keyObject, null, cancellationToken);
}
/// <summary>
/// This takes care of the real async part of the code.
/// </summary>
/// <param name="keyObject"></param>
/// <param name="cacheItemPolicy">CacheItemPolicy for when you want more control over the item</param>
/// <param name="cancellationToken">CancellationToken</param>
/// <returns>TResult</returns>
private async Task<TResult> GetOrCreateInternalAsync(TKey keyObject, CacheItemPolicy cacheItemPolicy = null, CancellationToken cancellationToken = default)
{
var key = CreateKey(keyObject);
var completionSource = new TaskCompletionSource<TResult>();
if (cacheItemPolicy == null)
{
cacheItemPolicy = new CacheItemPolicy
{
AbsoluteExpiration = ExpireTimeSpan.HasValue ? DateTimeOffset.Now.Add(ExpireTimeSpan.Value) : ObjectCache.InfiniteAbsoluteExpiration,
SlidingExpiration = SlidingTimeSpan ?? ObjectCache.NoSlidingExpiration
};
if (ActivateUpdateCallback)
{
cacheItemPolicy.UpdateCallback = UpdateCallback;
}
if (ActivateRemovedCallback)
{
cacheItemPolicy.RemovedCallback = RemovedCallback;
}
}
// Test if we got an existing object or our own
if (_cache.AddOrGetExisting(key, completionSource.Task, cacheItemPolicy) is Task<TResult> result && !completionSource.Task.Equals(result))
{
return await result.ConfigureAwait(false);
}
if (_cache.AddOrGetExisting(key, completionSource.Task, cacheItemPolicy) is Task<TResult> result && !completionSource.Task.Equals(result))
{
return await result.ConfigureAwait(false);
}
await _semaphoreSlim.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
result = _cache.AddOrGetExisting(key, completionSource.Task, cacheItemPolicy) as Task<TResult>;
if (result != null && !completionSource.Task.Equals(result))
{
return await result.ConfigureAwait(false);
}
await _semaphoreSlim.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
result = _cache.AddOrGetExisting(key, completionSource.Task, cacheItemPolicy) as Task<TResult>;
if (result != null && !completionSource.Task.Equals(result))
{
return await result.ConfigureAwait(false);
}
// Now, start the background task, which will set the completionSource with the correct response
// ReSharper disable once MethodSupportsCancellation
// ReSharper disable once UnusedVariable
var ignoreBackgroundTask = Task.Run(async () =>
{
try
{
var backgroundResult = await CreateAsync(keyObject, cancellationToken).ConfigureAwait(false);
completionSource.TrySetResult(backgroundResult);
}
catch (TaskCanceledException)
{
completionSource.TrySetCanceled();
}
catch (Exception ex)
{
completionSource.TrySetException(ex);
}
});
}
finally
{
_semaphoreSlim.Release();
}
// Now, start the background task, which will set the completionSource with the correct response
// ReSharper disable once MethodSupportsCancellation
// ReSharper disable once UnusedVariable
var ignoreBackgroundTask = Task.Run(async () =>
{
try
{
var backgroundResult = await CreateAsync(keyObject, cancellationToken).ConfigureAwait(false);
completionSource.TrySetResult(backgroundResult);
}
catch (TaskCanceledException)
{
completionSource.TrySetCanceled();
}
catch (Exception ex)
{
completionSource.TrySetException(ex);
}
});
}
finally
{
_semaphoreSlim.Release();
}
return await completionSource.Task.ConfigureAwait(false);
}
return await completionSource.Task.ConfigureAwait(false);
}
/// <summary>
/// Override to know when an item is removed, make sure to configure ActivateUpdateCallback / ActivateRemovedCallback
/// </summary>
/// <param name="cacheEntryRemovedArguments">CacheEntryRemovedArguments</param>
protected void RemovedCallback(CacheEntryRemovedArguments cacheEntryRemovedArguments)
{
_log.Verbose().WriteLine("Item {0} removed due to {1}.", cacheEntryRemovedArguments.CacheItem.Key, cacheEntryRemovedArguments.RemovedReason);
/// <summary>
/// Override to know when an item is removed, make sure to configure ActivateUpdateCallback / ActivateRemovedCallback
/// </summary>
/// <param name="cacheEntryRemovedArguments">CacheEntryRemovedArguments</param>
protected void RemovedCallback(CacheEntryRemovedArguments cacheEntryRemovedArguments)
{
_log.Verbose().WriteLine("Item {0} removed due to {1}.", cacheEntryRemovedArguments.CacheItem.Key, cacheEntryRemovedArguments.RemovedReason);
if (cacheEntryRemovedArguments.CacheItem.Value is IDisposable disposable)
{
_log.Debug().WriteLine("Disposed cached item.");
disposable.Dispose();
}
}
{
_log.Debug().WriteLine("Disposed cached item.");
disposable.Dispose();
}
}
/// <summary>
/// Override to modify the cache behaviour when an item is about to be removed, make sure to configure
/// ActivateUpdateCallback / ActivateRemovedCallback
/// </summary>
/// <param name="cacheEntryUpdateArguments">CacheEntryUpdateArguments</param>
protected void UpdateCallback(CacheEntryUpdateArguments cacheEntryUpdateArguments)
{
_log.Verbose().WriteLine("Update request for {0} due to {1}.", cacheEntryUpdateArguments.Key, cacheEntryUpdateArguments.RemovedReason);
}
}
/// <summary>
/// Override to modify the cache behaviour when an item is about to be removed, make sure to configure
/// ActivateUpdateCallback / ActivateRemovedCallback
/// </summary>
/// <param name="cacheEntryUpdateArguments">CacheEntryUpdateArguments</param>
protected void UpdateCallback(CacheEntryUpdateArguments cacheEntryUpdateArguments)
{
_log.Verbose().WriteLine("Update request for {0} due to {1}.", cacheEntryUpdateArguments.Key, cacheEntryUpdateArguments.RemovedReason);
}
}
}

View file

@ -30,207 +30,242 @@ using GreenshotPlugin.Controls;
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.Jira.Forms {
public partial class JiraForm : Form {
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(JiraForm));
private readonly JiraConnector _jiraConnector;
private Issue _selectedIssue;
private readonly GreenshotColumnSorter _columnSorter;
private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection<CoreConfiguration>();
namespace Greenshot.Plugin.Jira.Forms
{
public partial class JiraForm : Form
{
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(JiraForm));
private readonly JiraConnector _jiraConnector;
private Issue _selectedIssue;
private readonly GreenshotColumnSorter _columnSorter;
private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection<CoreConfiguration>();
public JiraForm(JiraConnector jiraConnector) {
InitializeComponent();
Icon = GreenshotResources.GetGreenshotIcon();
AcceptButton = uploadButton;
CancelButton = cancelButton;
public JiraForm(JiraConnector jiraConnector)
{
InitializeComponent();
Icon = GreenshotResources.GetGreenshotIcon();
AcceptButton = uploadButton;
CancelButton = cancelButton;
InitializeComponentText();
InitializeComponentText();
_columnSorter = new GreenshotColumnSorter();
jiraListView.ListViewItemSorter = _columnSorter;
_columnSorter = new GreenshotColumnSorter();
jiraListView.ListViewItemSorter = _columnSorter;
_jiraConnector = jiraConnector;
_jiraConnector = jiraConnector;
ChangeModus(false);
ChangeModus(false);
uploadButton.Enabled = false;
Load += OnLoad;
}
uploadButton.Enabled = false;
Load += OnLoad;
}
private async void OnLoad(object sender, EventArgs eventArgs)
{
try
{
if (!_jiraConnector.IsLoggedIn)
{
await _jiraConnector.LoginAsync();
}
}
catch (Exception e)
{
MessageBox.Show(Language.GetFormattedString("jira", LangKey.login_error, e.Message));
}
if (_jiraConnector.IsLoggedIn)
{
var filters = await _jiraConnector.GetFavoriteFiltersAsync();
if (filters.Count > 0)
{
foreach (var filter in filters)
{
jiraFilterBox.Items.Add(filter);
}
jiraFilterBox.SelectedIndex = 0;
}
ChangeModus(true);
if (_jiraConnector.Monitor.RecentJiras.Any())
{
_selectedIssue = _jiraConnector.Monitor.RecentJiras.First().JiraIssue;
jiraKey.Text = _selectedIssue.Key;
uploadButton.Enabled = true;
}
}
}
private async void OnLoad(object sender, EventArgs eventArgs)
{
try
{
if (!_jiraConnector.IsLoggedIn)
{
await _jiraConnector.LoginAsync();
}
}
catch (Exception e)
{
MessageBox.Show(Language.GetFormattedString("jira", LangKey.login_error, e.Message));
}
private void InitializeComponentText() {
label_jirafilter.Text = Language.GetString("jira", LangKey.label_jirafilter);
label_comment.Text = Language.GetString("jira", LangKey.label_comment);
label_filename.Text = Language.GetString("jira", LangKey.label_filename);
}
if (_jiraConnector.IsLoggedIn)
{
var filters = await _jiraConnector.GetFavoriteFiltersAsync();
if (filters.Count > 0)
{
foreach (var filter in filters)
{
jiraFilterBox.Items.Add(filter);
}
private void ChangeModus(bool enabled) {
jiraFilterBox.Enabled = enabled;
jiraListView.Enabled = enabled;
jiraFilenameBox.Enabled = enabled;
jiraCommentBox.Enabled = enabled;
}
jiraFilterBox.SelectedIndex = 0;
}
public void SetFilename(string filename) {
jiraFilenameBox.Text = filename;
}
ChangeModus(true);
if (_jiraConnector.Monitor.RecentJiras.Any())
{
_selectedIssue = _jiraConnector.Monitor.RecentJiras.First().JiraIssue;
jiraKey.Text = _selectedIssue.Key;
uploadButton.Enabled = true;
}
}
}
public Issue GetJiraIssue() {
return _selectedIssue;
}
private void InitializeComponentText()
{
label_jirafilter.Text = Language.GetString("jira", LangKey.label_jirafilter);
label_comment.Text = Language.GetString("jira", LangKey.label_comment);
label_filename.Text = Language.GetString("jira", LangKey.label_filename);
}
public async Task UploadAsync(IBinaryContainer attachment) {
attachment.Filename = jiraFilenameBox.Text;
await _jiraConnector.AttachAsync(_selectedIssue.Key, attachment);
private void ChangeModus(bool enabled)
{
jiraFilterBox.Enabled = enabled;
jiraListView.Enabled = enabled;
jiraFilenameBox.Enabled = enabled;
jiraCommentBox.Enabled = enabled;
}
if (!string.IsNullOrEmpty(jiraCommentBox.Text)) {
await _jiraConnector.AddCommentAsync(_selectedIssue.Key, jiraCommentBox.Text);
}
}
public void SetFilename(string filename)
{
jiraFilenameBox.Text = filename;
}
private async void JiraFilterBox_SelectedIndexChanged(object sender, EventArgs e) {
if (_jiraConnector.IsLoggedIn) {
public Issue GetJiraIssue()
{
return _selectedIssue;
}
uploadButton.Enabled = false;
var filter = (Filter)jiraFilterBox.SelectedItem;
if (filter == null) {
return;
}
IList<Issue> issues = null;
try
{
issues = await _jiraConnector.SearchAsync(filter);
}
catch (Exception ex)
{
Log.Error(ex);
MessageBox.Show(this, ex.Message, "Error in filter", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
public async Task UploadAsync(IBinaryContainer attachment)
{
attachment.Filename = jiraFilenameBox.Text;
await _jiraConnector.AttachAsync(_selectedIssue.Key, attachment);
jiraListView.Items.Clear();
if (issues?.Count > 0) {
jiraListView.Columns.Clear();
LangKey[] columns = { LangKey.column_issueType, LangKey.column_id, LangKey.column_created, LangKey.column_assignee, LangKey.column_reporter, LangKey.column_summary };
foreach (LangKey column in columns)
{
if (!Language.TryGetString("jira", column, out var translation))
{
translation = string.Empty;
}
jiraListView.Columns.Add(translation);
}
var scaledIconSize = DpiHelper.ScaleWithDpi(CoreConfig.IconSize, DpiHelper.GetDpi(Handle));
var imageList = new ImageList {
ImageSize = scaledIconSize
};
jiraListView.SmallImageList = imageList;
jiraListView.LargeImageList = imageList;
if (!string.IsNullOrEmpty(jiraCommentBox.Text))
{
await _jiraConnector.AddCommentAsync(_selectedIssue.Key, jiraCommentBox.Text);
}
}
foreach (var issue in issues) {
var item = new ListViewItem
{
Tag = issue
};
try
{
var issueIcon = await _jiraConnector.GetIssueTypeBitmapAsync(issue);
imageList.Images.Add(issueIcon);
item.ImageIndex = imageList.Images.Count - 1;
}
catch (Exception ex)
{
Log.Warn("Problem loading issue type, ignoring", ex);
}
private async void JiraFilterBox_SelectedIndexChanged(object sender, EventArgs e)
{
if (_jiraConnector.IsLoggedIn)
{
uploadButton.Enabled = false;
var filter = (Filter) jiraFilterBox.SelectedItem;
if (filter == null)
{
return;
}
item.SubItems.Add(issue.Key);
IList<Issue> issues = null;
try
{
issues = await _jiraConnector.SearchAsync(filter);
}
catch (Exception ex)
{
Log.Error(ex);
MessageBox.Show(this, ex.Message, "Error in filter", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
jiraListView.Items.Clear();
if (issues?.Count > 0)
{
jiraListView.Columns.Clear();
LangKey[] columns =
{
LangKey.column_issueType, LangKey.column_id, LangKey.column_created, LangKey.column_assignee, LangKey.column_reporter, LangKey.column_summary
};
foreach (LangKey column in columns)
{
if (!Language.TryGetString("jira", column, out var translation))
{
translation = string.Empty;
}
jiraListView.Columns.Add(translation);
}
var scaledIconSize = DpiHelper.ScaleWithDpi(CoreConfig.IconSize, DpiHelper.GetDpi(Handle));
var imageList = new ImageList
{
ImageSize = scaledIconSize
};
jiraListView.SmallImageList = imageList;
jiraListView.LargeImageList = imageList;
foreach (var issue in issues)
{
var item = new ListViewItem
{
Tag = issue
};
try
{
var issueIcon = await _jiraConnector.GetIssueTypeBitmapAsync(issue);
imageList.Images.Add(issueIcon);
item.ImageIndex = imageList.Images.Count - 1;
}
catch (Exception ex)
{
Log.Warn("Problem loading issue type, ignoring", ex);
}
item.SubItems.Add(issue.Key);
item.SubItems.Add(issue.Fields.Created.HasValue
? issue.Fields.Created.Value.ToString("d", DateTimeFormatInfo.InvariantInfo)
: string.Empty);
item.SubItems.Add(issue.Fields.Assignee?.DisplayName);
item.SubItems.Add(issue.Fields.Reporter?.DisplayName);
item.SubItems.Add(issue.Fields.Summary);
jiraListView.Items.Add(item);
for (int i = 0; i < columns.Length; i++)
{
jiraListView.AutoResizeColumn(i, ColumnHeaderAutoResizeStyle.ColumnContent);
}
jiraListView.Invalidate();
jiraListView.Update();
}
item.SubItems.Add(issue.Fields.Reporter?.DisplayName);
item.SubItems.Add(issue.Fields.Summary);
jiraListView.Items.Add(item);
for (int i = 0; i < columns.Length; i++)
{
jiraListView.AutoResizeColumn(i, ColumnHeaderAutoResizeStyle.ColumnContent);
}
jiraListView.Refresh();
}
}
}
jiraListView.Invalidate();
jiraListView.Update();
}
private void JiraListView_SelectedIndexChanged(object sender, EventArgs e) {
if (jiraListView.SelectedItems.Count > 0) {
_selectedIssue = (Issue)jiraListView.SelectedItems[0].Tag;
jiraKey.Text = _selectedIssue.Key;
uploadButton.Enabled = true;
} else {
uploadButton.Enabled = false;
}
}
jiraListView.Refresh();
}
}
}
private void JiraListView_ColumnClick(object sender, ColumnClickEventArgs e) {
// Determine if clicked column is already the column that is being sorted.
if (e.Column == _columnSorter.SortColumn) {
// Reverse the current sort direction for this column.
_columnSorter.Order = _columnSorter.Order == SortOrder.Ascending ? SortOrder.Descending : SortOrder.Ascending;
} else {
// Set the column number that is to be sorted; default to ascending.
_columnSorter.SortColumn = e.Column;
_columnSorter.Order = SortOrder.Ascending;
}
private void JiraListView_SelectedIndexChanged(object sender, EventArgs e)
{
if (jiraListView.SelectedItems.Count > 0)
{
_selectedIssue = (Issue) jiraListView.SelectedItems[0].Tag;
jiraKey.Text = _selectedIssue.Key;
uploadButton.Enabled = true;
}
else
{
uploadButton.Enabled = false;
}
}
// Perform the sort with these new sort options.
jiraListView.Sort();
}
private void JiraListView_ColumnClick(object sender, ColumnClickEventArgs e)
{
// Determine if clicked column is already the column that is being sorted.
if (e.Column == _columnSorter.SortColumn)
{
// Reverse the current sort direction for this column.
_columnSorter.Order = _columnSorter.Order == SortOrder.Ascending ? SortOrder.Descending : SortOrder.Ascending;
}
else
{
// Set the column number that is to be sorted; default to ascending.
_columnSorter.SortColumn = e.Column;
_columnSorter.Order = SortOrder.Ascending;
}
private async void JiraKeyTextChanged(object sender, EventArgs e) {
string jiranumber = jiraKey.Text;
uploadButton.Enabled = false;
int dashIndex = jiranumber.IndexOf('-');
if (dashIndex > 0 && jiranumber.Length > dashIndex+1) {
_selectedIssue = await _jiraConnector.GetIssueAsync(jiraKey.Text);
if (_selectedIssue != null) {
uploadButton.Enabled = true;
}
}
}
}
// Perform the sort with these new sort options.
jiraListView.Sort();
}
private async void JiraKeyTextChanged(object sender, EventArgs e)
{
string jiranumber = jiraKey.Text;
uploadButton.Enabled = false;
int dashIndex = jiranumber.IndexOf('-');
if (dashIndex > 0 && jiranumber.Length > dashIndex + 1)
{
_selectedIssue = await _jiraConnector.GetIssueAsync(jiraKey.Text);
if (_selectedIssue != null)
{
uploadButton.Enabled = true;
}
}
}
}
}

View file

@ -21,7 +21,9 @@
using GreenshotPlugin.Controls;
namespace Greenshot.Plugin.Jira.Forms {
public class JiraFormBase : GreenshotForm {
}
namespace Greenshot.Plugin.Jira.Forms
{
public class JiraFormBase : GreenshotForm
{
}
}

View file

@ -19,19 +19,21 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Greenshot.Plugin.Jira.Forms {
/// <summary>
/// Description of PasswordRequestForm.
/// </summary>
public partial class SettingsForm : JiraFormBase {
public SettingsForm()
{
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
AcceptButton = buttonOK;
CancelButton = buttonCancel;
}
}
namespace Greenshot.Plugin.Jira.Forms
{
/// <summary>
/// Description of PasswordRequestForm.
/// </summary>
public partial class SettingsForm : JiraFormBase
{
public SettingsForm()
{
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
AcceptButton = buttonOK;
CancelButton = buttonCancel;
}
}
}

View file

@ -28,28 +28,28 @@ using Dapplo.Jira.Entities;
namespace Greenshot.Plugin.Jira
{
/// <summary>
/// This is the bach for the IssueType bitmaps
/// </summary>
public class IssueTypeBitmapCache : AsyncMemoryCache<IssueType, Bitmap>
{
private readonly IJiraClient _jiraClient;
/// <summary>
/// This is the bach for the IssueType bitmaps
/// </summary>
public class IssueTypeBitmapCache : AsyncMemoryCache<IssueType, Bitmap>
{
private readonly IJiraClient _jiraClient;
public IssueTypeBitmapCache(IJiraClient jiraClient)
{
_jiraClient = jiraClient;
// Set the expire timeout to an hour
ExpireTimeSpan = TimeSpan.FromHours(4);
}
public IssueTypeBitmapCache(IJiraClient jiraClient)
{
_jiraClient = jiraClient;
// Set the expire timeout to an hour
ExpireTimeSpan = TimeSpan.FromHours(4);
}
protected override string CreateKey(IssueType keyObject)
{
return keyObject.Name;
}
protected override string CreateKey(IssueType keyObject)
{
return keyObject.Name;
}
protected override async Task<Bitmap> CreateAsync(IssueType issueType, CancellationToken cancellationToken = new CancellationToken())
{
return await _jiraClient.Server.GetUriContentAsync<Bitmap>(issueType.IconUri, cancellationToken).ConfigureAwait(false);
}
}
protected override async Task<Bitmap> CreateAsync(IssueType issueType, CancellationToken cancellationToken = new CancellationToken())
{
return await _jiraClient.Server.GetUriContentAsync<Bitmap>(issueType.IconUri, cancellationToken).ConfigureAwait(false);
}
}
}

View file

@ -22,25 +22,27 @@
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.Jira {
/// <summary>
/// Description of JiraConfiguration.
/// </summary>
[IniSection("Jira", Description="Greenshot Jira Plugin configuration")]
public class JiraConfiguration : IniSection {
public const string DefaultPrefix = "http://";
private const string DefaultUrl = DefaultPrefix + "jira";
namespace Greenshot.Plugin.Jira
{
/// <summary>
/// Description of JiraConfiguration.
/// </summary>
[IniSection("Jira", Description = "Greenshot Jira Plugin configuration")]
public class JiraConfiguration : IniSection
{
public const string DefaultPrefix = "http://";
private const string DefaultUrl = DefaultPrefix + "jira";
[IniProperty("Url", Description="Base url to Jira system, without anything else", DefaultValue=DefaultUrl)]
public string Url { get; set; }
[IniProperty("Url", Description = "Base url to Jira system, without anything else", DefaultValue = DefaultUrl)]
public string Url { get; set; }
[IniProperty("UploadFormat", Description="What file type to use for uploading", DefaultValue="png")]
public OutputFormat UploadFormat { get; set; }
[IniProperty("UploadFormat", Description = "What file type to use for uploading", DefaultValue = "png")]
public OutputFormat UploadFormat { get; set; }
[IniProperty("UploadJpegQuality", Description="JPEG file save quality in %.", DefaultValue="80")]
public int UploadJpegQuality { get; set; }
[IniProperty("UploadJpegQuality", Description = "JPEG file save quality in %.", DefaultValue = "80")]
public int UploadJpegQuality { get; set; }
[IniProperty("UploadReduceColors", Description="Reduce color amount of the uploaded image to 256", DefaultValue="False")]
public bool UploadReduceColors { get; set; }
}
[IniProperty("UploadReduceColors", Description = "Reduce color amount of the uploaded image to 256", DefaultValue = "False")]
public bool UploadReduceColors { get; set; }
}
}

View file

@ -34,247 +34,280 @@ using Dapplo.Jira.SvgWinForms.Converters;
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.Jira {
/// <summary>
/// This encapsulates the JiraClient to make it possible to change as less old Greenshot code as needed
/// </summary>
public sealed class JiraConnector : IDisposable {
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(JiraConnector));
private static readonly JiraConfiguration JiraConfig = IniConfig.GetIniSection<JiraConfiguration>();
private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection<CoreConfiguration>();
// Used to remove the wsdl information from the old SOAP Uri
public const string DefaultPostfix = "/rpc/soap/jirasoapservice-v2?wsdl";
private IJiraClient _jiraClient;
private IssueTypeBitmapCache _issueTypeBitmapCache;
namespace Greenshot.Plugin.Jira
{
/// <summary>
/// This encapsulates the JiraClient to make it possible to change as less old Greenshot code as needed
/// </summary>
public sealed class JiraConnector : IDisposable
{
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(JiraConnector));
private static readonly JiraConfiguration JiraConfig = IniConfig.GetIniSection<JiraConfiguration>();
/// <summary>
/// Initialize some basic stuff, in the case the SVG to bitmap converter
/// </summary>
static JiraConnector()
{
CoreConfig.PropertyChanged += (sender, args) =>
{
if (args.PropertyName == nameof(CoreConfig.IconSize))
private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection<CoreConfiguration>();
// Used to remove the wsdl information from the old SOAP Uri
public const string DefaultPostfix = "/rpc/soap/jirasoapservice-v2?wsdl";
private IJiraClient _jiraClient;
private IssueTypeBitmapCache _issueTypeBitmapCache;
/// <summary>
/// Initialize some basic stuff, in the case the SVG to bitmap converter
/// </summary>
static JiraConnector()
{
CoreConfig.PropertyChanged += (sender, args) =>
{
if (args.PropertyName == nameof(CoreConfig.IconSize))
{
var jiraConnector = SimpleServiceProvider.Current.GetInstance<JiraConnector>();
jiraConnector._jiraClient?.Behaviour.SetConfig(new SvgConfiguration { Width = CoreConfig.ScaledIconSize.Width, Height = CoreConfig.ScaledIconSize.Height });
}
};
jiraConnector._jiraClient?.Behaviour.SetConfig(new SvgConfiguration
{
Width = CoreConfig.ScaledIconSize.Width,
Height = CoreConfig.ScaledIconSize.Height
});
}
};
}
}
/// <summary>
/// Dispose, logout the users
/// </summary>
public void Dispose() {
if (_jiraClient != null)
{
/// <summary>
/// Dispose, logout the users
/// </summary>
public void Dispose()
{
if (_jiraClient != null)
{
Logout();
}
FavIcon?.Dispose();
}
}
/// <summary>
/// Constructor
/// </summary>
public JiraConnector()
{
JiraConfig.Url = JiraConfig.Url.Replace(DefaultPostfix, string.Empty);
}
FavIcon?.Dispose();
}
/// <summary>
/// Access the jira monitor
/// </summary>
public JiraMonitor Monitor { get; private set; }
/// <summary>
/// Constructor
/// </summary>
public JiraConnector()
{
JiraConfig.Url = JiraConfig.Url.Replace(DefaultPostfix, string.Empty);
}
public Bitmap FavIcon { get; private set; }
/// <summary>
/// Access the jira monitor
/// </summary>
public JiraMonitor Monitor { get; private set; }
/// <summary>
/// Internal login which catches the exceptions
/// </summary>
/// <returns>true if login was done successfully</returns>
private async Task<bool> DoLoginAsync(string user, string password, CancellationToken cancellationToken = default)
{
if (string.IsNullOrEmpty(user) || string.IsNullOrEmpty(password))
{
return false;
}
_jiraClient = JiraClient.Create(new Uri(JiraConfig.Url));
_jiraClient.Behaviour.SetConfig(new SvgConfiguration { Width = CoreConfig.ScaledIconSize.Width, Height = CoreConfig.ScaledIconSize.Height });
public Bitmap FavIcon { get; private set; }
/// <summary>
/// Internal login which catches the exceptions
/// </summary>
/// <returns>true if login was done successfully</returns>
private async Task<bool> DoLoginAsync(string user, string password, CancellationToken cancellationToken = default)
{
if (string.IsNullOrEmpty(user) || string.IsNullOrEmpty(password))
{
return false;
}
_jiraClient = JiraClient.Create(new Uri(JiraConfig.Url));
_jiraClient.Behaviour.SetConfig(new SvgConfiguration
{
Width = CoreConfig.ScaledIconSize.Width,
Height = CoreConfig.ScaledIconSize.Height
});
_jiraClient.SetBasicAuthentication(user, password);
_issueTypeBitmapCache = new IssueTypeBitmapCache(_jiraClient);
try
{
Monitor = new JiraMonitor();
await Monitor.AddJiraInstanceAsync(_jiraClient, cancellationToken);
_issueTypeBitmapCache = new IssueTypeBitmapCache(_jiraClient);
try
{
Monitor = new JiraMonitor();
await Monitor.AddJiraInstanceAsync(_jiraClient, cancellationToken);
var favIconUri = _jiraClient.JiraBaseUri.AppendSegments("favicon.ico");
try
{
FavIcon = await _jiraClient.Server.GetUriContentAsync<Bitmap>(favIconUri, cancellationToken);
}
catch (Exception ex)
{
Log.WarnFormat("Couldn't load favicon from {0}", favIconUri);
Log.Warn("Exception details: ", ex);
}
}
catch (Exception ex2)
{
var favIconUri = _jiraClient.JiraBaseUri.AppendSegments("favicon.ico");
try
{
FavIcon = await _jiraClient.Server.GetUriContentAsync<Bitmap>(favIconUri, cancellationToken);
}
catch (Exception ex)
{
Log.WarnFormat("Couldn't load favicon from {0}", favIconUri);
Log.Warn("Exception details: ", ex);
}
}
catch (Exception ex2)
{
Log.WarnFormat("Couldn't connect to JIRA {0}", JiraConfig.Url);
Log.Warn("Exception details: ", ex2);
return false;
}
return true;
}
return false;
}
/// <summary>
/// Use the credentials dialog, this will show if there are not correct credentials.
/// If there are credentials, call the real login.
/// </summary>
/// <returns>Task</returns>
public async Task LoginAsync(CancellationToken cancellationToken = default) {
Logout();
try {
// Get the system name, so the user knows where to login to
var credentialsDialog = new CredentialsDialog(JiraConfig.Url)
{
Name = null
};
while (credentialsDialog.Show(credentialsDialog.Name) == DialogResult.OK) {
if (await DoLoginAsync(credentialsDialog.Name, credentialsDialog.Password, cancellationToken)) {
if (credentialsDialog.SaveChecked) {
credentialsDialog.Confirm(true);
}
IsLoggedIn = true;
return;
}
// Login failed, confirm this
try {
credentialsDialog.Confirm(false);
} catch (ApplicationException e) {
// exception handling ...
Log.Error("Problem using the credentials dialog", e);
}
// For every windows version after XP show an incorrect password baloon
credentialsDialog.IncorrectPassword = true;
// Make sure the dialog is display, the password was false!
credentialsDialog.AlwaysDisplay = true;
}
} catch (ApplicationException e) {
// exception handling ...
Log.Error("Problem using the credentials dialog", e);
}
return true;
}
}
/// <summary>
/// Use the credentials dialog, this will show if there are not correct credentials.
/// If there are credentials, call the real login.
/// </summary>
/// <returns>Task</returns>
public async Task LoginAsync(CancellationToken cancellationToken = default)
{
Logout();
try
{
// Get the system name, so the user knows where to login to
var credentialsDialog = new CredentialsDialog(JiraConfig.Url)
{
Name = null
};
while (credentialsDialog.Show(credentialsDialog.Name) == DialogResult.OK)
{
if (await DoLoginAsync(credentialsDialog.Name, credentialsDialog.Password, cancellationToken))
{
if (credentialsDialog.SaveChecked)
{
credentialsDialog.Confirm(true);
}
/// <summary>
/// End the session, if there was one
/// </summary>
public void Logout() {
IsLoggedIn = true;
return;
}
// Login failed, confirm this
try
{
credentialsDialog.Confirm(false);
}
catch (ApplicationException e)
{
// exception handling ...
Log.Error("Problem using the credentials dialog", e);
}
// For every windows version after XP show an incorrect password baloon
credentialsDialog.IncorrectPassword = true;
// Make sure the dialog is display, the password was false!
credentialsDialog.AlwaysDisplay = true;
}
}
catch (ApplicationException e)
{
// exception handling ...
Log.Error("Problem using the credentials dialog", e);
}
}
/// <summary>
/// End the session, if there was one
/// </summary>
public void Logout()
{
if (_jiraClient == null || !IsLoggedIn) return;
Monitor.Dispose();
IsLoggedIn = false;
}
/// <summary>
/// check the login credentials, to prevent timeouts of the session, or makes a login
/// Do not use ConfigureAwait to call this, as it will move await from the UI thread.
/// </summary>
/// <returns></returns>
private async Task CheckCredentialsAsync(CancellationToken cancellationToken = default) {
if (!IsLoggedIn) {
await LoginAsync(cancellationToken);
}
}
/// <summary>
/// check the login credentials, to prevent timeouts of the session, or makes a login
/// Do not use ConfigureAwait to call this, as it will move await from the UI thread.
/// </summary>
/// <returns></returns>
private async Task CheckCredentialsAsync(CancellationToken cancellationToken = default)
{
if (!IsLoggedIn)
{
await LoginAsync(cancellationToken);
}
}
/// <summary>
/// Get the favorite filters
/// </summary>
/// <returns>List with filters</returns>
public async Task<IList<Filter>> GetFavoriteFiltersAsync(CancellationToken cancellationToken = default)
{
await CheckCredentialsAsync(cancellationToken);
return await _jiraClient.Filter.GetFavoritesAsync(cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Get the favorite filters
/// </summary>
/// <returns>List with filters</returns>
public async Task<IList<Filter>> GetFavoriteFiltersAsync(CancellationToken cancellationToken = default)
{
await CheckCredentialsAsync(cancellationToken);
return await _jiraClient.Filter.GetFavoritesAsync(cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Get the issue for a key
/// </summary>
/// <param name="issueKey">Jira issue key</param>
/// <param name="cancellationToken">CancellationToken</param>
/// <returns>Issue</returns>
public async Task<Issue> GetIssueAsync(string issueKey, CancellationToken cancellationToken = default)
{
await CheckCredentialsAsync(cancellationToken);
try
{
return await _jiraClient.Issue.GetAsync(issueKey, cancellationToken: cancellationToken).ConfigureAwait(false);
}
catch
{
return null;
}
}
/// <summary>
/// Get the issue for a key
/// </summary>
/// <param name="issueKey">Jira issue key</param>
/// <param name="cancellationToken">CancellationToken</param>
/// <returns>Issue</returns>
public async Task<Issue> GetIssueAsync(string issueKey, CancellationToken cancellationToken = default)
{
await CheckCredentialsAsync(cancellationToken);
try
{
return await _jiraClient.Issue.GetAsync(issueKey, cancellationToken: cancellationToken).ConfigureAwait(false);
}
catch
{
return null;
}
}
/// <summary>
/// Attach the content to the jira
/// </summary>
/// <param name="issueKey"></param>
/// <param name="content">IBinaryContainer</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task AttachAsync(string issueKey, IBinaryContainer content, CancellationToken cancellationToken = default)
{
await CheckCredentialsAsync(cancellationToken);
/// <summary>
/// Attach the content to the jira
/// </summary>
/// <param name="issueKey"></param>
/// <param name="content">IBinaryContainer</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task AttachAsync(string issueKey, IBinaryContainer content, CancellationToken cancellationToken = default)
{
await CheckCredentialsAsync(cancellationToken);
using var memoryStream = new MemoryStream();
content.WriteToStream(memoryStream);
memoryStream.Seek(0, SeekOrigin.Begin);
await _jiraClient.Attachment.AttachAsync(issueKey, memoryStream, content.Filename, content.ContentType, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Add a comment to the supplied issue
/// </summary>
/// <param name="issueKey">Jira issue key</param>
/// <param name="body">text</param>
/// <param name="visibility">the visibility role</param>
/// <param name="cancellationToken">CancellationToken</param>
public async Task AddCommentAsync(string issueKey, string body, Visibility visibility = null, CancellationToken cancellationToken = default)
{
await CheckCredentialsAsync(cancellationToken);
await _jiraClient.Issue.AddCommentAsync(issueKey, body, visibility, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Add a comment to the supplied issue
/// </summary>
/// <param name="issueKey">Jira issue key</param>
/// <param name="body">text</param>
/// <param name="visibility">the visibility role</param>
/// <param name="cancellationToken">CancellationToken</param>
public async Task AddCommentAsync(string issueKey, string body, Visibility visibility = null, CancellationToken cancellationToken = default)
{
await CheckCredentialsAsync(cancellationToken);
await _jiraClient.Issue.AddCommentAsync(issueKey, body, visibility, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Get the search results for the specified filter
/// </summary>
/// <param name="filter">Filter</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<IList<Issue>> SearchAsync(Filter filter, CancellationToken cancellationToken = default)
{
await CheckCredentialsAsync(cancellationToken);
var searchResult = await _jiraClient.Issue.SearchAsync(filter.Jql, null, new[] { "summary", "reporter", "assignee", "created", "issuetype" }, null, cancellationToken).ConfigureAwait(false);
return searchResult.Issues;
}
/// <summary>
/// Get the search results for the specified filter
/// </summary>
/// <param name="filter">Filter</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<IList<Issue>> SearchAsync(Filter filter, CancellationToken cancellationToken = default)
{
await CheckCredentialsAsync(cancellationToken);
var searchResult = await _jiraClient.Issue.SearchAsync(filter.Jql, null, new[]
{
"summary", "reporter", "assignee", "created", "issuetype"
}, null, cancellationToken).ConfigureAwait(false);
return searchResult.Issues;
}
/// <summary>
/// Get the bitmap representing the issue type of an issue, from cache.
/// </summary>
/// <param name="issue">Issue</param>
/// <param name="cancellationToken">CancellationToken</param>
/// <returns>Bitmap</returns>
public async Task<Bitmap> GetIssueTypeBitmapAsync(Issue issue, CancellationToken cancellationToken = default)
{
return await _issueTypeBitmapCache.GetOrCreateAsync(issue.Fields.IssueType, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Get the bitmap representing the issue type of an issue, from cache.
/// </summary>
/// <param name="issue">Issue</param>
/// <param name="cancellationToken">CancellationToken</param>
/// <returns>Bitmap</returns>
public async Task<Bitmap> GetIssueTypeBitmapAsync(Issue issue, CancellationToken cancellationToken = default)
{
return await _issueTypeBitmapCache.GetOrCreateAsync(issue.Fields.IssueType, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Get the base uri
/// </summary>
public Uri JiraBaseUri => _jiraClient.JiraBaseUri;
/// <summary>
/// Get the base uri
/// </summary>
public Uri JiraBaseUri => _jiraClient.JiraBaseUri;
/// <summary>
/// Is the user "logged in?

View file

@ -34,127 +34,148 @@ using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
namespace Greenshot.Plugin.Jira {
/// <summary>
/// Description of JiraDestination.
/// </summary>
public class JiraDestination : AbstractDestination {
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(JiraDestination));
private static readonly JiraConfiguration Config = IniConfig.GetIniSection<JiraConfiguration>();
private readonly Issue _jiraIssue;
namespace Greenshot.Plugin.Jira
{
/// <summary>
/// Description of JiraDestination.
/// </summary>
public class JiraDestination : AbstractDestination
{
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(JiraDestination));
private static readonly JiraConfiguration Config = IniConfig.GetIniSection<JiraConfiguration>();
private readonly Issue _jiraIssue;
public JiraDestination(Issue jiraIssue = null) {
public JiraDestination(Issue jiraIssue = null)
{
_jiraIssue = jiraIssue;
}
}
public override string Designation => "Jira";
public override string Designation => "Jira";
public override string Description {
get
{
if (_jiraIssue?.Fields?.Summary == null) {
return Language.GetString("jira", LangKey.upload_menu_item);
}
// Format the title of this destination
return _jiraIssue.Key + ": " + _jiraIssue.Fields.Summary.Substring(0, Math.Min(20, _jiraIssue.Fields.Summary.Length));
}
}
public override string Description
{
get
{
if (_jiraIssue?.Fields?.Summary == null)
{
return Language.GetString("jira", LangKey.upload_menu_item);
}
public override bool IsActive => base.IsActive && !string.IsNullOrEmpty(Config.Url);
// Format the title of this destination
return _jiraIssue.Key + ": " + _jiraIssue.Fields.Summary.Substring(0, Math.Min(20, _jiraIssue.Fields.Summary.Length));
}
}
public override bool IsDynamic => true;
public override bool IsActive => base.IsActive && !string.IsNullOrEmpty(Config.Url);
public override Image DisplayIcon {
get
{
Image displayIcon = null;
public override bool IsDynamic => true;
public override Image DisplayIcon
{
get
{
Image displayIcon = null;
var jiraConnector = SimpleServiceProvider.Current.GetInstance<JiraConnector>();
if (jiraConnector != null)
{
if (_jiraIssue != null)
{
// Try to get the issue type as icon
try
{
displayIcon = jiraConnector.GetIssueTypeBitmapAsync(_jiraIssue).Result;
}
catch (Exception ex)
{
Log.Warn($"Problem loading issue type for {_jiraIssue.Key}, ignoring", ex);
}
}
if (displayIcon == null)
{
displayIcon = jiraConnector.FavIcon;
}
}
if (displayIcon == null)
{
var resources = new ComponentResourceManager(typeof(JiraPlugin));
displayIcon = (Image)resources.GetObject("Jira");
}
return displayIcon;
}
}
if (jiraConnector != null)
{
if (_jiraIssue != null)
{
// Try to get the issue type as icon
try
{
displayIcon = jiraConnector.GetIssueTypeBitmapAsync(_jiraIssue).Result;
}
catch (Exception ex)
{
Log.Warn($"Problem loading issue type for {_jiraIssue.Key}, ignoring", ex);
}
}
public override IEnumerable<IDestination> DynamicDestinations()
{
var jiraConnector = SimpleServiceProvider.Current.GetInstance<JiraConnector>();
if (jiraConnector == null || !jiraConnector.IsLoggedIn) {
yield break;
}
foreach(var jiraDetails in jiraConnector.Monitor.RecentJiras)
{
yield return new JiraDestination(jiraDetails.JiraIssue);
}
}
if (displayIcon == null)
{
displayIcon = jiraConnector.FavIcon;
}
}
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surfaceToUpload, ICaptureDetails captureDetails) {
ExportInformation exportInformation = new ExportInformation(Designation, Description);
string filename = Path.GetFileName(FilenameHelper.GetFilename(Config.UploadFormat, captureDetails));
SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(Config.UploadFormat, Config.UploadJpegQuality, Config.UploadReduceColors);
if (displayIcon == null)
{
var resources = new ComponentResourceManager(typeof(JiraPlugin));
displayIcon = (Image) resources.GetObject("Jira");
}
return displayIcon;
}
}
public override IEnumerable<IDestination> DynamicDestinations()
{
var jiraConnector = SimpleServiceProvider.Current.GetInstance<JiraConnector>();
if (_jiraIssue != null) {
try {
// Run upload in the background
new PleaseWaitForm().ShowAndWait(Description, Language.GetString("jira", LangKey.communication_wait),
async () =>
{
var surfaceContainer = new SurfaceContainer(surfaceToUpload, outputSettings, filename);
await jiraConnector.AttachAsync(_jiraIssue.Key, surfaceContainer);
surfaceToUpload.UploadUrl = jiraConnector.JiraBaseUri.AppendSegments("browse", _jiraIssue.Key).AbsoluteUri;
}
);
Log.DebugFormat("Uploaded to Jira {0}", _jiraIssue.Key);
exportInformation.ExportMade = true;
exportInformation.Uri = surfaceToUpload.UploadUrl;
} catch (Exception e) {
MessageBox.Show(Language.GetString("jira", LangKey.upload_failure) + " " + e.Message);
}
} else {
var jiraForm = new JiraForm(jiraConnector);
jiraForm.SetFilename(filename);
var dialogResult = jiraForm.ShowDialog();
if (dialogResult == DialogResult.OK) {
try {
surfaceToUpload.UploadUrl = jiraConnector.JiraBaseUri.AppendSegments("browse", jiraForm.GetJiraIssue().Key).AbsoluteUri;
// Run upload in the background
new PleaseWaitForm().ShowAndWait(Description, Language.GetString("jira", LangKey.communication_wait),
async () =>
{
await jiraForm.UploadAsync(new SurfaceContainer(surfaceToUpload, outputSettings, filename));
}
);
Log.DebugFormat("Uploaded to Jira {0}", jiraForm.GetJiraIssue().Key);
exportInformation.ExportMade = true;
exportInformation.Uri = surfaceToUpload.UploadUrl;
} catch(Exception e) {
MessageBox.Show(Language.GetString("jira", LangKey.upload_failure) + " " + e.Message);
}
}
}
ProcessExport(exportInformation, surfaceToUpload);
return exportInformation;
}
}
if (jiraConnector == null || !jiraConnector.IsLoggedIn)
{
yield break;
}
foreach (var jiraDetails in jiraConnector.Monitor.RecentJiras)
{
yield return new JiraDestination(jiraDetails.JiraIssue);
}
}
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surfaceToUpload, ICaptureDetails captureDetails)
{
ExportInformation exportInformation = new ExportInformation(Designation, Description);
string filename = Path.GetFileName(FilenameHelper.GetFilename(Config.UploadFormat, captureDetails));
SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(Config.UploadFormat, Config.UploadJpegQuality, Config.UploadReduceColors);
var jiraConnector = SimpleServiceProvider.Current.GetInstance<JiraConnector>();
if (_jiraIssue != null)
{
try
{
// Run upload in the background
new PleaseWaitForm().ShowAndWait(Description, Language.GetString("jira", LangKey.communication_wait),
async () =>
{
var surfaceContainer = new SurfaceContainer(surfaceToUpload, outputSettings, filename);
await jiraConnector.AttachAsync(_jiraIssue.Key, surfaceContainer);
surfaceToUpload.UploadUrl = jiraConnector.JiraBaseUri.AppendSegments("browse", _jiraIssue.Key).AbsoluteUri;
}
);
Log.DebugFormat("Uploaded to Jira {0}", _jiraIssue.Key);
exportInformation.ExportMade = true;
exportInformation.Uri = surfaceToUpload.UploadUrl;
}
catch (Exception e)
{
MessageBox.Show(Language.GetString("jira", LangKey.upload_failure) + " " + e.Message);
}
}
else
{
var jiraForm = new JiraForm(jiraConnector);
jiraForm.SetFilename(filename);
var dialogResult = jiraForm.ShowDialog();
if (dialogResult == DialogResult.OK)
{
try
{
surfaceToUpload.UploadUrl = jiraConnector.JiraBaseUri.AppendSegments("browse", jiraForm.GetJiraIssue().Key).AbsoluteUri;
// Run upload in the background
new PleaseWaitForm().ShowAndWait(Description, Language.GetString("jira", LangKey.communication_wait),
async () => { await jiraForm.UploadAsync(new SurfaceContainer(surfaceToUpload, outputSettings, filename)); }
);
Log.DebugFormat("Uploaded to Jira {0}", jiraForm.GetJiraIssue().Key);
exportInformation.ExportMade = true;
exportInformation.Uri = surfaceToUpload.UploadUrl;
}
catch (Exception e)
{
MessageBox.Show(Language.GetString("jira", LangKey.upload_failure) + " " + e.Message);
}
}
}
ProcessExport(exportInformation, surfaceToUpload);
return exportInformation;
}
}
}

View file

@ -24,48 +24,28 @@ using Dapplo.Jira.Entities;
namespace Greenshot.Plugin.Jira
{
public class JiraDetails : IComparable<JiraDetails>
{
public JiraDetails()
{
FirstSeenAt = SeenAt = DateTimeOffset.Now;
}
public class JiraDetails : IComparable<JiraDetails>
{
public JiraDetails()
{
FirstSeenAt = SeenAt = DateTimeOffset.Now;
}
public string ProjectKey
{
get;
set;
}
public string ProjectKey { get; set; }
public string Id
{
get;
set;
}
public string Id { get; set; }
public string JiraKey => ProjectKey + "-" + Id;
public string JiraKey => ProjectKey + "-" + Id;
public Issue JiraIssue
{
get;
set;
}
public Issue JiraIssue { get; set; }
public DateTimeOffset FirstSeenAt
{
get;
private set;
}
public DateTimeOffset FirstSeenAt { get; private set; }
public DateTimeOffset SeenAt
{
get;
set;
}
public DateTimeOffset SeenAt { get; set; }
public int CompareTo(JiraDetails other)
{
return SeenAt.CompareTo(other.SeenAt);
}
}
public int CompareTo(JiraDetails other)
{
return SeenAt.CompareTo(other.SeenAt);
}
}
}

View file

@ -23,18 +23,10 @@ using System;
namespace Greenshot.Plugin.Jira
{
public class JiraEventArgs : EventArgs
{
public JiraEventTypes EventType
{
get;
set;
}
public class JiraEventArgs : EventArgs
{
public JiraEventTypes EventType { get; set; }
public JiraDetails Details
{
get;
set;
}
}
public JiraDetails Details { get; set; }
}
}

View file

@ -21,9 +21,9 @@
namespace Greenshot.Plugin.Jira
{
public enum JiraEventTypes
{
OrderChanged,
DetectedNewJiraIssue
}
public enum JiraEventTypes
{
OrderChanged,
DetectedNewJiraIssue
}
}

View file

@ -31,188 +31,203 @@ using GreenshotPlugin.Hooking;
namespace Greenshot.Plugin.Jira
{
/// <summary>
/// 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!
/// </summary>
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<IJiraClient> _jiraInstances = new List<IJiraClient>();
private readonly IDictionary<string, IJiraClient> _projectJiraClientMap = new Dictionary<string, IJiraClient>();
private readonly int _maxEntries;
// TODO: Add issues from issueHistory (JQL -> Where.IssueKey.InIssueHistory())
private IDictionary<string, JiraDetails> _recentJiras = new Dictionary<string, JiraDetails>();
{
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<IJiraClient> _jiraInstances = new List<IJiraClient>();
private readonly IDictionary<string, IJiraClient> _projectJiraClientMap = new Dictionary<string, IJiraClient>();
/// <summary>
/// Register to this event to get events when new jira issues are detected
/// </summary>
public event EventHandler<JiraEventArgs> JiraEvent;
private readonly int _maxEntries;
public JiraMonitor(int maxEntries = 40)
{
_maxEntries = maxEntries;
_monitor = new WindowsTitleMonitor();
_monitor.TitleChangeEvent += MonitorTitleChangeEvent;
}
// TODO: Add issues from issueHistory (JQL -> Where.IssueKey.InIssueHistory())
private IDictionary<string, JiraDetails> _recentJiras = new Dictionary<string, JiraDetails>();
/// <summary>
/// Dispose
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// Register to this event to get events when new jira issues are detected
/// </summary>
public event EventHandler<JiraEventArgs> JiraEvent;
/// <summary>
/// Dispose all managed resources
/// </summary>
/// <param name="disposing">when true is passed all managed resources are disposed.</param>
protected void Dispose(bool disposing)
{
if (!disposing)
{
return;
}
// free managed resources
_monitor.TitleChangeEvent -= MonitorTitleChangeEvent;
_monitor.Dispose();
// free native resources if there are any.
}
public JiraMonitor(int maxEntries = 40)
{
_maxEntries = maxEntries;
_monitor = new WindowsTitleMonitor();
_monitor.TitleChangeEvent += MonitorTitleChangeEvent;
}
/// <summary>
/// Retrieve the API belonging to a JiraDetails
/// </summary>
/// <param name="jiraDetails"></param>
/// <returns>IJiraClient</returns>
public IJiraClient GetJiraClientForKey(JiraDetails jiraDetails)
{
return _projectJiraClientMap[jiraDetails.ProjectKey];
}
/// Dispose
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Get the "list" of recently seen Jiras
/// </summary>
public IEnumerable<JiraDetails> RecentJiras =>
(from jiraDetails in _recentJiras.Values
orderby jiraDetails.SeenAt descending
select jiraDetails);
/// <summary>
/// Dispose all managed resources
/// </summary>
/// <param name="disposing">when true is passed all managed resources are disposed.</param>
protected void Dispose(bool disposing)
{
if (!disposing)
{
return;
}
/// <summary>
/// Check if this monitor has active instances
/// </summary>
public bool HasJiraInstances => _jiraInstances.Count > 0;
// free managed resources
_monitor.TitleChangeEvent -= MonitorTitleChangeEvent;
_monitor.Dispose();
// free native resources if there are any.
}
/// <summary>
/// Add an instance of a JIRA system
/// </summary>
/// <param name="jiraInstance">IJiraClient</param>
/// <param name="token">CancellationToken</param>
public async Task AddJiraInstanceAsync(IJiraClient jiraInstance, CancellationToken token = default)
{
_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);
}
}
}
}
/// <summary>
/// Retrieve the API belonging to a JiraDetails
/// </summary>
/// <param name="jiraDetails"></param>
/// <returns>IJiraClient</returns>
public IJiraClient GetJiraClientForKey(JiraDetails jiraDetails)
{
return _projectJiraClientMap[jiraDetails.ProjectKey];
}
/// <summary>
/// This method will update details, like the title, and send an event to registed listeners of the JiraEvent
/// </summary>
/// <param name="jiraDetails">Contains the jira key to retrieve the title (XYZ-1234)</param>
/// <returns>Task</returns>
private async Task DetectedNewJiraIssueAsync(JiraDetails jiraDetails)
{
try
{
/// <summary>
/// Get the "list" of recently seen Jiras
/// </summary>
public IEnumerable<JiraDetails> RecentJiras =>
(from jiraDetails in _recentJiras.Values
orderby jiraDetails.SeenAt descending
select jiraDetails);
/// <summary>
/// Check if this monitor has active instances
/// </summary>
public bool HasJiraInstances => _jiraInstances.Count > 0;
/// <summary>
/// Add an instance of a JIRA system
/// </summary>
/// <param name="jiraInstance">IJiraClient</param>
/// <param name="token">CancellationToken</param>
public async Task AddJiraInstanceAsync(IJiraClient jiraInstance, CancellationToken token = default)
{
_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);
}
}
}
}
/// <summary>
/// This method will update details, like the title, and send an event to registed listeners of the JiraEvent
/// </summary>
/// <param name="jiraDetails">Contains the jira key to retrieve the title (XYZ-1234)</param>
/// <returns>Task</returns>
private async Task DetectedNewJiraIssueAsync(JiraDetails jiraDetails)
{
try
{
if (_projectJiraClientMap.TryGetValue(jiraDetails.ProjectKey, out var 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);
}
}
{
var issue = await jiraClient.Issue.GetAsync(jiraDetails.JiraKey).ConfigureAwait(false);
jiraDetails.JiraIssue = issue;
}
/// <summary>
/// Handle title changes, check for JIRA
/// </summary>
/// <param name="eventArgs"></param>
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];
// 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);
}
}
/// <summary>
/// Handle title changes, check for JIRA
/// </summary>
/// <param name="eventArgs"></param>
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];
// Check if we have a JIRA instance with a project for this key
if (_projectJiraClientMap.TryGetValue(projectKey, out var jiraClient))
{
// We have found a project for this _jira key, so it must be a valid & known JIRA
if (_projectJiraClientMap.TryGetValue(projectKey, out var jiraClient))
{
// We have found a project for this _jira key, so it must be a valid & known JIRA
if (_recentJiras.TryGetValue(jiraKey, out var currentJiraDetails))
{
// update
currentJiraDetails.SeenAt = DateTimeOffset.Now;
{
// update
currentJiraDetails.SeenAt = DateTimeOffset.Now;
// Notify the order change
JiraEvent?.Invoke(this, new JiraEventArgs { Details = currentJiraDetails, EventType = JiraEventTypes.OrderChanged });
// Nothing else to do
// 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);
return;
}
// 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);
}
}
}
// 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);
}
}
}
}

View file

@ -30,109 +30,117 @@ using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
using log4net;
namespace Greenshot.Plugin.Jira {
/// <summary>
/// This is the JiraPlugin base code
/// </summary>
namespace Greenshot.Plugin.Jira
{
/// <summary>
/// This is the JiraPlugin base code
/// </summary>
[Plugin("Jira", true)]
public class JiraPlugin : IGreenshotPlugin {
private static readonly ILog Log = LogManager.GetLogger(typeof(JiraPlugin));
private JiraConfiguration _config;
public class JiraPlugin : IGreenshotPlugin
{
private static readonly ILog Log = LogManager.GetLogger(typeof(JiraPlugin));
private JiraConfiguration _config;
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(bool disposing) {
if (disposing)
protected void Dispose(bool disposing)
{
if (disposing)
{
var jiraConnector = SimpleServiceProvider.Current.GetInstance<JiraConnector>();
jiraConnector?.Dispose();
}
}
}
}
/// <summary>
/// Implementation of the IGreenshotPlugin.Initialize
/// </summary>
/// <returns>true if plugin is initialized, false if not (doesn't show)</returns>
public bool Initialize() {
// Register configuration (don't need the configuration itself)
_config = IniConfig.GetIniSection<JiraConfiguration>();
/// <summary>
/// Implementation of the IGreenshotPlugin.Initialize
/// </summary>
/// <returns>true if plugin is initialized, false if not (doesn't show)</returns>
public bool Initialize()
{
// Register configuration (don't need the configuration itself)
_config = IniConfig.GetIniSection<JiraConfiguration>();
// Provide the JiraConnector
SimpleServiceProvider.Current.AddService(new JiraConnector());
// Provide the IDestination
SimpleServiceProvider.Current.AddService<IDestination>(new JiraDestination());
// Provide the JiraConnector
SimpleServiceProvider.Current.AddService(new JiraConnector());
// Provide the IDestination
SimpleServiceProvider.Current.AddService<IDestination>(new JiraDestination());
// Make sure the log is enabled for the correct level.
if (Log.IsDebugEnabled)
{
LogSettings.RegisterDefaultLogger<Log4NetLogger>(LogLevels.Verbose);
}
else if (Log.IsInfoEnabled)
{
LogSettings.RegisterDefaultLogger<Log4NetLogger>(LogLevels.Info);
}
else if (Log.IsWarnEnabled)
{
LogSettings.RegisterDefaultLogger<Log4NetLogger>(LogLevels.Warn);
}
else if (Log.IsErrorEnabled)
{
LogSettings.RegisterDefaultLogger<Log4NetLogger>(LogLevels.Error);
}
else if (Log.IsErrorEnabled)
{
LogSettings.RegisterDefaultLogger<Log4NetLogger>(LogLevels.Error);
}
else
{
LogSettings.RegisterDefaultLogger<Log4NetLogger>(LogLevels.Fatal);
}
// Make sure the log is enabled for the correct level.
if (Log.IsDebugEnabled)
{
LogSettings.RegisterDefaultLogger<Log4NetLogger>(LogLevels.Verbose);
}
else if (Log.IsInfoEnabled)
{
LogSettings.RegisterDefaultLogger<Log4NetLogger>(LogLevels.Info);
}
else if (Log.IsWarnEnabled)
{
LogSettings.RegisterDefaultLogger<Log4NetLogger>(LogLevels.Warn);
}
else if (Log.IsErrorEnabled)
{
LogSettings.RegisterDefaultLogger<Log4NetLogger>(LogLevels.Error);
}
else if (Log.IsErrorEnabled)
{
LogSettings.RegisterDefaultLogger<Log4NetLogger>(LogLevels.Error);
}
else
{
LogSettings.RegisterDefaultLogger<Log4NetLogger>(LogLevels.Fatal);
}
return true;
}
}
public void Shutdown() {
Log.Debug("Jira Plugin shutdown.");
public void Shutdown()
{
Log.Debug("Jira Plugin shutdown.");
var jiraConnector = SimpleServiceProvider.Current.GetInstance<JiraConnector>();
jiraConnector?.Logout();
}
/// <summary>
/// Implementation of the IPlugin.Configure
/// </summary>
public void Configure() {
string url = _config.Url;
if (ShowConfigDialog()) {
// check for re-login
/// <summary>
/// Implementation of the IPlugin.Configure
/// </summary>
public void Configure()
{
string url = _config.Url;
if (ShowConfigDialog())
{
// check for re-login
var jiraConnector = SimpleServiceProvider.Current.GetInstance<JiraConnector>();
if (jiraConnector != null && jiraConnector.IsLoggedIn && !string.IsNullOrEmpty(url)) {
if (!url.Equals(_config.Url)) {
if (jiraConnector != null && jiraConnector.IsLoggedIn && !string.IsNullOrEmpty(url))
{
if (!url.Equals(_config.Url))
{
jiraConnector.Logout();
Task.Run(async () =>
{
await jiraConnector.LoginAsync();
});
}
}
}
}
Task.Run(async () => { await jiraConnector.LoginAsync(); });
}
}
}
}
/// <summary>
/// A form for username/password
/// </summary>
/// <returns>bool true if OK was pressed, false if cancel</returns>
private bool ShowConfigDialog()
{
var settingsForm = new SettingsForm();
var result = settingsForm.ShowDialog();
if (result == DialogResult.OK)
{
return true;
}
return false;
}
}
/// <summary>
/// A form for username/password
/// </summary>
/// <returns>bool true if OK was pressed, false if cancel</returns>
private bool ShowConfigDialog()
{
var settingsForm = new SettingsForm();
var result = settingsForm.ShowDialog();
if (result == DialogResult.OK)
{
return true;
}
return false;
}
}
}

View file

@ -19,20 +19,22 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Greenshot.Plugin.Jira {
public enum LangKey {
upload_menu_item,
column_assignee,
column_created,
column_issueType,
column_id,
column_reporter,
column_summary,
label_comment,
label_filename,
label_jirafilter,
login_error,
upload_failure,
communication_wait,
}
namespace Greenshot.Plugin.Jira
{
public enum LangKey
{
upload_menu_item,
column_assignee,
column_created,
column_issueType,
column_id,
column_reporter,
column_summary,
label_comment,
label_filename,
label_jirafilter,
login_error,
upload_failure,
communication_wait,
}
}

View file

@ -24,97 +24,98 @@ using log4net;
namespace Greenshot.Plugin.Jira
{
/// <summary>
/// Used to make Dapplo.Log, used in Dapplo.Jira, write to Log4net
/// </summary>
public class Log4NetLogger : AbstractLogger
{
private ILog GetLogger(LogSource logSource)
{
return logSource.SourceType != null ? LogManager.GetLogger(logSource.SourceType) : LogManager.GetLogger(logSource.Source);
}
/// <summary>
/// Used to make Dapplo.Log, used in Dapplo.Jira, write to Log4net
/// </summary>
public class Log4NetLogger : AbstractLogger
{
private ILog GetLogger(LogSource logSource)
{
return logSource.SourceType != null ? LogManager.GetLogger(logSource.SourceType) : LogManager.GetLogger(logSource.Source);
}
/// <summary>
/// Write the supplied information to a log4net.ILog
/// </summary>
/// <param name="logInfo">LogInfo</param>
/// <param name="messageTemplate">string</param>
/// <param name="propertyValues">params object[]</param>
public override void Write(LogInfo logInfo, string messageTemplate, params object[] propertyValues)
{
var log = GetLogger(logInfo.Source);
/// <summary>
/// Write the supplied information to a log4net.ILog
/// </summary>
/// <param name="logInfo">LogInfo</param>
/// <param name="messageTemplate">string</param>
/// <param name="propertyValues">params object[]</param>
public override void Write(LogInfo logInfo, string messageTemplate, params object[] propertyValues)
{
var log = GetLogger(logInfo.Source);
switch (logInfo.LogLevel)
{
case LogLevels.Verbose:
case LogLevels.Debug:
if (propertyValues != null)
log.DebugFormat(messageTemplate, propertyValues);
else
log.Debug(messageTemplate);
break;
case LogLevels.Error:
if (propertyValues != null)
log.ErrorFormat(messageTemplate, propertyValues);
else
log.Error(messageTemplate);
break;
case LogLevels.Fatal:
if (propertyValues != null)
log.FatalFormat(messageTemplate, propertyValues);
else
log.Fatal(messageTemplate);
break;
case LogLevels.Info:
if (propertyValues != null)
log.InfoFormat(messageTemplate, propertyValues);
else
log.Info(messageTemplate);
break;
case LogLevels.Warn:
if (propertyValues != null)
log.WarnFormat(messageTemplate, propertyValues);
else
log.Warn(messageTemplate);
break;
}
}
switch (logInfo.LogLevel)
{
case LogLevels.Verbose:
case LogLevels.Debug:
if (propertyValues != null)
log.DebugFormat(messageTemplate, propertyValues);
else
log.Debug(messageTemplate);
break;
case LogLevels.Error:
if (propertyValues != null)
log.ErrorFormat(messageTemplate, propertyValues);
else
log.Error(messageTemplate);
break;
case LogLevels.Fatal:
if (propertyValues != null)
log.FatalFormat(messageTemplate, propertyValues);
else
log.Fatal(messageTemplate);
break;
case LogLevels.Info:
if (propertyValues != null)
log.InfoFormat(messageTemplate, propertyValues);
else
log.Info(messageTemplate);
break;
case LogLevels.Warn:
if (propertyValues != null)
log.WarnFormat(messageTemplate, propertyValues);
else
log.Warn(messageTemplate);
break;
}
}
/// <summary>
/// Make sure there are no newlines passed
/// </summary>
/// <param name="logInfo"></param>
/// <param name="messageTemplate"></param>
/// <param name="logParameters"></param>
public override void WriteLine(LogInfo logInfo, string messageTemplate, params object[] logParameters)
{
Write(logInfo, messageTemplate, logParameters);
}
/// <summary>
/// Make sure there are no newlines passed
/// </summary>
/// <param name="logInfo"></param>
/// <param name="messageTemplate"></param>
/// <param name="logParameters"></param>
public override void WriteLine(LogInfo logInfo, string messageTemplate, params object[] logParameters)
{
Write(logInfo, messageTemplate, logParameters);
}
/// <summary>
/// Test if a certain LogLevels enum is enabled
/// </summary>
/// <param name="level">LogLevels value</param>
/// <param name="logSource">LogSource to check for</param>
/// <returns>bool true if the LogLevels enum is enabled</returns>
public override bool IsLogLevelEnabled(LogLevels level, LogSource logSource = null)
{
var log = GetLogger(logSource);
switch (level)
{
case LogLevels.Verbose:
case LogLevels.Debug:
return log.IsDebugEnabled;
case LogLevels.Error:
return log.IsErrorEnabled;
case LogLevels.Fatal:
return log.IsFatalEnabled;
case LogLevels.Info:
return log.IsInfoEnabled;
case LogLevels.Warn:
return log.IsWarnEnabled;
}
return false;
}
}
/// <summary>
/// Test if a certain LogLevels enum is enabled
/// </summary>
/// <param name="level">LogLevels value</param>
/// <param name="logSource">LogSource to check for</param>
/// <returns>bool true if the LogLevels enum is enabled</returns>
public override bool IsLogLevelEnabled(LogLevels level, LogSource logSource = null)
{
var log = GetLogger(logSource);
switch (level)
{
case LogLevels.Verbose:
case LogLevels.Debug:
return log.IsDebugEnabled;
case LogLevels.Error:
return log.IsErrorEnabled;
case LogLevels.Fatal:
return log.IsFatalEnabled;
case LogLevels.Info:
return log.IsInfoEnabled;
case LogLevels.Warn:
return log.IsWarnEnabled;
}
return false;
}
}
}

View file

@ -1,5 +1,6 @@
// Copyright (c) Dapplo and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Greenshot.Plugin.Office.Com
{
/// <summary>

View file

@ -38,6 +38,7 @@ namespace Greenshot.Plugin.Office.Com
{
return;
}
// Do not catch an exception from this.
// You may want to remove these guards depending on
// what you think the semantics should be.
@ -45,6 +46,7 @@ namespace Greenshot.Plugin.Office.Com
{
Marshal.ReleaseComObject(ComObject);
}
ComObject = default;
}
}

View file

@ -24,6 +24,7 @@ namespace Greenshot.Plugin.Office.Com
{
return clsId;
}
return clsId;
}

View file

@ -23,7 +23,7 @@ namespace Greenshot.Plugin.Office.Com
{
if (GetActiveObject(ref clsId, IntPtr.Zero, out object comObject).Succeeded())
{
return DisposableCom.Create((T)comObject);
return DisposableCom.Create((T) comObject);
}
return null;

View file

@ -28,70 +28,89 @@ using GreenshotPlugin.Core;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
namespace Greenshot.Plugin.Office.Destinations {
/// <summary>
/// Description of PowerpointDestination.
/// </summary>
public class ExcelDestination : AbstractDestination {
private const int IconApplication = 0;
private const int IconWorkbook = 1;
private static readonly string ExePath;
private readonly string _workbookName;
namespace Greenshot.Plugin.Office.Destinations
{
/// <summary>
/// Description of PowerpointDestination.
/// </summary>
public class ExcelDestination : AbstractDestination
{
private const int IconApplication = 0;
private const int IconWorkbook = 1;
private static readonly string ExePath;
private readonly string _workbookName;
static ExcelDestination() {
ExePath = PluginUtils.GetExePath("EXCEL.EXE");
if (ExePath != null && File.Exists(ExePath)) {
WindowDetails.AddProcessToExcludeFromFreeze("excel");
} else {
ExePath = null;
}
}
static ExcelDestination()
{
ExePath = PluginUtils.GetExePath("EXCEL.EXE");
if (ExePath != null && File.Exists(ExePath))
{
WindowDetails.AddProcessToExcludeFromFreeze("excel");
}
else
{
ExePath = null;
}
}
public ExcelDestination() {
}
public ExcelDestination()
{
}
public ExcelDestination(string workbookName) {
_workbookName = workbookName;
}
public ExcelDestination(string workbookName)
{
_workbookName = workbookName;
}
public override string Designation => "Excel";
public override string Designation => "Excel";
public override string Description => _workbookName ?? "Microsoft Excel";
public override string Description => _workbookName ?? "Microsoft Excel";
public override int Priority => 5;
public override int Priority => 5;
public override bool IsDynamic => true;
public override bool IsDynamic => true;
public override bool IsActive => base.IsActive && ExePath != null;
public override bool IsActive => base.IsActive && ExePath != null;
public override Image DisplayIcon => PluginUtils.GetCachedExeIcon(ExePath, !string.IsNullOrEmpty(_workbookName) ? IconWorkbook : IconApplication);
public override Image DisplayIcon => PluginUtils.GetCachedExeIcon(ExePath, !string.IsNullOrEmpty(_workbookName) ? IconWorkbook : IconApplication);
public override IEnumerable<IDestination> DynamicDestinations() {
foreach (string workbookName in ExcelExporter.GetWorkbooks()) {
yield return new ExcelDestination(workbookName);
}
}
public override IEnumerable<IDestination> DynamicDestinations()
{
foreach (string workbookName in ExcelExporter.GetWorkbooks())
{
yield return new ExcelDestination(workbookName);
}
}
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) {
ExportInformation exportInformation = new ExportInformation(Designation, Description);
bool createdFile = false;
string imageFile = captureDetails.Filename;
if (imageFile == null || surface.Modified || !Regex.IsMatch(imageFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$")) {
imageFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat());
createdFile = true;
}
if (_workbookName != null) {
ExcelExporter.InsertIntoExistingWorkbook(_workbookName, imageFile, surface.Image.Size);
} else {
ExcelExporter.InsertIntoNewWorkbook(imageFile, surface.Image.Size);
}
exportInformation.ExportMade = true;
ProcessExport(exportInformation, surface);
// Cleanup imageFile if we created it here, so less tmp-files are generated and left
if (createdFile) {
ImageOutput.DeleteNamedTmpFile(imageFile);
}
return exportInformation;
}
}
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails)
{
ExportInformation exportInformation = new ExportInformation(Designation, Description);
bool createdFile = false;
string imageFile = captureDetails.Filename;
if (imageFile == null || surface.Modified || !Regex.IsMatch(imageFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$"))
{
imageFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat());
createdFile = true;
}
if (_workbookName != null)
{
ExcelExporter.InsertIntoExistingWorkbook(_workbookName, imageFile, surface.Image.Size);
}
else
{
ExcelExporter.InsertIntoNewWorkbook(imageFile, surface.Image.Size);
}
exportInformation.ExportMade = true;
ProcessExport(exportInformation, surface);
// Cleanup imageFile if we created it here, so less tmp-files are generated and left
if (createdFile)
{
ImageOutput.DeleteNamedTmpFile(imageFile);
}
return exportInformation;
}
}
}

View file

@ -28,97 +28,117 @@ using Greenshot.Plugin.Office.OfficeExport.Entities;
using GreenshotPlugin.Core;
using GreenshotPlugin.Interfaces;
namespace Greenshot.Plugin.Office.Destinations {
public class OneNoteDestination : AbstractDestination {
private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(WordDestination));
private const int ICON_APPLICATION = 0;
public const string DESIGNATION = "OneNote";
private static readonly string exePath;
private readonly OneNotePage page;
private readonly OneNoteExporter _oneNoteExporter = new OneNoteExporter();
namespace Greenshot.Plugin.Office.Destinations
{
public class OneNoteDestination : AbstractDestination
{
private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(WordDestination));
private const int ICON_APPLICATION = 0;
public const string DESIGNATION = "OneNote";
private static readonly string exePath;
private readonly OneNotePage page;
private readonly OneNoteExporter _oneNoteExporter = new OneNoteExporter();
static OneNoteDestination() {
exePath = PluginUtils.GetExePath("ONENOTE.EXE");
if (exePath != null && File.Exists(exePath)) {
WindowDetails.AddProcessToExcludeFromFreeze("onenote");
} else {
exePath = null;
}
}
static OneNoteDestination()
{
exePath = PluginUtils.GetExePath("ONENOTE.EXE");
if (exePath != null && File.Exists(exePath))
{
WindowDetails.AddProcessToExcludeFromFreeze("onenote");
}
else
{
exePath = null;
}
}
public OneNoteDestination() {
public OneNoteDestination()
{
}
}
public OneNoteDestination(OneNotePage page)
{
this.page = page;
}
public OneNoteDestination(OneNotePage page) {
this.page = page;
}
public override string Designation
{
get { return DESIGNATION; }
}
public override string Designation {
get {
return DESIGNATION;
}
}
public override string Description
{
get
{
if (page == null)
{
return "Microsoft OneNote";
}
else
{
return page.DisplayName;
}
}
}
public override string Description {
get {
if (page == null) {
return "Microsoft OneNote";
} else {
return page.DisplayName;
}
}
}
public override int Priority
{
get { return 4; }
}
public override int Priority {
get {
return 4;
}
}
public override bool IsDynamic
{
get { return true; }
}
public override bool IsDynamic {
get {
return true;
}
}
public override bool IsActive
{
get { return base.IsActive && exePath != null; }
}
public override bool IsActive {
get {
return base.IsActive && exePath != null;
}
}
public override Image DisplayIcon
{
get { return PluginUtils.GetCachedExeIcon(exePath, ICON_APPLICATION); }
}
public override Image DisplayIcon {
get {
return PluginUtils.GetCachedExeIcon(exePath, ICON_APPLICATION);
}
}
public override IEnumerable<IDestination> DynamicDestinations()
{
foreach (OneNotePage page in _oneNoteExporter.GetPages())
{
yield return new OneNoteDestination(page);
}
}
public override IEnumerable<IDestination> DynamicDestinations() {
foreach (OneNotePage page in _oneNoteExporter.GetPages()) {
yield return new OneNoteDestination(page);
}
}
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails)
{
ExportInformation exportInformation = new ExportInformation(Designation, Description);
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) {
ExportInformation exportInformation = new ExportInformation(Designation, Description);
if (page == null)
{
try
{
exportInformation.ExportMade = _oneNoteExporter.ExportToNewPage(surface);
}
catch (Exception ex)
{
exportInformation.ErrorMessage = ex.Message;
LOG.Error(ex);
}
}
else
{
try
{
exportInformation.ExportMade = _oneNoteExporter.ExportToPage(surface, page);
}
catch (Exception ex)
{
exportInformation.ErrorMessage = ex.Message;
LOG.Error(ex);
}
}
if (page == null) {
try {
exportInformation.ExportMade = _oneNoteExporter.ExportToNewPage(surface);
} catch(Exception ex) {
exportInformation.ErrorMessage = ex.Message;
LOG.Error(ex);
}
} else {
try {
exportInformation.ExportMade = _oneNoteExporter.ExportToPage(surface, page);
} catch(Exception ex) {
exportInformation.ErrorMessage = ex.Message;
LOG.Error(ex);
}
}
return exportInformation;
}
}
return exportInformation;
}
}
}

View file

@ -32,152 +32,192 @@ using GreenshotPlugin.Interfaces.Plugin;
using Microsoft.Office.Interop.Outlook;
using Microsoft.Win32;
namespace Greenshot.Plugin.Office.Destinations {
/// <summary>
/// Description of OutlookDestination.
/// </summary>
public class OutlookDestination : AbstractDestination {
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(OutlookDestination));
private const int IconApplication = 0;
private const int IconMeeting = 2;
namespace Greenshot.Plugin.Office.Destinations
{
/// <summary>
/// Description of OutlookDestination.
/// </summary>
public class OutlookDestination : AbstractDestination
{
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(OutlookDestination));
private const int IconApplication = 0;
private const int IconMeeting = 2;
private static readonly Image MailIcon = GreenshotResources.GetImage("Email.Image");
private static readonly OfficeConfiguration OfficeConfig = IniConfig.GetIniSection<OfficeConfiguration>();
private static readonly string ExePath;
private static readonly bool IsActiveFlag;
private const string MapiClient = "Microsoft Outlook";
private readonly string _outlookInspectorCaption;
private readonly OlObjectClass _outlookInspectorType;
private readonly OutlookEmailExporter _outlookEmailExporter = new();
private static readonly Image MailIcon = GreenshotResources.GetImage("Email.Image");
private static readonly OfficeConfiguration OfficeConfig = IniConfig.GetIniSection<OfficeConfiguration>();
private static readonly string ExePath;
private static readonly bool IsActiveFlag;
private const string MapiClient = "Microsoft Outlook";
private readonly string _outlookInspectorCaption;
private readonly OlObjectClass _outlookInspectorType;
private readonly OutlookEmailExporter _outlookEmailExporter = new();
static OutlookDestination() {
if (HasOutlook()) {
IsActiveFlag = true;
}
ExePath = PluginUtils.GetExePath("OUTLOOK.EXE");
if (ExePath != null && File.Exists(ExePath)) {
WindowDetails.AddProcessToExcludeFromFreeze("outlook");
} else {
ExePath = GetOutlookExePath();
}
if (ExePath == null) {
IsActiveFlag = false;
}
}
static OutlookDestination()
{
if (HasOutlook())
{
IsActiveFlag = true;
}
ExePath = PluginUtils.GetExePath("OUTLOOK.EXE");
if (ExePath != null && File.Exists(ExePath))
{
WindowDetails.AddProcessToExcludeFromFreeze("outlook");
}
else
{
ExePath = GetOutlookExePath();
}
if (ExePath == null)
{
IsActiveFlag = false;
}
}
private static string GetOutlookExePath() => RegistryHive.LocalMachine.ReadKey64Or32(@"Microsoft\Windows\CurrentVersion\App Paths\OUTLOOK.EXE");
private static string GetOutlookExePath() => RegistryHive.LocalMachine.ReadKey64Or32(@"Microsoft\Windows\CurrentVersion\App Paths\OUTLOOK.EXE");
/// <summary>
/// Check if Outlook is installed
/// </summary>
/// <returns>Returns true if outlook is installed</returns>
private static bool HasOutlook()
{
string outlookPath = GetOutlookExePath();
if (outlookPath == null)
{
return false;
}
return File.Exists(outlookPath);
}
/// <summary>
/// Check if Outlook is installed
/// </summary>
/// <returns>Returns true if outlook is installed</returns>
private static bool HasOutlook()
{
string outlookPath = GetOutlookExePath();
if (outlookPath == null)
{
return false;
}
public OutlookDestination() {
}
return File.Exists(outlookPath);
}
public OutlookDestination(string outlookInspectorCaption, OlObjectClass outlookInspectorType) {
_outlookInspectorCaption = outlookInspectorCaption;
_outlookInspectorType = outlookInspectorType;
}
public OutlookDestination()
{
}
public override string Designation => "Outlook";
public OutlookDestination(string outlookInspectorCaption, OlObjectClass outlookInspectorType)
{
_outlookInspectorCaption = outlookInspectorCaption;
_outlookInspectorType = outlookInspectorType;
}
public override string Description => _outlookInspectorCaption ?? MapiClient;
public override string Designation => "Outlook";
public override int Priority => 3;
public override string Description => _outlookInspectorCaption ?? MapiClient;
public override bool IsActive => base.IsActive && IsActiveFlag;
public override int Priority => 3;
public override bool IsDynamic => true;
public override bool IsActive => base.IsActive && IsActiveFlag;
public override Keys EditorShortcutKeys => Keys.Control | Keys.E;
public override bool IsDynamic => true;
public override Image DisplayIcon {
get
{
if (_outlookInspectorCaption == null)
{
return PluginUtils.GetCachedExeIcon(ExePath, IconApplication);
}
if (OlObjectClass.olAppointment.Equals(_outlookInspectorType)) {
// Make sure we loaded the icon, maybe the configuration has been changed!
return PluginUtils.GetCachedExeIcon(ExePath, IconMeeting);
}
return MailIcon;
}
}
public override Keys EditorShortcutKeys => Keys.Control | Keys.E;
public override IEnumerable<IDestination> DynamicDestinations() {
IDictionary<string, OlObjectClass> inspectorCaptions = _outlookEmailExporter.RetrievePossibleTargets();
if (inspectorCaptions != null) {
foreach (string inspectorCaption in inspectorCaptions.Keys) {
yield return new OutlookDestination(inspectorCaption, inspectorCaptions[inspectorCaption]);
}
}
}
public override Image DisplayIcon
{
get
{
if (_outlookInspectorCaption == null)
{
return PluginUtils.GetCachedExeIcon(ExePath, IconApplication);
}
/// <summary>
/// Export the capture to outlook
/// </summary>
/// <param name="manuallyInitiated"></param>
/// <param name="surface"></param>
/// <param name="captureDetails"></param>
/// <returns></returns>
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) {
ExportInformation exportInformation = new ExportInformation(Designation, Description);
// Outlook logic
string tmpFile = captureDetails.Filename;
if (tmpFile == null || surface.Modified || !Regex.IsMatch(tmpFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$")) {
tmpFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat());
} else {
Log.InfoFormat("Using already available file: {0}", tmpFile);
}
if (OlObjectClass.olAppointment.Equals(_outlookInspectorType))
{
// Make sure we loaded the icon, maybe the configuration has been changed!
return PluginUtils.GetCachedExeIcon(ExePath, IconMeeting);
}
// Create a attachment name for the image
string attachmentName = captureDetails.Title;
if (!string.IsNullOrEmpty(attachmentName)) {
attachmentName = attachmentName.Trim();
}
// Set default if non is set
if (string.IsNullOrEmpty(attachmentName)) {
attachmentName = "Greenshot Capture";
}
// Make sure it's "clean" so it doesn't corrupt the header
attachmentName = Regex.Replace(attachmentName, @"[^\x20\d\w]", string.Empty);
return MailIcon;
}
}
if (_outlookInspectorCaption != null) {
public override IEnumerable<IDestination> DynamicDestinations()
{
IDictionary<string, OlObjectClass> inspectorCaptions = _outlookEmailExporter.RetrievePossibleTargets();
if (inspectorCaptions != null)
{
foreach (string inspectorCaption in inspectorCaptions.Keys)
{
yield return new OutlookDestination(inspectorCaption, inspectorCaptions[inspectorCaption]);
}
}
}
/// <summary>
/// Export the capture to outlook
/// </summary>
/// <param name="manuallyInitiated"></param>
/// <param name="surface"></param>
/// <param name="captureDetails"></param>
/// <returns></returns>
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails)
{
ExportInformation exportInformation = new ExportInformation(Designation, Description);
// Outlook logic
string tmpFile = captureDetails.Filename;
if (tmpFile == null || surface.Modified || !Regex.IsMatch(tmpFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$"))
{
tmpFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat());
}
else
{
Log.InfoFormat("Using already available file: {0}", tmpFile);
}
// Create a attachment name for the image
string attachmentName = captureDetails.Title;
if (!string.IsNullOrEmpty(attachmentName))
{
attachmentName = attachmentName.Trim();
}
// Set default if non is set
if (string.IsNullOrEmpty(attachmentName))
{
attachmentName = "Greenshot Capture";
}
// Make sure it's "clean" so it doesn't corrupt the header
attachmentName = Regex.Replace(attachmentName, @"[^\x20\d\w]", string.Empty);
if (_outlookInspectorCaption != null)
{
_outlookEmailExporter.ExportToInspector(_outlookInspectorCaption, tmpFile, attachmentName);
exportInformation.ExportMade = true;
} else {
if (!manuallyInitiated) {
var inspectorCaptions = _outlookEmailExporter.RetrievePossibleTargets();
if (inspectorCaptions != null && inspectorCaptions.Count > 0) {
var destinations = new List<IDestination>
{
new OutlookDestination()
};
foreach (string inspectorCaption in inspectorCaptions.Keys) {
destinations.Add(new OutlookDestination(inspectorCaption, inspectorCaptions[inspectorCaption]));
}
// Return the ExportInformation from the picker without processing, as this indirectly comes from us self
return ShowPickerMenu(false, surface, captureDetails, destinations);
}
} else {
exportInformation.ExportMade = _outlookEmailExporter.ExportToOutlook(OfficeConfig.OutlookEmailFormat, tmpFile, FilenameHelper.FillPattern(OfficeConfig.EmailSubjectPattern, captureDetails, false), attachmentName, OfficeConfig.EmailTo, OfficeConfig.EmailCC, OfficeConfig.EmailBCC, null);
}
}
ProcessExport(exportInformation, surface);
return exportInformation;
}
}
exportInformation.ExportMade = true;
}
else
{
if (!manuallyInitiated)
{
var inspectorCaptions = _outlookEmailExporter.RetrievePossibleTargets();
if (inspectorCaptions != null && inspectorCaptions.Count > 0)
{
var destinations = new List<IDestination>
{
new OutlookDestination()
};
foreach (string inspectorCaption in inspectorCaptions.Keys)
{
destinations.Add(new OutlookDestination(inspectorCaption, inspectorCaptions[inspectorCaption]));
}
// Return the ExportInformation from the picker without processing, as this indirectly comes from us self
return ShowPickerMenu(false, surface, captureDetails, destinations);
}
}
else
{
exportInformation.ExportMade = _outlookEmailExporter.ExportToOutlook(OfficeConfig.OutlookEmailFormat, tmpFile,
FilenameHelper.FillPattern(OfficeConfig.EmailSubjectPattern, captureDetails, false), attachmentName, OfficeConfig.EmailTo, OfficeConfig.EmailCC,
OfficeConfig.EmailBCC, null);
}
}
ProcessExport(exportInformation, surface);
return exportInformation;
}
}
}

View file

@ -29,95 +29,127 @@ using GreenshotPlugin.Core;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
namespace Greenshot.Plugin.Office.Destinations {
/// <summary>
/// Description of PowerpointDestination.
/// </summary>
public class PowerpointDestination : AbstractDestination {
private const int IconApplication = 0;
private const int IconPresentation = 1;
namespace Greenshot.Plugin.Office.Destinations
{
/// <summary>
/// Description of PowerpointDestination.
/// </summary>
public class PowerpointDestination : AbstractDestination
{
private const int IconApplication = 0;
private const int IconPresentation = 1;
private static readonly string ExePath;
private readonly string _presentationName;
private readonly PowerpointExporter _powerpointExporter = new PowerpointExporter();
private static readonly string ExePath;
private readonly string _presentationName;
private readonly PowerpointExporter _powerpointExporter = new PowerpointExporter();
static PowerpointDestination() {
ExePath = PluginUtils.GetExePath("POWERPNT.EXE");
if (ExePath != null && File.Exists(ExePath)) {
WindowDetails.AddProcessToExcludeFromFreeze("powerpnt");
} else {
ExePath = null;
}
}
static PowerpointDestination()
{
ExePath = PluginUtils.GetExePath("POWERPNT.EXE");
if (ExePath != null && File.Exists(ExePath))
{
WindowDetails.AddProcessToExcludeFromFreeze("powerpnt");
}
else
{
ExePath = null;
}
}
public PowerpointDestination() {
}
public PowerpointDestination()
{
}
public PowerpointDestination(string presentationName) {
_presentationName = presentationName;
}
public PowerpointDestination(string presentationName)
{
_presentationName = presentationName;
}
public override string Designation => "Powerpoint";
public override string Designation => "Powerpoint";
public override string Description {
get
{
if (_presentationName == null) {
return "Microsoft Powerpoint";
}
return _presentationName;
}
}
public override string Description
{
get
{
if (_presentationName == null)
{
return "Microsoft Powerpoint";
}
public override int Priority => 4;
return _presentationName;
}
}
public override bool IsDynamic => true;
public override int Priority => 4;
public override bool IsActive => base.IsActive && ExePath != null;
public override bool IsDynamic => true;
public override Image DisplayIcon {
get {
if (!string.IsNullOrEmpty(_presentationName)) {
return PluginUtils.GetCachedExeIcon(ExePath, IconPresentation);
}
public override bool IsActive => base.IsActive && ExePath != null;
return PluginUtils.GetCachedExeIcon(ExePath, IconApplication);
}
}
public override Image DisplayIcon
{
get
{
if (!string.IsNullOrEmpty(_presentationName))
{
return PluginUtils.GetCachedExeIcon(ExePath, IconPresentation);
}
public override IEnumerable<IDestination> DynamicDestinations() {
foreach (string presentationName in _powerpointExporter.GetPowerpointPresentations()) {
yield return new PowerpointDestination(presentationName);
}
}
return PluginUtils.GetCachedExeIcon(ExePath, IconApplication);
}
}
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) {
ExportInformation exportInformation = new ExportInformation(Designation, Description);
string tmpFile = captureDetails.Filename;
Size imageSize = Size.Empty;
if (tmpFile == null || surface.Modified || !Regex.IsMatch(tmpFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$")) {
tmpFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat());
imageSize = surface.Image.Size;
}
if (_presentationName != null) {
exportInformation.ExportMade = _powerpointExporter.ExportToPresentation(_presentationName, tmpFile, imageSize, captureDetails.Title);
} else {
if (!manuallyInitiated) {
var presentations = _powerpointExporter.GetPowerpointPresentations().ToList();
if (presentations != null && presentations.Count > 0) {
var destinations = new List<IDestination> {new PowerpointDestination()};
foreach (string presentation in presentations) {
destinations.Add(new PowerpointDestination(presentation));
}
// Return the ExportInformation from the picker without processing, as this indirectly comes from us self
return ShowPickerMenu(false, surface, captureDetails, destinations);
}
} else if (!exportInformation.ExportMade) {
exportInformation.ExportMade = _powerpointExporter.InsertIntoNewPresentation(tmpFile, imageSize, captureDetails.Title);
}
}
ProcessExport(exportInformation, surface);
return exportInformation;
}
}
public override IEnumerable<IDestination> DynamicDestinations()
{
foreach (string presentationName in _powerpointExporter.GetPowerpointPresentations())
{
yield return new PowerpointDestination(presentationName);
}
}
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails)
{
ExportInformation exportInformation = new ExportInformation(Designation, Description);
string tmpFile = captureDetails.Filename;
Size imageSize = Size.Empty;
if (tmpFile == null || surface.Modified || !Regex.IsMatch(tmpFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$"))
{
tmpFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat());
imageSize = surface.Image.Size;
}
if (_presentationName != null)
{
exportInformation.ExportMade = _powerpointExporter.ExportToPresentation(_presentationName, tmpFile, imageSize, captureDetails.Title);
}
else
{
if (!manuallyInitiated)
{
var presentations = _powerpointExporter.GetPowerpointPresentations().ToList();
if (presentations != null && presentations.Count > 0)
{
var destinations = new List<IDestination>
{
new PowerpointDestination()
};
foreach (string presentation in presentations)
{
destinations.Add(new PowerpointDestination(presentation));
}
// Return the ExportInformation from the picker without processing, as this indirectly comes from us self
return ShowPickerMenu(false, surface, captureDetails, destinations);
}
}
else if (!exportInformation.ExportMade)
{
exportInformation.ExportMade = _powerpointExporter.InsertIntoNewPresentation(tmpFile, imageSize, captureDetails.Title);
}
}
ProcessExport(exportInformation, surface);
return exportInformation;
}
}
}

View file

@ -30,102 +30,134 @@ using GreenshotPlugin.Core;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
namespace Greenshot.Plugin.Office.Destinations {
/// <summary>
/// Description of EmailDestination.
/// </summary>
public class WordDestination : AbstractDestination {
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(WordDestination));
private const int IconApplication = 0;
private const int IconDocument = 1;
private static readonly string ExePath;
private readonly string _documentCaption;
namespace Greenshot.Plugin.Office.Destinations
{
/// <summary>
/// Description of EmailDestination.
/// </summary>
public class WordDestination : AbstractDestination
{
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(WordDestination));
private const int IconApplication = 0;
private const int IconDocument = 1;
private static readonly string ExePath;
private readonly string _documentCaption;
private readonly WordExporter _wordExporter = new WordExporter();
static WordDestination() {
ExePath = PluginUtils.GetExePath("WINWORD.EXE");
if (ExePath != null && !File.Exists(ExePath)) {
ExePath = null;
}
}
public WordDestination() {
static WordDestination()
{
ExePath = PluginUtils.GetExePath("WINWORD.EXE");
if (ExePath != null && !File.Exists(ExePath))
{
ExePath = null;
}
}
}
public WordDestination()
{
}
public WordDestination(string wordCaption) {
_documentCaption = wordCaption;
}
public WordDestination(string wordCaption)
{
_documentCaption = wordCaption;
}
public override string Designation => "Word";
public override string Designation => "Word";
public override string Description => _documentCaption ?? "Microsoft Word";
public override string Description => _documentCaption ?? "Microsoft Word";
public override int Priority => 4;
public override int Priority => 4;
public override bool IsDynamic => true;
public override bool IsDynamic => true;
public override bool IsActive => base.IsActive && ExePath != null;
public override bool IsActive => base.IsActive && ExePath != null;
public override Image DisplayIcon => PluginUtils.GetCachedExeIcon(ExePath, !string.IsNullOrEmpty(_documentCaption) ? IconDocument : IconApplication);
public override Image DisplayIcon => PluginUtils.GetCachedExeIcon(ExePath, !string.IsNullOrEmpty(_documentCaption) ? IconDocument : IconApplication);
public override IEnumerable<IDestination> DynamicDestinations() {
foreach (string wordCaption in _wordExporter.GetWordDocuments()) {
yield return new WordDestination(wordCaption);
}
}
public override IEnumerable<IDestination> DynamicDestinations()
{
foreach (string wordCaption in _wordExporter.GetWordDocuments())
{
yield return new WordDestination(wordCaption);
}
}
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) {
ExportInformation exportInformation = new ExportInformation(Designation, Description);
string tmpFile = captureDetails.Filename;
if (tmpFile == null || surface.Modified || !Regex.IsMatch(tmpFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$")) {
tmpFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat());
}
if (_documentCaption != null) {
try {
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails)
{
ExportInformation exportInformation = new ExportInformation(Designation, Description);
string tmpFile = captureDetails.Filename;
if (tmpFile == null || surface.Modified || !Regex.IsMatch(tmpFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$"))
{
tmpFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat());
}
if (_documentCaption != null)
{
try
{
_wordExporter.InsertIntoExistingDocument(_documentCaption, tmpFile);
exportInformation.ExportMade = true;
} catch (Exception) {
try {
exportInformation.ExportMade = true;
}
catch (Exception)
{
try
{
_wordExporter.InsertIntoExistingDocument(_documentCaption, tmpFile);
exportInformation.ExportMade = true;
} catch (Exception ex) {
Log.Error(ex);
// TODO: Change to general logic in ProcessExport
surface.SendMessageEvent(this, SurfaceMessageTyp.Error, Language.GetFormattedString("destination_exportfailed", Description));
}
}
} else {
if (!manuallyInitiated) {
var documents = _wordExporter.GetWordDocuments().ToList();
if (documents != null && documents.Count > 0) {
var destinations = new List<IDestination>
{
new WordDestination()
};
foreach (string document in documents) {
destinations.Add(new WordDestination(document));
}
// Return the ExportInformation from the picker without processing, as this indirectly comes from us self
return ShowPickerMenu(false, surface, captureDetails, destinations);
}
}
try {
exportInformation.ExportMade = true;
}
catch (Exception ex)
{
Log.Error(ex);
// TODO: Change to general logic in ProcessExport
surface.SendMessageEvent(this, SurfaceMessageTyp.Error, Language.GetFormattedString("destination_exportfailed", Description));
}
}
}
else
{
if (!manuallyInitiated)
{
var documents = _wordExporter.GetWordDocuments().ToList();
if (documents != null && documents.Count > 0)
{
var destinations = new List<IDestination>
{
new WordDestination()
};
foreach (string document in documents)
{
destinations.Add(new WordDestination(document));
}
// Return the ExportInformation from the picker without processing, as this indirectly comes from us self
return ShowPickerMenu(false, surface, captureDetails, destinations);
}
}
try
{
_wordExporter.InsertIntoNewDocument(tmpFile, null, null);
exportInformation.ExportMade = true;
} catch(Exception) {
// Retry once, just in case
try {
exportInformation.ExportMade = true;
}
catch (Exception)
{
// Retry once, just in case
try
{
_wordExporter.InsertIntoNewDocument(tmpFile, null, null);
exportInformation.ExportMade = true;
} catch (Exception ex) {
Log.Error(ex);
// TODO: Change to general logic in ProcessExport
surface.SendMessageEvent(this, SurfaceMessageTyp.Error, Language.GetFormattedString("destination_exportfailed", Description));
}
}
}
ProcessExport(exportInformation, surface);
return exportInformation;
}
}
exportInformation.ExportMade = true;
}
catch (Exception ex)
{
Log.Error(ex);
// TODO: Change to general logic in ProcessExport
surface.SendMessageEvent(this, SurfaceMessageTyp.Error, Language.GetFormattedString("destination_exportfailed", Description));
}
}
}
ProcessExport(exportInformation, surface);
return exportInformation;
}
}
}

View file

@ -23,32 +23,40 @@ using Greenshot.Plugin.Office.OfficeInterop;
using GreenshotPlugin.IniFile;
using Microsoft.Office.Interop.PowerPoint;
namespace Greenshot.Plugin.Office {
namespace Greenshot.Plugin.Office
{
/// <summary>
/// Description of CoreConfiguration.
/// </summary>
[IniSection("Office", Description = "Greenshot Office configuration")]
public class OfficeConfiguration : IniSection
{
[IniProperty("OutlookEmailFormat", Description = "Default type for emails. (Text, HTML)", DefaultValue = "HTML")]
public EmailFormat OutlookEmailFormat { get; set; }
/// <summary>
/// Description of CoreConfiguration.
/// </summary>
[IniSection("Office", Description="Greenshot Office configuration")]
public class OfficeConfiguration : IniSection {
[IniProperty("OutlookEmailFormat", Description = "Default type for emails. (Text, HTML)", DefaultValue = "HTML")]
public EmailFormat OutlookEmailFormat { get; set; }
[IniProperty("EmailSubjectPattern", Description = "Email subject pattern, works like the OutputFileFilenamePattern", DefaultValue = "${title}")]
public string EmailSubjectPattern { get; set; }
[IniProperty("EmailSubjectPattern", Description = "Email subject pattern, works like the OutputFileFilenamePattern", DefaultValue = "${title}")]
public string EmailSubjectPattern { get; set; }
[IniProperty("EmailTo", Description = "Default value for the to in emails that are created", DefaultValue = "")]
public string EmailTo { get; set; }
[IniProperty("EmailCC", Description = "Default value for the CC in emails that are created", DefaultValue = "")]
public string EmailCC { get; set; }
[IniProperty("EmailBCC", Description = "Default value for the BCC in emails that are created", DefaultValue = "")]
public string EmailBCC { get; set; }
[IniProperty("OutlookAllowExportInMeetings", Description = "For Outlook: Allow export in meeting items", DefaultValue = "False")]
public bool OutlookAllowExportInMeetings { get; set; }
[IniProperty("WordLockAspectRatio", Description = "For Word: Lock the aspect ratio of the image", DefaultValue = "True")]
public bool WordLockAspectRatio { get; set; }
[IniProperty("PowerpointLockAspectRatio", Description = "For Powerpoint: Lock the aspect ratio of the image", DefaultValue = "True")]
public bool PowerpointLockAspectRatio { get; set; }
[IniProperty("PowerpointSlideLayout", Description = "For Powerpoint: Slide layout, changing this to a wrong value will fallback on ppLayoutBlank!!", DefaultValue = "ppLayoutPictureWithCaption")]
public PpSlideLayout PowerpointSlideLayout { get; set; }
[IniProperty("EmailTo", Description = "Default value for the to in emails that are created", DefaultValue = "")]
public string EmailTo { get; set; }
}
[IniProperty("EmailCC", Description = "Default value for the CC in emails that are created", DefaultValue = "")]
public string EmailCC { get; set; }
[IniProperty("EmailBCC", Description = "Default value for the BCC in emails that are created", DefaultValue = "")]
public string EmailBCC { get; set; }
[IniProperty("OutlookAllowExportInMeetings", Description = "For Outlook: Allow export in meeting items", DefaultValue = "False")]
public bool OutlookAllowExportInMeetings { get; set; }
[IniProperty("WordLockAspectRatio", Description = "For Word: Lock the aspect ratio of the image", DefaultValue = "True")]
public bool WordLockAspectRatio { get; set; }
[IniProperty("PowerpointLockAspectRatio", Description = "For Powerpoint: Lock the aspect ratio of the image", DefaultValue = "True")]
public bool PowerpointLockAspectRatio { get; set; }
[IniProperty("PowerpointSlideLayout", Description = "For Powerpoint: Slide layout, changing this to a wrong value will fallback on ppLayoutBlank!!",
DefaultValue = "ppLayoutPictureWithCaption")]
public PpSlideLayout PowerpointSlideLayout { get; set; }
}
}

View file

@ -34,6 +34,7 @@ namespace Greenshot.Plugin.Office.OfficeExport.Entities
{
return string.Format("{0} / {1}", Parent.Name, Name);
}
return string.Format("{0} / {1} / {2}", Parent.Parent.Name, Parent.Name, Name);
}
}

View file

@ -53,10 +53,12 @@ namespace Greenshot.Plugin.Office.OfficeExport
// Ignore, probably no excel running
return null;
}
if (excelApplication?.ComObject != null)
{
InitializeVariables(excelApplication);
}
return excelApplication;
}
@ -71,6 +73,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
excelApplication = DisposableCom.Create(new Application());
}
InitializeVariables(excelApplication);
return excelApplication;
}
@ -108,10 +111,11 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
return;
}
if (!Version.TryParse(excelApplication.ComObject.Version, out _excelVersion))
{
LOG.Warn("Assuming Excel version 1997.");
_excelVersion = new Version((int)OfficeVersions.Office97, 0, 0, 0);
_excelVersion = new Version((int) OfficeVersions.Office97, 0, 0, 0);
}
}
@ -132,7 +136,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
using var workbooks = DisposableCom.Create(excelApplication.ComObject.Workbooks);
for (int i = 1; i <= workbooks.ComObject.Count; i++)
{
using var workbook = DisposableCom.Create((_Workbook)workbooks.ComObject[i]);
using var workbook = DisposableCom.Create((_Workbook) workbooks.ComObject[i]);
if ((workbook != null) && workbook.ComObject.Name.StartsWith(workbookName))
{
InsertIntoExistingWorkbook(workbook, tmpFile, imageSize);
@ -191,9 +195,8 @@ namespace Greenshot.Plugin.Office.OfficeExport
excelApplication.ComObject.Visible = true;
using var workbooks = DisposableCom.Create(excelApplication.ComObject.Workbooks);
using var workbook = DisposableCom.Create((_Workbook)workbooks.ComObject.Add());
using var workbook = DisposableCom.Create((_Workbook) workbooks.ComObject.Add());
InsertIntoExistingWorkbook(workbook, tmpFile, imageSize);
}
}
}

View file

@ -38,7 +38,10 @@ namespace Greenshot.Plugin.Office.OfficeExport
public class OneNoteExporter
{
private const string XmlImageContent = "<one:Image format=\"png\"><one:Size width=\"{1}.0\" height=\"{2}.0\" isSetByUser=\"true\" /><one:Data>{0}</one:Data></one:Image>";
private const string XmlOutline = "<?xml version=\"1.0\"?><one:Page xmlns:one=\"{2}\" ID=\"{1}\"><one:Title><one:OE><one:T><![CDATA[{3}]]></one:T></one:OE></one:Title>{0}</one:Page>";
private const string XmlOutline =
"<?xml version=\"1.0\"?><one:Page xmlns:one=\"{2}\" ID=\"{1}\"><one:Title><one:OE><one:T><![CDATA[{3}]]></one:T></one:OE></one:Title>{0}</one:Page>";
private const string OnenoteNamespace2010 = "http://schemas.microsoft.com/office/onenote/2010/onenote";
private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(OneNoteExporter));
@ -107,6 +110,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
LOG.Warn("Unable to navigate to the target page", ex);
}
return true;
}
@ -126,6 +130,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
// Ignore, probably no OneNote running
return null;
}
return oneNoteApplication;
}
@ -140,6 +145,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
oneNoteApplication = DisposableCom.Create(new Application());
}
return oneNoteApplication;
}
@ -183,6 +189,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
};
}
}
if ("one:Section".Equals(xmlReader.Name))
{
string id = xmlReader.GetAttribute("ID");
@ -196,6 +203,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
};
}
}
if ("one:Page".Equals(xmlReader.Name))
{
// Skip deleted items
@ -214,6 +222,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
continue;
}
page.IsCurrentlyViewed = "true".Equals(xmlReader.GetAttribute("isCurrentlyViewed"));
pages.Add(page);
}
@ -231,22 +240,26 @@ namespace Greenshot.Plugin.Office.OfficeExport
}
catch (COMException cEx)
{
if (cEx.ErrorCode == unchecked((int)0x8002801D))
if (cEx.ErrorCode == unchecked((int) 0x8002801D))
{
LOG.Warn("Wrong registry keys, to solve this remove the OneNote key as described here: http://microsoftmercenary.com/wp/outlook-excel-interop-calls-breaking-solved/");
LOG.Warn(
"Wrong registry keys, to solve this remove the OneNote key as described here: http://microsoftmercenary.com/wp/outlook-excel-interop-calls-breaking-solved/");
}
LOG.Warn("Problem retrieving onenote destinations, ignoring: ", cEx);
}
catch (Exception ex)
{
LOG.Warn("Problem retrieving onenote destinations, ignoring: ", ex);
}
pages.Sort((page1, page2) =>
{
if (page1.IsCurrentlyViewed || page2.IsCurrentlyViewed)
{
return page2.IsCurrentlyViewed.CompareTo(page1.IsCurrentlyViewed);
}
return string.Compare(page1.DisplayName, page2.DisplayName, StringComparison.Ordinal);
});
return pages;
@ -264,6 +277,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
return null;
}
// ReSharper disable once RedundantAssignment
string unfiledNotesPath = "";
oneNoteApplication.ComObject.GetSpecialLocation(specialLocation, out unfiledNotesPath);
@ -285,6 +299,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
continue;
}
string id = xmlReader.GetAttribute("ID");
string path = xmlReader.GetAttribute("path");
if (unfiledNotesPath.Equals(path))
@ -301,6 +316,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
}
}
}
return null;
}
}

View file

@ -47,7 +47,9 @@ namespace Greenshot.Plugin.Office.OfficeExport
private const string ProfilesKey = @"Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles\";
private const string AccountKey = "9375CFF0413111d3B88A00104B2A6676";
private const string NewSignatureValue = "New Signature";
private const string DefaultProfileValue = "DefaultProfile";
// Schema definitions for the MAPI properties, see: http://msdn.microsoft.com/en-us/library/aa454438.aspx and: http://msdn.microsoft.com/en-us/library/bb446117.aspx
private const string AttachmentContentId = @"http://schemas.microsoft.com/mapi/proptag/0x3712001E";
@ -73,10 +75,10 @@ namespace Greenshot.Plugin.Office.OfficeExport
}
// The activeexplorer inline response only works with >= 2013, Microsoft Outlook 15.0 Object Library
if (_outlookVersion.Major >= (int)OfficeVersions.Office2013)
if (_outlookVersion.Major >= (int) OfficeVersions.Office2013)
{
// Check inline "panel" for Outlook 2013
using var activeExplorer = DisposableCom.Create((_Explorer)outlookApplication.ComObject.ActiveExplorer());
using var activeExplorer = DisposableCom.Create((_Explorer) outlookApplication.ComObject.ActiveExplorer());
// Only if we have one and if the capture is the one we selected
if ((activeExplorer != null) && activeExplorer.ComObject.Caption.StartsWith(inspectorCaption))
{
@ -90,15 +92,17 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
return ExportToInspector(null, activeExplorer, mailItem.Class, mailItem, tmpFile, attachmentName);
}
break;
case AppointmentItem appointmentItem:
if ((_outlookVersion.Major >= (int)OfficeVersions.Office2010) && _officeConfiguration.OutlookAllowExportInMeetings)
if ((_outlookVersion.Major >= (int) OfficeVersions.Office2010) && _officeConfiguration.OutlookAllowExportInMeetings)
{
if (!string.IsNullOrEmpty(appointmentItem.Organizer) && appointmentItem.Organizer.Equals(_currentUser))
{
return ExportToInspector(null, activeExplorer, appointmentItem.Class, null, tmpFile, attachmentName);
}
}
break;
}
}
@ -110,10 +114,11 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
return false;
}
LOG.DebugFormat("Got {0} inspectors to check", inspectors.ComObject.Count);
for (int i = 1; i <= inspectors.ComObject.Count; i++)
{
using var inspector = DisposableCom.Create((_Inspector)inspectors.ComObject[i]);
using var inspector = DisposableCom.Create((_Inspector) inspectors.ComObject[i]);
string currentCaption = inspector.ComObject.Caption;
if (!currentCaption.StartsWith(inspectorCaption))
{
@ -130,6 +135,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
continue;
}
try
{
return ExportToInspector(inspector, null, mailItem.Class, mailItem, tmpFile, attachmentName);
@ -138,9 +144,10 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
LOG.Error($"Export to {currentCaption} failed.", exExport);
}
break;
case AppointmentItem appointmentItem:
if ((_outlookVersion.Major >= (int)OfficeVersions.Office2010) && _officeConfiguration.OutlookAllowExportInMeetings)
if ((_outlookVersion.Major >= (int) OfficeVersions.Office2010) && _officeConfiguration.OutlookAllowExportInMeetings)
{
if (!string.IsNullOrEmpty(appointmentItem.Organizer) && !appointmentItem.Organizer.Equals(_currentUser))
{
@ -153,6 +160,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
// skip, can't export to olAppointment
continue;
}
try
{
return ExportToInspector(inspector, null, appointmentItem.Class, null, tmpFile, attachmentName);
@ -161,6 +169,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
LOG.Error($"Export to {currentCaption} failed.", exExport);
}
break;
default:
continue;
@ -168,6 +177,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
}
}
}
return false;
}
@ -181,7 +191,8 @@ namespace Greenshot.Plugin.Office.OfficeExport
/// <param name="explorer"></param>
/// <param name="itemClass"></param>
/// <returns></returns>
private bool ExportToInspector(IDisposableCom<_Inspector> inspector, IDisposableCom<_Explorer> explorer, OlObjectClass itemClass, MailItem mailItem, string tmpFile, string attachmentName)
private bool ExportToInspector(IDisposableCom<_Inspector> inspector, IDisposableCom<_Explorer> explorer, OlObjectClass itemClass, MailItem mailItem, string tmpFile,
string attachmentName)
{
bool isMail = OlObjectClass.olMail.Equals(itemClass);
bool isAppointment = OlObjectClass.olAppointment.Equals(itemClass);
@ -190,6 +201,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
LOG.Warn("Item is no mail or appointment.");
return false;
}
try
{
// Make sure the inspector is activated, only this way the word editor is active!
@ -200,25 +212,27 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
isTextFormat = OlBodyFormat.olFormatPlain.Equals(mailItem.BodyFormat);
}
if (isAppointment || !isTextFormat)
{
// Check for wordmail, if so use the wordexporter
// http://msdn.microsoft.com/en-us/library/dd492012%28v=office.12%29.aspx
// Earlier versions of Outlook also supported an Inspector.HTMLEditor object property, but since Internet Explorer is no longer the rendering engine for HTML messages and posts, HTMLEditor is no longer supported.
IDisposableCom<_Document> wordDocument = null;
if ((explorer != null) && (_outlookVersion.Major >= (int)OfficeVersions.Office2013))
if ((explorer != null) && (_outlookVersion.Major >= (int) OfficeVersions.Office2013))
{
// TODO: Needs to have the Microsoft Outlook 15.0 Object Library installed
wordDocument = DisposableCom.Create((_Document)explorer.ComObject.ActiveInlineResponseWordEditor);
wordDocument = DisposableCom.Create((_Document) explorer.ComObject.ActiveInlineResponseWordEditor);
}
else if (inspector != null)
{
if (inspector.ComObject.IsWordMail() && (inspector.ComObject.EditorType == OlEditorType.olEditorWord))
{
var tmpWordDocument = (_Document)inspector.ComObject.WordEditor;
var tmpWordDocument = (_Document) inspector.ComObject.WordEditor;
wordDocument = DisposableCom.Create(tmpWordDocument);
}
}
if (wordDocument != null)
{
using (wordDocument)
@ -248,6 +262,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
LOG.Info("Trying export for outlook < 2007.");
}
}
// Only use mailitem as it should be filled!!
if (mailItem != null)
{
@ -255,7 +270,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
}
string contentId;
if (_outlookVersion.Major >= (int)OfficeVersions.Office2007)
if (_outlookVersion.Major >= (int) OfficeVersions.Office2007)
{
contentId = Guid.NewGuid().ToString();
}
@ -283,7 +298,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
var selection = document2.selection;
if (selection != null)
{
var range = (IHTMLTxtRange)selection.createRange();
var range = (IHTMLTxtRange) selection.createRange();
if (range != null)
{
// First paste, than attach (otherwise the range is wrong!)
@ -315,7 +330,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
// Create the attachment (if inlined the attachment isn't visible as attachment!)
using var attachments = DisposableCom.Create(mailItem.Attachments);
using var attachment = DisposableCom.Create(attachments.ComObject.Add(tmpFile, OlAttachmentType.olByValue, inlinePossible ? 0 : 1, attachmentName));
if (_outlookVersion.Major >= (int)OfficeVersions.Office2007)
if (_outlookVersion.Major >= (int) OfficeVersions.Office2007)
{
// Add the content id to the attachment, this only works for Outlook >= 2007
try
@ -341,9 +356,11 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
caption = explorer.ComObject.Caption;
}
LOG.Warn($"Problem while trying to add attachment to Item '{caption}'", ex);
return false;
}
try
{
if (inspector != null)
@ -360,6 +377,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
LOG.Warn("Problem activating inspector/explorer: ", ex);
return false;
}
LOG.Debug("Finished!");
return true;
}
@ -376,27 +394,32 @@ namespace Greenshot.Plugin.Office.OfficeExport
/// <param name="cc"></param>
/// <param name="bcc"></param>
/// <param name="url"></param>
private void ExportToNewEmail(IDisposableCom<Application> outlookApplication, EmailFormat format, string tmpFile, string subject, string attachmentName, string to, string cc, string bcc, string url)
private void ExportToNewEmail(IDisposableCom<Application> outlookApplication, EmailFormat format, string tmpFile, string subject, string attachmentName, string to,
string cc, string bcc, string url)
{
using var newItem = DisposableCom.Create((MailItem)outlookApplication.ComObject.CreateItem(OlItemType.olMailItem));
using var newItem = DisposableCom.Create((MailItem) outlookApplication.ComObject.CreateItem(OlItemType.olMailItem));
if (newItem == null)
{
return;
}
var newMail = newItem.ComObject;
newMail.Subject = subject;
if (!string.IsNullOrEmpty(to))
{
newMail.To = to;
}
if (!string.IsNullOrEmpty(cc))
{
newMail.CC = cc;
}
if (!string.IsNullOrEmpty(bcc))
{
newMail.BCC = bcc;
}
newMail.BodyFormat = OlBodyFormat.olFormatHTML;
string bodyString = null;
// Read the default signature, if nothing found use empty email
@ -408,6 +431,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
LOG.Error("Problem reading signature!", e);
}
switch (format)
{
case EmailFormat.Text:
@ -421,9 +445,11 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
bodyString = "";
}
newMail.Body = bodyString;
}
}
break;
default:
string contentId = Path.GetFileName(tmpFile);
@ -432,7 +458,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
using var attachment = DisposableCom.Create(attachments.ComObject.Add(tmpFile, OlAttachmentType.olByValue, 0, attachmentName));
// add content ID to the attachment
if (_outlookVersion.Major >= (int)OfficeVersions.Office2007)
if (_outlookVersion.Major >= (int) OfficeVersions.Office2007)
{
try
{
@ -456,6 +482,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
href = $"<A HREF=\"{url}\">";
hrefEnd = "</A>";
}
string htmlImgEmbedded = $"<BR/>{href}<IMG border=0 hspace=0 alt=\"{attachmentName}\" align=baseline src=\"cid:{contentId}\">{hrefEnd}<BR/>";
string fallbackBody = $"<HTML><BODY>{htmlImgEmbedded}</BODY></HTML>";
if (bodyString == null)
@ -482,13 +509,15 @@ namespace Greenshot.Plugin.Office.OfficeExport
bodyString = fallbackBody;
}
}
newMail.HTMLBody = bodyString;
break;
}
// So not save, otherwise the email is always stored in Draft folder.. (newMail.Save();)
newMail.Display(false);
using var inspector = DisposableCom.Create((_Inspector)newMail.GetInspector);
using var inspector = DisposableCom.Create((_Inspector) newMail.GetInspector);
if (inspector != null)
{
try
@ -528,12 +557,14 @@ namespace Greenshot.Plugin.Office.OfficeExport
exported = true;
}
}
return exported;
}
catch (Exception e)
{
LOG.Error("Error while creating an outlook mail item: ", e);
}
return exported;
}
@ -548,6 +579,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
outlookApplication = DisposableCom.Create(new Application());
}
InitializeVariables(outlookApplication);
return outlookApplication;
}
@ -568,10 +600,12 @@ namespace Greenshot.Plugin.Office.OfficeExport
// Ignore, probably no outlook running
return null;
}
if ((outlookApplication != null) && (outlookApplication.ComObject != null))
{
InitializeVariables(outlookApplication);
}
return outlookApplication;
}
@ -587,7 +621,8 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
return null;
}
string defaultProfile = (string)profilesKey.GetValue(DefaultProfileValue);
string defaultProfile = (string) profilesKey.GetValue(DefaultProfileValue);
LOG.DebugFormat("defaultProfile={0}", defaultProfile);
using var profileKey = profilesKey.OpenSubKey(defaultProfile + @"\" + AccountKey, false);
if (profileKey != null)
@ -599,19 +634,21 @@ namespace Greenshot.Plugin.Office.OfficeExport
using var numberKey = profileKey.OpenSubKey(number, false);
if (numberKey != null)
{
byte[] val = (byte[])numberKey.GetValue(NewSignatureValue);
byte[] val = (byte[]) numberKey.GetValue(NewSignatureValue);
if (val == null)
{
continue;
}
string signatureName = "";
foreach (byte b in val)
{
if (b != 0)
{
signatureName += (char)b;
signatureName += (char) b;
}
}
LOG.DebugFormat("Found email signature: {0}", signatureName);
var extension = format switch
{
@ -628,6 +665,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
}
}
}
return null;
}
@ -642,13 +680,15 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
return;
}
if (!Version.TryParse(outlookApplication.ComObject.Version, out _outlookVersion))
{
LOG.Warn("Assuming outlook version 1997.");
_outlookVersion = new Version((int)OfficeVersions.Office97, 0, 0, 0);
_outlookVersion = new Version((int) OfficeVersions.Office97, 0, 0, 0);
}
// Preventing retrieval of currentUser if Outlook is older than 2007
if (_outlookVersion.Major >= (int)OfficeVersions.Office2007)
if (_outlookVersion.Major >= (int) OfficeVersions.Office2007)
{
try
{
@ -657,6 +697,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
using var currentUser = DisposableCom.Create(mapiNamespace.ComObject.CurrentUser);
_currentUser = currentUser.ComObject.Name;
}
LOG.InfoFormat("Current user: {0}", _currentUser);
}
catch (Exception exNs)
@ -682,7 +723,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
}
// The activeexplorer inline response only works with >= 2013, Microsoft Outlook 15.0 Object Library
if (_outlookVersion.Major >= (int)OfficeVersions.Office2013)
if (_outlookVersion.Major >= (int) OfficeVersions.Office2013)
{
// Check inline "panel" for Outlook 2013
using var activeExplorer = DisposableCom.Create(outlookApplication.ComObject.ActiveExplorer());
@ -701,15 +742,17 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
inspectorCaptions.Add(caption, mailItem.Class);
}
break;
case AppointmentItem appointmentItem:
if ((_outlookVersion.Major >= (int)OfficeVersions.Office2010) && _officeConfiguration.OutlookAllowExportInMeetings)
if ((_outlookVersion.Major >= (int) OfficeVersions.Office2010) && _officeConfiguration.OutlookAllowExportInMeetings)
{
if (!string.IsNullOrEmpty(appointmentItem.Organizer) && appointmentItem.Organizer.Equals(_currentUser))
{
inspectorCaptions.Add(caption, appointmentItem.Class);
}
}
break;
}
}
@ -740,10 +783,11 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
continue;
}
inspectorCaptions.Add(caption, mailItem.Class);
break;
case AppointmentItem appointmentItem:
if ((_outlookVersion.Major >= (int)OfficeVersions.Office2010) && _officeConfiguration.OutlookAllowExportInMeetings)
if ((_outlookVersion.Major >= (int) OfficeVersions.Office2010) && _officeConfiguration.OutlookAllowExportInMeetings)
{
if (!string.IsNullOrEmpty(appointmentItem.Organizer) && !appointmentItem.Organizer.Equals(_currentUser))
{
@ -756,6 +800,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
// skip, can't export to olAppointment
continue;
}
inspectorCaptions.Add(caption, appointmentItem.Class);
break;
default:
@ -769,6 +814,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
LOG.Warn("Problem retrieving word destinations, ignoring: ", ex);
}
return inspectorCaptions;
}
}

View file

@ -60,6 +60,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
left = pageSetup.ComObject.SlideWidth / 2 - imageSize.Width / 2f;
top = pageSetup.ComObject.SlideHeight / 2 - imageSize.Height / 2f;
}
float width = imageSize.Width;
float height = imageSize.Height;
IDisposableCom<Shape> shapeForCaption = null;
@ -86,6 +87,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
shapeForLocation.ComObject.Left = left;
}
shapeForLocation.ComObject.Width = imageSize.Width;
if (height > shapeForLocation.ComObject.Height)
@ -98,6 +100,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
top = shapeForLocation.ComObject.Top + shapeForLocation.ComObject.Height / 2 - imageSize.Height / 2f;
}
shapeForLocation.ComObject.Height = imageSize.Height;
}
catch (Exception e)
@ -106,6 +109,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
using var slides = DisposableCom.Create(presentation.ComObject.Slides);
slide = DisposableCom.Create(slides.ComObject.Add(slides.ComObject.Count + 1, PpSlideLayout.ppLayoutBlank));
}
using (var shapes = DisposableCom.Create(slide.ComObject.Shapes))
{
using var shape = DisposableCom.Create(shapes.ComObject.AddPicture(tmpFile, MsoTriState.msoFalse, MsoTriState.msoTrue, 0, 0, width, height));
@ -117,20 +121,24 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
shape.ComObject.LockAspectRatio = MsoTriState.msoFalse;
}
shape.ComObject.ScaleHeight(1, MsoTriState.msoTrue, MsoScaleFrom.msoScaleFromMiddle);
shape.ComObject.ScaleWidth(1, MsoTriState.msoTrue, MsoScaleFrom.msoScaleFromMiddle);
if (hasScaledWidth)
{
shape.ComObject.Width = width;
}
if (hasScaledHeight)
{
shape.ComObject.Height = height;
}
shape.ComObject.Left = left;
shape.ComObject.Top = top;
shape.ComObject.AlternativeText = title;
}
if (shapeForCaption != null)
{
try
@ -148,6 +156,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
LOG.Warn("Problem setting the title to a text-range", ex);
}
}
// Activate/Goto the slide
try
{
@ -194,10 +203,12 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
continue;
}
if (!presentation.ComObject.Name.StartsWith(presentationName))
{
continue;
}
try
{
AddPictureToPresentation(presentation, tmpFile, imageSize, title);
@ -209,6 +220,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
}
}
}
return false;
}
@ -223,6 +235,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
powerPointApplication = DisposableCom.Create(new Application());
}
InitializeVariables(powerPointApplication);
return powerPointApplication;
}
@ -243,10 +256,12 @@ namespace Greenshot.Plugin.Office.OfficeExport
// Ignore, probably no PowerPoint running
return null;
}
if (powerPointApplication?.ComObject != null)
{
InitializeVariables(powerPointApplication);
}
return powerPointApplication;
}
@ -271,10 +286,12 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
continue;
}
if (presentation.ComObject.ReadOnly == MsoTriState.msoTrue)
{
continue;
}
if (IsAfter2003())
{
if (presentation.ComObject.Final)
@ -282,6 +299,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
continue;
}
}
yield return presentation.ComObject.Name;
}
}
@ -296,10 +314,11 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
return;
}
if (!Version.TryParse(powerpointApplication.ComObject.Version, out _powerpointVersion))
{
LOG.Warn("Assuming Powerpoint version 1997.");
_powerpointVersion = new Version((int)OfficeVersions.Office97, 0, 0, 0);
_powerpointVersion = new Version((int) OfficeVersions.Office97, 0, 0, 0);
}
}
@ -332,13 +351,13 @@ namespace Greenshot.Plugin.Office.OfficeExport
}
}
}
return isPictureAdded;
}
private bool IsAfter2003()
{
return _powerpointVersion.Major > (int)OfficeVersions.Office2003;
return _powerpointVersion.Major > (int) OfficeVersions.Office2003;
}
}
}

View file

@ -53,6 +53,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
shape.ComObject.LockAspectRatio = MsoTriState.msoTrue;
}
selection.ComObject.InsertAfter("\r\n");
selection.ComObject.MoveDown(WdUnits.wdLine, 1, Type.Missing);
return shape;
@ -69,6 +70,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
wordApplication = DisposableCom.Create(new Application());
}
InitializeVariables(wordApplication);
return wordApplication;
}
@ -89,10 +91,12 @@ namespace Greenshot.Plugin.Office.OfficeExport
// Ignore, probably no word running
return null;
}
if ((wordApplication != null) && (wordApplication.ComObject != null))
{
InitializeVariables(wordApplication);
}
return wordApplication;
}
@ -116,6 +120,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
continue;
}
if (IsAfter2003())
{
if (document.ComObject.Final)
@ -139,10 +144,11 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
return;
}
if (!Version.TryParse(wordApplication.ComObject.Version, out _wordVersion))
{
LOG.Warn("Assuming Word version 1997.");
_wordVersion = new Version((int)OfficeVersions.Office97, 0, 0, 0);
_wordVersion = new Version((int) OfficeVersions.Office97, 0, 0, 0);
}
}
@ -164,7 +170,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
using var documents = DisposableCom.Create(wordApplication.ComObject.Documents);
for (int i = 1; i <= documents.ComObject.Count; i++)
{
using var wordDocument = DisposableCom.Create((_Document)documents.ComObject[i]);
using var wordDocument = DisposableCom.Create((_Document) documents.ComObject[i]);
using var activeWindow = DisposableCom.Create(wordDocument.ComObject.ActiveWindow);
if (activeWindow.ComObject.Caption.StartsWith(wordCaption))
{
@ -172,6 +178,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
}
}
}
return false;
}
@ -184,7 +191,8 @@ namespace Greenshot.Plugin.Office.OfficeExport
/// <param name="address">string</param>
/// <param name="tooltip">string with the tooltip of the image</param>
/// <returns>bool</returns>
internal bool InsertIntoExistingDocument(IDisposableCom<Application> wordApplication, IDisposableCom<_Document> wordDocument, string tmpFile, string address, string tooltip)
internal bool InsertIntoExistingDocument(IDisposableCom<Application> wordApplication, IDisposableCom<_Document> wordDocument, string tmpFile, string address,
string tooltip)
{
// Bug #1517: image will be inserted into that document, where the focus was last. It will not inserted into the chosen one.
// Solution: Make sure the selected document is active, otherwise the insert will be made in a different document!
@ -204,6 +212,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
LOG.InfoFormat("No selection to insert {0} into found.", tmpFile);
return false;
}
// Add Picture
using (var shape = AddPictureToSelection(selection, tmpFile))
{
@ -214,6 +223,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
screentip = tooltip;
}
try
{
using var hyperlinks = DisposableCom.Create(wordDocument.ComObject.Hyperlinks);
@ -225,6 +235,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
}
}
}
try
{
// When called for Outlook, the follow error is created: This object model command is not available in e-mail
@ -245,6 +256,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
LOG.WarnFormat("Couldn't set zoom to 100, error: {0}", e.Message);
}
}
try
{
wordApplication.ComObject.Activate();
@ -254,6 +266,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
LOG.Warn("Error activating word application", ex);
}
try
{
wordDocument.ComObject.Activate();
@ -263,6 +276,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
LOG.Warn("Error activating word document", ex);
}
return true;
}
@ -279,6 +293,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
return;
}
wordApplication.ComObject.Visible = true;
wordApplication.ComObject.Activate();
// Create new Document
@ -299,6 +314,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
screentip = tooltip;
}
try
{
using var hyperlinks = DisposableCom.Create(wordDocument.ComObject.Hyperlinks);
@ -313,6 +329,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
}
}
}
try
{
wordDocument.ComObject.Activate();
@ -322,6 +339,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
LOG.Warn("Error activating word document", ex);
}
try
{
using var activeWindow = DisposableCom.Create(wordDocument.ComObject.ActiveWindow);
@ -340,7 +358,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
/// <returns></returns>
private bool IsAfter2003()
{
return _wordVersion.Major > (int)OfficeVersions.Office2003;
return _wordVersion.Major > (int) OfficeVersions.Office2003;
}
}
}

View file

@ -19,18 +19,19 @@
namespace Greenshot.Plugin.Office.OfficeInterop
{
/// <summary>
/// Specifies which EmailFormat the email needs to use
/// </summary>
public enum EmailFormat
{
/// <summary>
/// Specifies which EmailFormat the email needs to use
/// </summary>
public enum EmailFormat
{
/// <summary>
/// Use the plain text format
/// </summary>
Text,
Text,
/// <summary>
/// Use HTML format
/// </summary>
Html
}
Html
}
}

View file

@ -19,38 +19,44 @@
namespace Greenshot.Plugin.Office.OfficeInterop
{
/// <summary>
/// A mapping between the version and a usable name
/// </summary>
public enum OfficeVersions
{
/// <summary>
/// A mapping between the version and a usable name
/// </summary>
public enum OfficeVersions
{
/// <summary>
/// Office 97
/// </summary>
Office97 = 8,
Office97 = 8,
/// <summary>
/// Office 2003
/// </summary>
Office2003 = 11,
Office2003 = 11,
/// <summary>
/// Office 2007
/// </summary>
Office2007 = 12,
Office2007 = 12,
/// <summary>
/// Office 2010
/// </summary>
Office2010 = 14,
Office2010 = 14,
/// <summary>
/// Office 2013
/// </summary>
Office2013 = 15,
Office2013 = 15,
/// <summary>
/// Office 2016
/// </summary>
Office2016 = 16,
/// <summary>
/// Office 2019
/// </summary>
Office2019 = 17
}
}
}

View file

@ -26,91 +26,123 @@ using GreenshotPlugin.Core;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
namespace Greenshot.Plugin.Office {
/// <summary>
/// This is the OfficePlugin base code
/// </summary>
namespace Greenshot.Plugin.Office
{
/// <summary>
/// This is the OfficePlugin base code
/// </summary>
[Plugin("Office", false)]
public class OfficePlugin : IGreenshotPlugin
{
private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(OfficePlugin));
private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(OfficePlugin));
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(bool disposing) {
// Do nothing
}
protected void Dispose(bool disposing)
{
// Do nothing
}
private IEnumerable<IDestination> Destinations() {
IDestination destination;
try {
destination = new ExcelDestination();
} catch {
destination = null;
}
if (destination != null) {
yield return destination;
}
private IEnumerable<IDestination> Destinations()
{
IDestination destination;
try
{
destination = new ExcelDestination();
}
catch
{
destination = null;
}
try {
destination = new PowerpointDestination();
} catch {
destination = null;
}
if (destination != null) {
yield return destination;
}
if (destination != null)
{
yield return destination;
}
try {
destination = new WordDestination();
} catch {
destination = null;
}
if (destination != null) {
yield return destination;
}
try
{
destination = new PowerpointDestination();
}
catch
{
destination = null;
}
try {
destination = new OutlookDestination();
} catch {
destination = null;
}
if (destination != null) {
yield return destination;
}
if (destination != null)
{
yield return destination;
}
try {
destination = new OneNoteDestination();
} catch {
destination = null;
}
if (destination != null) {
yield return destination;
}
}
try
{
destination = new WordDestination();
}
catch
{
destination = null;
}
if (destination != null)
{
yield return destination;
}
try
{
destination = new OutlookDestination();
}
catch
{
destination = null;
}
if (destination != null)
{
yield return destination;
}
try
{
destination = new OneNoteDestination();
}
catch
{
destination = null;
}
if (destination != null)
{
yield return destination;
}
}
/// <summary>
/// Implementation of the IGreenshotPlugin.Initialize
/// </summary>
/// <returns>true if plugin is initialized, false if not (doesn't show)</returns>
public bool Initialize() {
/// <summary>
/// Implementation of the IGreenshotPlugin.Initialize
/// </summary>
/// <returns>true if plugin is initialized, false if not (doesn't show)</returns>
public bool Initialize()
{
SimpleServiceProvider.Current.AddService(Destinations());
return true;
}
return true;
}
public void Shutdown() {
LOG.Debug("Office Plugin shutdown.");
}
public void Shutdown()
{
LOG.Debug("Office Plugin shutdown.");
}
/// <summary>
/// Implementation of the IPlugin.Configure
/// </summary>
public void Configure() {
/// <summary>
/// Implementation of the IPlugin.Configure
/// </summary>
public void Configure()
{
throw new NotImplementedException();
}
}
}
}
}

View file

@ -19,10 +19,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Greenshot.Plugin.Photobucket.Forms {
/// <summary>
/// This class is needed for design-time resolving of the language files
/// </summary>
public class PhotobucketForm : GreenshotPlugin.Controls.GreenshotForm {
}
namespace Greenshot.Plugin.Photobucket.Forms
{
/// <summary>
/// This class is needed for design-time resolving of the language files
/// </summary>
public class PhotobucketForm : GreenshotPlugin.Controls.GreenshotForm
{
}
}

View file

@ -19,19 +19,21 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Greenshot.Plugin.Photobucket.Forms {
/// <summary>
/// Description of PasswordRequestForm.
/// </summary>
public partial class SettingsForm : PhotobucketForm {
public SettingsForm()
{
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
AcceptButton = buttonOK;
CancelButton = buttonCancel;
}
}
namespace Greenshot.Plugin.Photobucket.Forms
{
/// <summary>
/// Description of PasswordRequestForm.
/// </summary>
public partial class SettingsForm : PhotobucketForm
{
public SettingsForm()
{
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
AcceptButton = buttonOK;
CancelButton = buttonCancel;
}
}
}

Some files were not shown because too many files have changed in this diff Show more