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
parent 726644de99
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,16 +25,18 @@ using Greenshot.Plugin.Box.Forms;
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.Box {
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 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")]
[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")]
@ -42,6 +44,7 @@ namespace Greenshot.Plugin.Box {
[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; }
@ -51,30 +54,26 @@ namespace Greenshot.Plugin.Box {
/// <summary>
/// Not stored
/// </summary>
public string AccessToken {
get;
set;
}
public string AccessToken { get; set; }
/// <summary>
/// Not stored
/// </summary>
public DateTimeOffset AccessTokenExpires {
get;
set;
}
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() {
public bool ShowConfigDialog()
{
DialogResult result = new SettingsForm().ShowDialog();
if (result == DialogResult.OK) {
if (result == DialogResult.OK)
{
return true;
}
return false;
}
}
}

View file

@ -24,10 +24,14 @@ using System.Drawing;
using GreenshotPlugin.Core;
using GreenshotPlugin.Interfaces;
namespace Greenshot.Plugin.Box {
public class BoxDestination : AbstractDestination {
namespace Greenshot.Plugin.Box
{
public class BoxDestination : AbstractDestination
{
private readonly BoxPlugin _plugin;
public BoxDestination(BoxPlugin plugin) {
public BoxDestination(BoxPlugin plugin)
{
_plugin = plugin;
}
@ -35,20 +39,25 @@ namespace Greenshot.Plugin.Box {
public override string Description => Language.GetString("box", LangKey.upload_menu_item);
public override Image DisplayIcon {
get {
public override Image DisplayIcon
{
get
{
ComponentResourceManager resources = new ComponentResourceManager(typeof(BoxPlugin));
return (Image)resources.GetObject("Box");
return (Image) resources.GetObject("Box");
}
}
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);
string uploadUrl = _plugin.Upload(captureDetails, surface);
if (uploadUrl != null) {
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 {
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; }
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; }
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; }
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,18 +30,21 @@ using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
namespace Greenshot.Plugin.Box {
namespace Greenshot.Plugin.Box
{
/// <summary>
/// This is the Box base code
/// </summary>
[Plugin("Box", true)]
public class BoxPlugin : IGreenshotPlugin {
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() {
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
@ -59,13 +62,14 @@ namespace Greenshot.Plugin.Box {
/// <summary>
/// Implementation of the IGreenshotPlugin.Initialize
/// </summary>
public bool Initialize() {
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 {
_itemPlugInConfig = new ToolStripMenuItem
{
Image = (Image) _resources.GetObject("Box"),
Text = Language.GetString("box", LangKey.Configure)
};
@ -76,49 +80,57 @@ namespace Greenshot.Plugin.Box {
return true;
}
public void OnLanguageChanged(object sender, EventArgs e) {
if (_itemPlugInConfig != null) {
public void OnLanguageChanged(object sender, EventArgs e)
{
if (_itemPlugInConfig != null)
{
_itemPlugInConfig.Text = Language.GetString("box", LangKey.Configure);
}
}
public void Shutdown() {
public void Shutdown()
{
LOG.Debug("Box Plugin shutdown.");
}
/// <summary>
/// Implementation of the IPlugin.Configure
/// </summary>
public void Configure() {
public void Configure()
{
_config.ShowConfigDialog();
}
public void ConfigMenuClick(object sender, EventArgs eventArgs) {
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) {
public string Upload(ICaptureDetails captureDetails, ISurface surfaceToUpload)
{
SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(_config.UploadFormat, _config.UploadJpegQuality, false);
try {
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);
}
delegate { url = BoxUtils.UploadToBox(imageToUpload, captureDetails.Title, filename); }
);
if (url != null && _config.AfterUploadLinkToClipBoard) {
if (url != null && _config.AfterUploadLinkToClipBoard)
{
ClipboardHelper.SetClipboardData(url);
}
return url;
} catch (Exception ex) {
}
catch (Exception ex)
{
LOG.Error("Error uploading.", ex);
MessageBox.Show(Language.GetString("box", LangKey.upload_failure) + " " + ex.Message);
return null;

View file

@ -27,12 +27,13 @@ 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 {
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";
@ -45,13 +46,16 @@ namespace Greenshot.Plugin.Box {
/// <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);
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()) {
using (var requestStream = webRequest.GetRequestStream())
{
requestStream.Write(data, 0, data.Length);
}
return NetworkHelper.GetResponseAsString(webRequest);
}
@ -63,8 +67,8 @@ namespace Greenshot.Plugin.Box {
/// <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) {
public static string UploadToBox(SurfaceContainer image, string title, string filename)
{
// Fill the OAuth2Settings
var settings = new OAuth2Settings
{
@ -83,12 +87,17 @@ namespace Greenshot.Plugin.Box {
// Copy the settings from the config, which is kept in memory and on the disk
try {
try
{
var webRequest = OAuth2Helper.CreateOAuth2WebRequest(HTTPMethod.POST, UploadFileUri, settings);
IDictionary<string, object> parameters = new Dictionary<string, object>
{
{ "file", image },
{ "parent_id", Config.FolderId }
{
"file", image
},
{
"parent_id", Config.FolderId
}
};
NetworkHelper.WriteMultipartFormData(webRequest, parameters);
@ -100,13 +109,17 @@ namespace Greenshot.Plugin.Box {
var upload = JsonSerializer.Deserialize<Upload>(response);
if (upload?.Entries == null || upload.Entries.Count == 0) return null;
if (Config.UseSharedLink) {
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 {
}
finally
{
// Copy the settings back to the config, so they are stored.
Config.RefreshToken = settings.RefreshToken;
Config.AccessToken = settings.AccessToken;
@ -116,23 +129,26 @@ namespace Greenshot.Plugin.Box {
}
}
}
/// <summary>
/// A simple helper class for the DataContractJsonSerializer
/// </summary>
internal static class JsonSerializer {
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) {
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,12 +19,15 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Greenshot.Plugin.Box.Forms {
namespace Greenshot.Plugin.Box.Forms
{
/// <summary>
/// Description of PasswordRequestForm.
/// </summary>
public partial class SettingsForm : BoxForm {
public SettingsForm() {
public partial class SettingsForm : BoxForm
{
public SettingsForm()
{
//
// The InitializeComponent() call is required for Windows Forms designer support.
//

View file

@ -18,8 +18,11 @@
* 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 {
namespace Greenshot.Plugin.Box
{
public enum LangKey
{
upload_menu_item,
upload_failure,
communication_wait,

View file

@ -26,16 +26,21 @@ using GreenshotConfluencePlugin.confluence;
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.Confluence {
public class Page {
public Page(RemotePage page) {
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) {
public Page(RemoteSearchResult searchResult, string space)
{
Id = searchResult.id;
Title = searchResult.title;
SpaceKey = space;
@ -43,53 +48,39 @@ namespace Greenshot.Plugin.Confluence {
Content = searchResult.excerpt;
}
public Page(RemotePageSummary pageSummary) {
public Page(RemotePageSummary pageSummary)
{
Id = pageSummary.id;
Title = pageSummary.title;
SpaceKey = pageSummary.space;
Url =pageSummary.url;
Url = pageSummary.url;
}
public long Id {
get;
set;
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 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) {
public class Space
{
public Space(RemoteSpaceSummary space)
{
Key = space.key;
Name = space.name;
}
public string Key {
get;
set;
}
public string Name {
get;
set;
}
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 {
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";
@ -102,29 +93,37 @@ namespace Greenshot.Plugin.Confluence {
private string _url;
private readonly Cache<string, RemotePage> _pageCache = new Cache<string, RemotePage>(60 * Config.Timeout);
public void Dispose() {
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(bool disposing) {
if (_confluence != null) {
protected void Dispose(bool disposing)
{
if (_confluence != null)
{
Logout();
}
if (disposing) {
if (_confluence != null) {
if (disposing)
{
if (_confluence != null)
{
_confluence.Dispose();
_confluence = null;
}
}
}
public ConfluenceConnector(string url, int timeout) {
public ConfluenceConnector(string url, int timeout)
{
_timeout = timeout;
Init(url);
}
private void Init(string url) {
private void Init(string url)
{
_url = url;
_confluence = new ConfluenceSoapServiceService
{
@ -133,7 +132,8 @@ namespace Greenshot.Plugin.Confluence {
};
}
~ConfluenceConnector() {
~ConfluenceConnector()
{
Dispose(false);
}
@ -141,21 +141,29 @@ namespace Greenshot.Plugin.Confluence {
/// Internal login which catches the exceptions
/// </summary>
/// <returns>true if login was done sucessfully</returns>
private bool DoLogin(string user, string password) {
try {
private bool DoLogin(string user, string password)
{
try
{
_credentials = _confluence.login(user, password);
_loggedInTime = DateTime.Now;
_loggedIn = true;
} catch (Exception e) {
}
catch (Exception e)
{
// Check if confluence-v2 caused an error, use v1 instead
if (e.Message.Contains(V2Failed) && _url.Contains("v2")) {
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)) {
if (e.Message.Contains(AuthFailedExceptionName))
{
return false;
}
// Not an authentication issue
_loggedIn = false;
_credentials = null;
@ -163,66 +171,89 @@ namespace Greenshot.Plugin.Confluence {
e.Data.Add("url", _url);
throw;
}
return true;
}
public void Login() {
public void Login()
{
Logout();
try {
try
{
// Get the system name, so the user knows where to login to
string systemName = _url.Replace(ConfluenceConfiguration.DEFAULT_POSTFIX1,"");
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) {
while (dialog.Show(dialog.Name) == DialogResult.OK)
{
if (DoLogin(dialog.Name, dialog.Password))
{
if (dialog.SaveChecked)
{
dialog.Confirm(true);
}
return;
} else {
try {
}
else
{
try
{
dialog.Confirm(false);
} catch (ApplicationException e) {
}
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) {
}
catch (ApplicationException e)
{
// exception handling ...
Log.Error("Problem using the credentials dialog", e);
}
}
public void Logout() {
if (_credentials != null) {
public void Logout()
{
if (_credentials != null)
{
_confluence.logout(_credentials);
_credentials = null;
_loggedIn = false;
}
}
private void CheckCredentials() {
if (_loggedIn) {
if (_loggedInTime.AddMinutes(_timeout-1).CompareTo(DateTime.Now) < 0) {
private void CheckCredentials()
{
if (_loggedIn)
{
if (_loggedInTime.AddMinutes(_timeout - 1).CompareTo(DateTime.Now) < 0)
{
Logout();
Login();
}
} else {
}
else
{
Login();
}
}
public bool IsLoggedIn => _loggedIn;
public void AddAttachment(long pageId, string mime, string comment, string filename, IBinaryContainer image) {
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
@ -234,69 +265,88 @@ namespace Greenshot.Plugin.Confluence {
_confluence.addAttachment(_credentials, pageId, attachment, image.ToByteArray());
}
public Page GetPage(string spaceKey, string pageTitle) {
public Page GetPage(string spaceKey, string pageTitle)
{
RemotePage page = null;
string cacheKey = spaceKey + pageTitle;
if (_pageCache.Contains(cacheKey)) {
if (_pageCache.Contains(cacheKey))
{
page = _pageCache[cacheKey];
}
if (page == null) {
if (page == null)
{
CheckCredentials();
page = _confluence.getPage(_credentials, spaceKey, pageTitle);
_pageCache.Add(cacheKey, page);
}
return new Page(page);
}
public Page GetPage(long pageId) {
public Page GetPage(long pageId)
{
RemotePage page = null;
string cacheKey = pageId.ToString();
if (_pageCache.Contains(cacheKey)) {
if (_pageCache.Contains(cacheKey))
{
page = _pageCache[cacheKey];
}
if (page == null) {
if (page == null)
{
CheckCredentials();
page = _confluence.getPage(_credentials, pageId);
_pageCache.Add(cacheKey, page);
}
return new Page(page);
}
public Page GetSpaceHomepage(Space spaceSummary) {
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() {
public IEnumerable<Space> GetSpaceSummaries()
{
CheckCredentials();
RemoteSpaceSummary [] spaces = _confluence.getSpaces(_credentials);
foreach(RemoteSpaceSummary space in spaces) {
RemoteSpaceSummary[] spaces = _confluence.getSpaces(_credentials);
foreach (RemoteSpaceSummary space in spaces)
{
yield return new Space(space);
}
}
public IEnumerable<Page> GetPageChildren(Page parentPage) {
public IEnumerable<Page> GetPageChildren(Page parentPage)
{
CheckCredentials();
RemotePageSummary[] pages = _confluence.getChildren(_credentials, parentPage.Id);
foreach(RemotePageSummary page in pages) {
foreach (RemotePageSummary page in pages)
{
yield return new Page(page);
}
}
public IEnumerable<Page> GetPageSummaries(Space space) {
public IEnumerable<Page> GetPageSummaries(Space space)
{
CheckCredentials();
RemotePageSummary[] pages = _confluence.getPages(_credentials, space.Key);
foreach(RemotePageSummary page in pages) {
foreach (RemotePageSummary page in pages)
{
yield return new Page(page);
}
}
public IEnumerable<Page> SearchPages(string query, string space) {
public IEnumerable<Page> SearchPages(string query, string space)
{
CheckCredentials();
foreach(var searchResult in _confluence.search(_credentials, query, 20)) {
foreach (var searchResult in _confluence.search(_credentials, query, 20))
{
Log.DebugFormat("Got result of type {0}", searchResult.type);
if ("page".Equals(searchResult.type))
{

View file

@ -23,63 +23,45 @@ using System;
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.Confluence {
namespace Greenshot.Plugin.Confluence
{
/// <summary>
/// Description of ConfluenceConfiguration.
/// </summary>
[Serializable]
[IniSection("Confluence", Description="Greenshot Confluence Plugin configuration")]
public class ConfluenceConfiguration : IniSection {
[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("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("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;
}
public bool IncludePersonSpaces { get; set; }
}
}

View file

@ -32,121 +32,145 @@ using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
namespace Greenshot.Plugin.Confluence {
namespace Greenshot.Plugin.Confluence
{
/// <summary>
/// Description of ConfluenceDestination.
/// </summary>
public class ConfluenceDestination : AbstractDestination {
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() {
static ConfluenceDestination()
{
IsInitialized = false;
try {
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) {
}
catch (Exception ex)
{
Log.ErrorFormat("Problem in the confluence static initializer: {0}", ex.Message);
}
}
public static bool IsInitialized
public static bool IsInitialized { get; private set; }
public ConfluenceDestination()
{
get;
private set;
}
public ConfluenceDestination() {
}
public ConfluenceDestination(Page page) {
public ConfluenceDestination(Page page)
{
_page = page;
}
public override string Designation {
get {
return "Confluence";
}
public override string Designation
{
get { return "Confluence"; }
}
public override string Description {
get {
if (_page == null) {
public override string Description
{
get
{
if (_page == null)
{
return Language.GetString("confluence", LangKey.upload_menu_item);
} else {
}
else
{
return Language.GetString("confluence", LangKey.upload_menu_item) + ": \"" + _page.Title + "\"";
}
}
}
public override bool IsDynamic {
get {
return true;
}
public override bool IsDynamic
{
get { return true; }
}
public override bool IsActive {
get {
return base.IsActive && !string.IsNullOrEmpty(ConfluenceConfig.Url);
}
public override bool IsActive
{
get { return base.IsActive && !string.IsNullOrEmpty(ConfluenceConfig.Url); }
}
public override Image DisplayIcon {
get {
return ConfluenceIcon;
}
public override Image DisplayIcon
{
get { return ConfluenceIcon; }
}
public override IEnumerable<IDestination> DynamicDestinations() {
if (ConfluencePlugin.ConfluenceConnectorNoLogin == null || !ConfluencePlugin.ConfluenceConnectorNoLogin.IsLoggedIn) {
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) {
if (currentPages == null || currentPages.Count == 0)
{
yield break;
}
foreach(Page currentPage in currentPages) {
foreach (Page currentPage in currentPages)
{
yield return new ConfluenceDestination(currentPage);
}
}
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);
// force password check to take place before the pages load
if (!ConfluencePlugin.ConfluenceConnector.IsLoggedIn) {
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) {
if (selectedPage == null)
{
Forms.ConfluenceUpload confluenceUpload = new Forms.ConfluenceUpload(filename);
bool? dialogResult = confluenceUpload.ShowDialog();
if (dialogResult.HasValue && dialogResult.Value) {
if (dialogResult.HasValue && dialogResult.Value)
{
selectedPage = confluenceUpload.SelectedPage;
if (confluenceUpload.IsOpenPageSelected) {
if (confluenceUpload.IsOpenPageSelected)
{
openPage = false;
}
filename = confluenceUpload.Filename;
}
}
string extension = "." + ConfluenceConfig.UploadFormat;
if (!filename.ToLower().EndsWith(extension)) {
if (!filename.ToLower().EndsWith(extension))
{
filename += extension;
}
if (selectedPage != null) {
if (selectedPage != null)
{
bool uploaded = Upload(surface, selectedPage, filename, out var errorMessage);
if (uploaded) {
if (openPage) {
if (uploaded)
{
if (openPage)
{
try
{
Process.Start(selectedPage.Url);
@ -156,23 +180,32 @@ namespace Greenshot.Plugin.Confluence {
// Ignore
}
}
exportInformation.ExportMade = true;
exportInformation.Uri = selectedPage.Url;
} else {
}
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);
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 {
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));
delegate
{
ConfluencePlugin.ConfluenceConnector.AddAttachment(page.Id, "image/" + ConfluenceConfig.UploadFormat.ToString().ToLower(), null, filename,
new SurfaceContainer(surfaceToUpload, outputSettings, filename));
}
);
Log.Debug("Uploaded to Confluence.");
@ -180,25 +213,39 @@ namespace Greenshot.Plugin.Confluence {
{
return true;
}
int retryCount = 2;
while (retryCount >= 0) {
try {
while (retryCount >= 0)
{
try
{
Clipboard.SetText("!" + filename + "!");
break;
} catch (Exception ee) {
if (retryCount == 0) {
}
catch (Exception ee)
{
if (retryCount == 0)
{
Log.Error(ee);
} else {
}
else
{
Thread.Sleep(100);
}
} finally {
}
finally
{
--retryCount;
}
}
return true;
} catch(Exception e) {
}
catch (Exception e)
{
errorMessage = e.Message;
}
return false;
}
}

View file

@ -28,53 +28,70 @@ using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
namespace Greenshot.Plugin.Confluence {
namespace Greenshot.Plugin.Confluence
{
/// <summary>
/// This is the ConfluencePlugin base code
/// </summary>
[Plugin("Confluence", true)]
public class ConfluencePlugin : IGreenshotPlugin {
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() {
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(bool disposing) {
protected void Dispose(bool disposing)
{
//if (disposing) {}
}
private static void CreateConfluenceConnector() {
if (_confluenceConnector == null) {
if (_config.Url.Contains("soap-axis")) {
private static void CreateConfluenceConnector()
{
if (_confluenceConnector == null)
{
if (_config.Url.Contains("soap-axis"))
{
_confluenceConnector = new ConfluenceConnector(_config.Url, _config.Timeout);
} else {
}
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) {
public static ConfluenceConnector ConfluenceConnector
{
get
{
if (_confluenceConnector == null)
{
CreateConfluenceConnector();
}
try {
if (_confluenceConnector != null && !_confluenceConnector.IsLoggedIn) {
try
{
if (_confluenceConnector != null && !_confluenceConnector.IsLoggedIn)
{
_confluenceConnector.Login();
}
} catch (Exception e) {
}
catch (Exception e)
{
MessageBox.Show(Language.GetFormattedString("confluence", LangKey.login_error, e.Message));
}
return _confluenceConnector;
}
}
@ -82,29 +99,39 @@ namespace Greenshot.Plugin.Confluence {
/// <summary>
/// Implementation of the IGreenshotPlugin.Initialize
/// </summary>
public bool Initialize() {
public bool Initialize()
{
// Register configuration (don't need the configuration itself)
_config = IniConfig.GetIniSection<ConfluenceConfiguration>();
if(_config.IsDirty) {
if (_config.IsDirty)
{
IniConfig.Save();
}
try {
try
{
TranslationManager.Instance.TranslationProvider = new LanguageXMLTranslationProvider();
//resources = new ComponentResourceManager(typeof(ConfluencePlugin));
} catch (Exception ex) {
}
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() {
public void Shutdown()
{
LOG.Debug("Confluence Plugin shutdown.");
if (_confluenceConnector != null) {
if (_confluenceConnector != null)
{
_confluenceConnector.Logout();
_confluenceConnector = null;
}
@ -113,20 +140,26 @@ namespace Greenshot.Plugin.Confluence {
/// <summary>
/// Implementation of the IPlugin.Configure
/// </summary>
public void Configure() {
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) {
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) {
if (_confluenceConnector != null)
{
if (!url.Equals(_config.Url))
{
if (_confluenceConnector.IsLoggedIn)
{
_confluenceConnector.Logout();
}
_confluenceConnector = null;
}
}

View file

@ -26,76 +26,106 @@ using System.Text.RegularExpressions;
using System.Windows.Automation;
using GreenshotPlugin.Core;
namespace Greenshot.Plugin.Confluence {
namespace Greenshot.Plugin.Confluence
{
/// <summary>
/// Description of ConfluenceUtils.
/// </summary>
public class ConfluenceUtils {
public class ConfluenceUtils
{
private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(ConfluenceUtils));
public static List<Page> GetCurrentPages() {
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()) {
foreach (string browserurl in GetBrowserUrls())
{
string url;
try {
try
{
url = Uri.UnescapeDataString(browserurl).Replace("+", " ");
} catch {
}
catch
{
LOG.WarnFormat("Error processing URL: {0}", browserurl);
continue;
}
MatchCollection pageIdMatch = pageIdRegex.Matches(url);
if (pageIdMatch != null && pageIdMatch.Count > 0) {
if (pageIdMatch != null && pageIdMatch.Count > 0)
{
long pageId = long.Parse(pageIdMatch[0].Groups[1].Value);
try {
try
{
bool pageDouble = false;
foreach(Page page in pages) {
if (page.Id == pageId) {
foreach (Page page in pages)
{
if (page.Id == pageId)
{
pageDouble = true;
LOG.DebugFormat("Skipping double page with ID {0}", pageId);
break;
}
}
if (!pageDouble) {
if (!pageDouble)
{
Page page = ConfluencePlugin.ConfluenceConnector.GetPage(pageId);
LOG.DebugFormat("Adding page {0}", page.Title);
pages.Add(page);
}
continue;
} catch (Exception ex) {
}
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) {
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)) {
if (string.IsNullOrEmpty(title) || string.IsNullOrEmpty(space))
{
continue;
}
if (title.EndsWith("#")) {
title = title.Substring(0, title.Length-1);
if (title.EndsWith("#"))
{
title = title.Substring(0, title.Length - 1);
}
try {
try
{
bool pageDouble = false;
foreach(Page page in pages) {
if (page.Title.Equals(title)) {
foreach (Page page in pages)
{
if (page.Title.Equals(title))
{
LOG.DebugFormat("Skipping double page with title {0}", title);
pageDouble = true;
break;
}
}
if (!pageDouble) {
if (!pageDouble)
{
Page page = ConfluencePlugin.ConfluenceConnector.GetPage(space, title);
LOG.DebugFormat("Adding page {0}", page.Title);
pages.Add(page);
}
} catch (Exception ex) {
}
catch (Exception ex)
{
// Preventing security problems
LOG.DebugFormat("Couldn't get page details for space {0} / title {1}", space, title);
LOG.Warn(ex);
@ -103,49 +133,65 @@ namespace Greenshot.Plugin.Confluence {
}
}
}
return pages;
}
private static IEnumerable<string> GetBrowserUrls() {
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) {
foreach (WindowDetails window in WindowDetails.GetAllWindows("MozillaWindowClass"))
{
if (window.Text.Length == 0)
{
continue;
}
Condition conditionDocument = new AndCondition(new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Document), new PropertyCondition(AutomationElement.IsOffscreenProperty, false));
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) {
if (docElement == null)
{
continue;
}
foreach (AutomationPattern pattern in docElement.GetSupportedPatterns()) {
if (pattern.ProgrammaticName != "ValuePatternIdentifiers.Pattern") {
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)) {
if (!string.IsNullOrEmpty(url))
{
urls.Add(url);
break;
}
}
}
foreach(string url in IEHelper.GetIEUrls().Distinct()) {
foreach (string url in IEHelper.GetIEUrls().Distinct())
{
urls.Add(url);
}
return urls;
}
}
}

View file

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

View file

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

View file

@ -21,7 +21,8 @@
using System.Collections.Generic;
namespace Greenshot.Plugin.Confluence.Forms {
namespace Greenshot.Plugin.Confluence.Forms
{
/// <summary>
/// Interaction logic for ConfluencePagePicker.xaml
/// </summary>
@ -29,27 +30,34 @@ namespace Greenshot.Plugin.Confluence.Forms {
{
private readonly ConfluenceUpload _confluenceUpload;
public ConfluencePagePicker(ConfluenceUpload confluenceUpload, List<Page> pagesToPick) {
public ConfluencePagePicker(ConfluenceUpload confluenceUpload, List<Page> pagesToPick)
{
_confluenceUpload = confluenceUpload;
DataContext = pagesToPick;
InitializeComponent();
}
private void PageListView_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) {
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;
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 {
}
else
{
_confluenceUpload.SelectedPage = null;
}
}
private void Page_Loaded(object sender, System.Windows.RoutedEventArgs e) {
private void Page_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
SelectionChanged();
}
}

View file

@ -25,7 +25,8 @@ using System.Linq;
using System.Windows;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.Confluence.Forms {
namespace Greenshot.Plugin.Confluence.Forms
{
public partial class ConfluenceSearch
{
private static readonly ConfluenceConfiguration ConfluenceConfig = IniConfig.GetIniSection<ConfluenceConfiguration>();
@ -35,58 +36,76 @@ namespace Greenshot.Plugin.Confluence.Forms {
public ObservableCollection<Page> Pages { get; } = new ObservableCollection<Page>();
public ConfluenceSearch(ConfluenceUpload confluenceUpload) {
public ConfluenceSearch(ConfluenceUpload confluenceUpload)
{
_confluenceUpload = confluenceUpload;
DataContext = this;
InitializeComponent();
if (ConfluenceConfig.SearchSpaceKey == null) {
if (ConfluenceConfig.SearchSpaceKey == null)
{
SpaceComboBox.SelectedItem = Spaces.FirstOrDefault();
} else {
foreach(var space in Spaces) {
if (space.Key.Equals(ConfluenceConfig.SearchSpaceKey)) {
}
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) {
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 {
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) {
private void Search_Click(object sender, RoutedEventArgs e)
{
DoSearch();
}
private void DoSearch() {
string spaceKey = (string)SpaceComboBox.SelectedValue;
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)) {
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) {
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) {
private void Page_Loaded(object sender, RoutedEventArgs e)
{
SelectionChanged();
}
private void SearchText_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e) {
private void SearchText_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
Search.IsEnabled = !string.IsNullOrEmpty(searchText.Text);
}
}

View file

@ -27,7 +27,8 @@ using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
namespace Greenshot.Plugin.Confluence.Forms {
namespace Greenshot.Plugin.Confluence.Forms
{
/// <summary>
/// Interaction logic for ConfluenceTreePicker.xaml
/// </summary>
@ -38,28 +39,36 @@ namespace Greenshot.Plugin.Confluence.Forms {
private readonly ConfluenceUpload _confluenceUpload;
private bool _isInitDone;
public ConfluenceTreePicker(ConfluenceUpload confluenceUpload) {
public ConfluenceTreePicker(ConfluenceUpload confluenceUpload)
{
_confluenceConnector = ConfluencePlugin.ConfluenceConnector;
_confluenceUpload = confluenceUpload;
InitializeComponent();
}
private void PageTreeViewItem_DoubleClick(object sender, MouseButtonEventArgs eventArgs) {
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) {
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;}));
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) {
Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart) (() =>
{
foreach (var childPage in pages)
{
Log.Debug("Adding page: " + childPage.Title);
var pageTreeViewItem = new TreeViewItem
{
@ -70,32 +79,46 @@ namespace Greenshot.Plugin.Confluence.Forms {
pageTreeViewItem.PreviewMouseDoubleClick += PageTreeViewItem_DoubleClick;
pageTreeViewItem.PreviewMouseLeftButtonDown += PageTreeViewItem_Click;
}
ShowBusy.Visibility = Visibility.Collapsed;
}));
}) { Name = "Loading childpages for confluence page " + page.Title }.Start();
})
{
Name = "Loading childpages for confluence page " + page.Title
}.Start();
}
private void PageTreeViewItem_Click(object sender, MouseButtonEventArgs eventArgs) {
private void PageTreeViewItem_Click(object sender, MouseButtonEventArgs eventArgs)
{
Log.Debug("pageTreeViewItem_PreviewMouseDoubleClick is called!");
if (eventArgs.Source is not TreeViewItem clickedItem) {
if (eventArgs.Source is not TreeViewItem clickedItem)
{
return;
}
Page page = clickedItem.Tag as Page;
_confluenceUpload.SelectedPage = page;
if (page != null) {
if (page != null)
{
Log.Debug("Page selected: " + page.Title);
}
}
private void Page_Loaded(object sender, RoutedEventArgs e) {
private void Page_Loaded(object sender, RoutedEventArgs e)
{
_confluenceUpload.SelectedPage = null;
if (_isInitDone) {
if (_isInitDone)
{
return;
}
ShowBusy.Visibility = Visibility.Visible;
new Thread(() => {
Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)(() => {
foreach (Space space in _confluenceUpload.Spaces) {
new Thread(() =>
{
Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart) (() =>
{
foreach (Space space in _confluenceUpload.Spaces)
{
TreeViewItem spaceTreeViewItem = new TreeViewItem
{
Header = space.Name,
@ -103,7 +126,8 @@ namespace Greenshot.Plugin.Confluence.Forms {
};
// Get homepage
try {
try
{
Page page = _confluenceConnector.GetSpaceHomepage(space);
TreeViewItem pageTreeViewItem = new TreeViewItem
{
@ -114,14 +138,20 @@ namespace Greenshot.Plugin.Confluence.Forms {
pageTreeViewItem.PreviewMouseLeftButtonDown += PageTreeViewItem_Click;
spaceTreeViewItem.Items.Add(pageTreeViewItem);
ConfluenceTreeView.Items.Add(spaceTreeViewItem);
} catch (Exception ex) {
}
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();
})
{
Name = "Loading spaces for confluence"
}.Start();
}
}
}

View file

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

View file

@ -19,8 +19,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Greenshot.Plugin.Confluence {
public enum LangKey {
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,17 +1,22 @@
using GreenshotPlugin.Core;
namespace Greenshot.Plugin.Confluence.Support {
namespace Greenshot.Plugin.Confluence.Support
{
/// <summary>
///
/// </summary>
public class LanguageXMLTranslationProvider : ITranslationProvider {
public class LanguageXMLTranslationProvider : ITranslationProvider
{
/// <summary>
/// See <see cref="ITranslationProvider.Translate" />
/// </summary>
public object Translate(string key) {
if (Language.HasKey("confluence", key)) {
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>

View file

@ -2,15 +2,18 @@
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) {
public TranslationData(string key)
{
_key = key;
LanguageChangedEventManager.AddListener(TranslationManager.Instance, this);
}
@ -19,7 +22,8 @@ namespace Greenshot.Plugin.Confluence.Support {
/// Releases unmanaged resources and performs other cleanup operations before the
/// <see cref="TranslationData"/> is reclaimed by garbage collection.
/// </summary>
~TranslationData() {
~TranslationData()
{
LanguageChangedEventManager.RemoveListener(TranslationManager.Instance, this);
}
@ -32,12 +36,13 @@ namespace Greenshot.Plugin.Confluence.Support {
OnLanguageChanged(sender, e);
return true;
}
return false;
}
private void OnLanguageChanged(object sender, EventArgs e)
{
PropertyChanged?.Invoke( this, new PropertyChangedEventArgs("Value"));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value"));
}
public event PropertyChangedEventHandler PropertyChanged;

View file

@ -1,7 +1,9 @@
using System;
namespace Greenshot.Plugin.Confluence.Support {
public class TranslationManager {
namespace Greenshot.Plugin.Confluence.Support
{
public class TranslationManager
{
private static TranslationManager _translationManager;
public event EventHandler LanguageChanged;
@ -29,11 +31,14 @@ namespace Greenshot.Plugin.Confluence.Support {
public ITranslationProvider TranslationProvider { get; set; }
public object Translate(string key) {
public object Translate(string key)
{
object translatedValue = TranslationProvider?.Translate(key);
if( translatedValue != null) {
if (translatedValue != null)
{
return translatedValue;
}
return $"!{key}!";
}
}

View file

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

View file

@ -29,18 +29,21 @@ using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
namespace Greenshot.Plugin.Dropbox {
namespace Greenshot.Plugin.Dropbox
{
/// <summary>
/// This is the Dropbox base code
/// </summary>
[Plugin("Dropbox", true)]
public class DropboxPlugin : IGreenshotPlugin {
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() {
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
@ -56,8 +59,8 @@ namespace Greenshot.Plugin.Dropbox {
/// <summary>
/// Implementation of the IGreenshotPlugin.Initialize
/// </summary>
public bool Initialize() {
public bool Initialize()
{
// Register configuration (don't need the configuration itself)
_config = IniConfig.GetIniSection<DropboxPluginConfiguration>();
_resources = new ComponentResourceManager(typeof(DropboxPlugin));
@ -65,7 +68,7 @@ namespace Greenshot.Plugin.Dropbox {
_itemPlugInConfig = new ToolStripMenuItem
{
Text = Language.GetString("dropbox", LangKey.Configure),
Image = (Image)_resources.GetObject("Dropbox")
Image = (Image) _resources.GetObject("Dropbox")
};
_itemPlugInConfig.Click += ConfigMenuClick;
@ -74,44 +77,49 @@ namespace Greenshot.Plugin.Dropbox {
return true;
}
public void OnLanguageChanged(object sender, EventArgs e) {
if (_itemPlugInConfig != null) {
public void OnLanguageChanged(object sender, EventArgs e)
{
if (_itemPlugInConfig != null)
{
_itemPlugInConfig.Text = Language.GetString("dropbox", LangKey.Configure);
}
}
public void Shutdown() {
public void Shutdown()
{
Log.Debug("Dropbox Plugin shutdown.");
}
/// <summary>
/// Implementation of the IPlugin.Configure
/// </summary>
public void Configure() {
public void Configure()
{
_config.ShowConfigDialog();
}
public void ConfigMenuClick(object sender, EventArgs eventArgs) {
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) {
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);
}
delegate { result = DropboxUtils.UploadToDropbox(surfaceToUpload, outputSettings, captureDetails); }
);
return result;
} catch (Exception e) {
}
catch (Exception e)
{
Log.Error(e);
MessageBox.Show(Language.GetString("dropbox", LangKey.upload_failure) + " " + e.Message);
return false;

View file

@ -25,16 +25,18 @@ using Greenshot.Plugin.Dropbox.Forms;
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.Dropbox {
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 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")]
[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")]
@ -57,11 +59,14 @@ namespace Greenshot.Plugin.Dropbox {
/// A form for token
/// </summary>
/// <returns>bool true if OK was pressed, false if cancel</returns>
public bool ShowConfigDialog() {
public bool ShowConfigDialog()
{
DialogResult result = new SettingsForm().ShowDialog();
if (result == DialogResult.OK) {
if (result == DialogResult.OK)
{
return true;
}
return false;
}
}

View file

@ -29,15 +29,18 @@ using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
using Newtonsoft.Json;
namespace Greenshot.Plugin.Dropbox {
namespace Greenshot.Plugin.Dropbox
{
/// <summary>
/// Description of DropboxUtils.
/// </summary>
public class DropboxUtils {
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)
@ -62,13 +65,21 @@ namespace Greenshot.Plugin.Dropbox {
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,10 +89,13 @@ namespace Greenshot.Plugin.Dropbox {
var response = JsonConvert.DeserializeObject<IDictionary<string, string>>(responseString);
return response.ContainsKey("id");
}
catch (Exception ex) {
catch (Exception ex)
{
Log.Error("Upload error: ", ex);
throw;
} finally {
}
finally
{
DropboxConfig.RefreshToken = oauth2Settings.RefreshToken;
DropboxConfig.AccessToken = oauth2Settings.AccessToken;
DropboxConfig.AccessTokenExpires = oauth2Settings.AccessTokenExpires;

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

View file

@ -18,8 +18,11 @@
* 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 {
namespace Greenshot.Plugin.Dropbox
{
public enum LangKey
{
upload_menu_item,
upload_failure,
communication_wait,

View file

@ -25,37 +25,48 @@ using System.IO;
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.ExternalCommand {
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.")]
[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")]
[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")]
[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")]
[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")]
[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("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")]
[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.")]
[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.")]
[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.")]
@ -71,13 +82,19 @@ namespace Greenshot.Plugin.ExternalCommand {
private const string PaintDotNet = "Paint.NET";
private static readonly string PaintDotNetPath;
private static readonly bool HasPaintDotNet;
static ExternalCommandConfiguration() {
try {
static ExternalCommandConfiguration()
{
try
{
PaintPath = PluginUtils.GetExePath("pbrush.exe");
HasPaint = !string.IsNullOrEmpty(PaintPath) && File.Exists(PaintPath);
} catch {
}
catch
{
// Ignore
}
try
{
PaintDotNetPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), @"Paint.NET\PaintDotNet.exe");
@ -99,6 +116,7 @@ namespace Greenshot.Plugin.ExternalCommand {
{
return;
}
Commands.Remove(command);
Commandline.Remove(command);
Argument.Remove(command);

View file

@ -31,45 +31,58 @@ using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
namespace Greenshot.Plugin.ExternalCommand {
namespace Greenshot.Plugin.ExternalCommand
{
/// <summary>
/// Description of OCRDestination.
/// </summary>
public class ExternalCommandDestination : AbstractDestination {
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 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;
public ExternalCommandDestination(string commando) {
public ExternalCommandDestination(string commando)
{
_presetCommand = commando;
}
public override string Designation => "External " + _presetCommand.Replace(',','_');
public override string Designation => "External " + _presetCommand.Replace(',', '_');
public override string Description => _presetCommand;
public override IEnumerable<IDestination> DynamicDestinations() {
public override IEnumerable<IDestination> DynamicDestinations()
{
yield break;
}
public override Image DisplayIcon => IconCache.IconForCommand(_presetCommand);
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);
SurfaceOutputSettings outputSettings = new SurfaceOutputSettings();
outputSettings.PreventGreenshotFormat();
if (_presetCommand != null) {
if (!config.RunInbackground.ContainsKey(_presetCommand)) {
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) {
if (runInBackground)
{
Thread commandThread = new Thread(delegate()
{
CallExternalCommand(exportInformation, fullPath, out output, out error);
@ -82,11 +95,14 @@ namespace Greenshot.Plugin.ExternalCommand {
commandThread.SetApartmentState(ApartmentState.STA);
commandThread.Start();
exportInformation.ExportMade = true;
} else {
}
else
{
CallExternalCommand(exportInformation, fullPath, out output, out error);
ProcessExport(exportInformation, surface);
}
}
return exportInformation;
}
@ -98,32 +114,44 @@ namespace Greenshot.Plugin.ExternalCommand {
/// <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) {
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) {
try
{
if (CallExternalCommand(_presetCommand, fullPath, out output, out error) == 0)
{
exportInformation.ExportMade = true;
if (!string.IsNullOrEmpty(output)) {
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) {
if (config.OutputToClipboard)
{
ClipboardHelper.SetClipboardData(output);
}
if (uriMatches.Count > 0) {
if (uriMatches.Count > 0)
{
exportInformation.Uri = uriMatches[0].Groups[1].Value;
LOG.InfoFormat("Got URI : {0} ", exportInformation.Uri);
if (config.UriToClipboard) {
if (config.UriToClipboard)
{
ClipboardHelper.SetClipboardData(exportInformation.Uri);
}
}
}
} else {
}
else
{
LOG.WarnFormat("Error calling external command: {0} ", output);
exportInformation.ExportMade = false;
exportInformation.ErrorMessage = error;
}
} catch (Exception ex) {
}
catch (Exception ex)
{
exportInformation.ExportMade = false;
exportInformation.ErrorMessage = ex.Message;
LOG.WarnFormat("Error calling external command: {0} ", exportInformation.ErrorMessage);
@ -138,18 +166,27 @@ namespace Greenshot.Plugin.ExternalCommand {
/// <param name="output"></param>
/// <param name="error"></param>
/// <returns></returns>
private int CallExternalCommand(string commando, string fullPath, out string output, out string error) {
try {
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 {
}
catch (Win32Exception w32Ex)
{
try
{
return CallExternalCommand(commando, fullPath, "runas", out output, out error);
} catch {
}
catch
{
w32Ex.Data.Add("commandline", config.Commandline[_presetCommand]);
w32Ex.Data.Add("arguments", config.Argument[_presetCommand]);
throw;
}
} catch (Exception ex) {
}
catch (Exception ex)
{
ex.Data.Add("commandline", config.Commandline[_presetCommand]);
ex.Data.Add("arguments", config.Argument[_presetCommand]);
throw;
@ -165,7 +202,8 @@ namespace Greenshot.Plugin.ExternalCommand {
/// <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) {
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;
@ -183,33 +221,46 @@ 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;
}

View file

@ -21,10 +21,12 @@
using GreenshotPlugin.Controls;
namespace Greenshot.Plugin.ExternalCommand {
namespace Greenshot.Plugin.ExternalCommand
{
/// <summary>
/// This class is needed for design-time resolving of the language files
/// </summary>
public class ExternalCommandForm : GreenshotForm {
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,22 +25,31 @@ using System.IO;
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.ExternalCommand {
public static class 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) {
public static Image IconForCommand(string commandName)
{
Image icon = null;
if (commandName != null) {
if (config.Commandline.ContainsKey(commandName) && File.Exists(config.Commandline[commandName])) {
try {
if (commandName != null)
{
if (config.Commandline.ContainsKey(commandName) && File.Exists(config.Commandline[commandName]))
{
try
{
icon = PluginUtils.GetCachedExeIcon(config.Commandline[commandName], 0);
} catch (Exception ex) {
}
catch (Exception ex)
{
LOG.Warn("Problem loading icon for " + config.Commandline[commandName], ex);
}
}
}
return icon;
}
}

View file

@ -24,14 +24,17 @@ using System.Drawing;
using System.Windows.Forms;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.ExternalCommand {
namespace Greenshot.Plugin.ExternalCommand
{
/// <summary>
/// Description of SettingsForm.
/// </summary>
public partial class SettingsForm : ExternalCommandForm {
public partial class SettingsForm : ExternalCommandForm
{
private static readonly ExternalCommandConfiguration ExternalCommandConfig = IniConfig.GetIniSection<ExternalCommandConfiguration>();
public SettingsForm() {
public SettingsForm()
{
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
@ -41,65 +44,83 @@ namespace Greenshot.Plugin.ExternalCommand {
UpdateView();
}
private void ButtonOkClick(object sender, EventArgs e) {
private void ButtonOkClick(object sender, EventArgs e)
{
IniConfig.Save();
}
private void ButtonAddClick(object sender, EventArgs e) {
private void ButtonAddClick(object sender, EventArgs e)
{
var form = new SettingsFormDetail(null);
form.ShowDialog();
UpdateView();
}
private void ButtonDeleteClick(object sender, EventArgs e) {
foreach(ListViewItem item in listView1.SelectedItems) {
private void ButtonDeleteClick(object sender, EventArgs e)
{
foreach (ListViewItem item in listView1.SelectedItems)
{
string commando = item.Tag as string;
ExternalCommandConfig.Delete(commando);
}
UpdateView();
}
private void UpdateView() {
private void UpdateView()
{
listView1.Items.Clear();
if(ExternalCommandConfig.Commands != null) {
if (ExternalCommandConfig.Commands != null)
{
listView1.ListViewItemSorter = new ListviewComparer();
ImageList imageList = new ImageList();
listView1.SmallImageList = imageList;
int imageNr = 0;
foreach(string commando in ExternalCommandConfig.Commands) {
foreach (string commando in ExternalCommandConfig.Commands)
{
ListViewItem item;
Image iconForExe = IconCache.IconForCommand(commando);
if(iconForExe != null) {
if (iconForExe != null)
{
imageList.Images.Add(iconForExe);
item = new ListViewItem(commando, imageNr++);
} else {
}
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;
}
private void ListView1ItemSelectionChanged(object sender, EventArgs e) {
private void ListView1ItemSelectionChanged(object sender, EventArgs e)
{
button_edit.Enabled = listView1.SelectedItems.Count > 0;
}
private void ButtonEditClick(object sender, EventArgs e) {
private void ButtonEditClick(object sender, EventArgs e)
{
ListView1DoubleClick(sender, e);
}
private void ListView1DoubleClick(object sender, EventArgs e) {
private void ListView1DoubleClick(object sender, EventArgs e)
{
// Safety check for bug #1484
bool selectionActive = listView1.SelectedItems.Count > 0;
if(!selectionActive) {
if (!selectionActive)
{
button_edit.Enabled = false;
return;
}
string commando = listView1.SelectedItems[0].Tag as string;
var form = new SettingsFormDetail(commando);
@ -109,17 +130,22 @@ namespace Greenshot.Plugin.ExternalCommand {
}
}
public class ListviewComparer : System.Collections.IComparer {
public int Compare(object x, object y) {
if(!(x is ListViewItem)) {
return (0);
}
if(!(y is ListViewItem)) {
public class ListviewComparer : System.Collections.IComparer
{
public int Compare(object x, object y)
{
if (!(x is ListViewItem))
{
return (0);
}
var l1 = (ListViewItem)x;
var l2 = (ListViewItem)y;
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,52 +26,64 @@ using System.Windows.Forms;
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.ExternalCommand {
namespace Greenshot.Plugin.ExternalCommand
{
/// <summary>
/// Description of SettingsFormDetail.
/// </summary>
public partial class SettingsFormDetail : ExternalCommandForm {
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;
public SettingsFormDetail(string commando) {
public SettingsFormDetail(string commando)
{
InitializeComponent();
AcceptButton = buttonOk;
CancelButton = buttonCancel;
_commando = commando;
if(commando != null) {
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 {
}
else
{
textBox_arguments.Text = "\"{0}\"";
}
OkButtonState();
}
private void ButtonOkClick(object sender, EventArgs e) {
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) {
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 {
}
else
{
ExternalCommandConfig.Commands.Add(commandName);
ExternalCommandConfig.Commandline.Add(commandName, commandLine);
ExternalCommandConfig.Argument.Add(commandName, arguments);
}
}
private void Button3Click(object sender, EventArgs e) {
private void Button3Click(object sender, EventArgs e)
{
var openFileDialog = new OpenFileDialog
{
Filter = "Executables (*.exe, *.bat, *.com)|*.exe; *.bat; *.com|All files (*)|*",
@ -89,35 +101,47 @@ namespace Greenshot.Plugin.ExternalCommand {
Log.WarnFormat("Can't get the initial path via {0}", textBox_commandline.Text);
Log.Warn("Exception: ", ex);
}
if(initialPath != null && Directory.Exists(initialPath)) {
if (initialPath != null && Directory.Exists(initialPath))
{
openFileDialog.InitialDirectory = initialPath;
} else {
}
else
{
initialPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
openFileDialog.InitialDirectory = initialPath;
}
Log.DebugFormat("Starting OpenFileDialog at {0}", initialPath);
if(openFileDialog.ShowDialog() == DialogResult.OK) {
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
textBox_commandline.Text = openFileDialog.FileName;
}
}
private void 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)) {
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)) {
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)) {
if (string.IsNullOrEmpty(textBox_commandline.Text))
{
buttonOk.Enabled = false;
}
@ -134,6 +158,7 @@ namespace Greenshot.Plugin.ExternalCommand {
textBox_commandline.BackColor = Color.Red;
}
}
// Are the arguments in a valid format?
try
{
@ -149,11 +174,13 @@ namespace Greenshot.Plugin.ExternalCommand {
}
}
private void textBox_name_TextChanged(object sender, EventArgs e) {
private void textBox_name_TextChanged(object sender, EventArgs e)
{
OkButtonState();
}
private void textBox_commandline_TextChanged(object sender, EventArgs e) {
private void textBox_commandline_TextChanged(object sender, EventArgs e)
{
OkButtonState();
}
@ -161,6 +188,5 @@ namespace Greenshot.Plugin.ExternalCommand {
{
OkButtonState();
}
}
}

View file

@ -24,17 +24,21 @@ using Greenshot.Plugin.Flickr.Forms;
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.Flickr {
public enum SafetyLevel {
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 {
public class FlickrConfiguration : IniSection
{
[IniProperty("flickrIsPublic", Description = "IsPublic.", DefaultValue = "true")]
public bool IsPublic { get; set; }
@ -50,10 +54,10 @@ namespace Greenshot.Plugin.Flickr {
[IniProperty("HiddenFromSearch", Description = "Hidden from search", DefaultValue = "false")]
public bool HiddenFromSearch { get; set; }
[IniProperty("UploadFormat", Description="What file type to use for uploading", DefaultValue="png")]
[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")]
[IniProperty("UploadJpegQuality", Description = "JPEG file save quality in %.", DefaultValue = "80")]
public int UploadJpegQuality { get; set; }
[IniProperty("AfterUploadLinkToClipBoard", Description = "After upload send flickr link to clipboard.", DefaultValue = "true")]
@ -64,6 +68,7 @@ namespace Greenshot.Plugin.Flickr {
[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; }
@ -71,11 +76,14 @@ namespace Greenshot.Plugin.Flickr {
/// A form for token
/// </summary>
/// <returns>bool true if OK was pressed, false if cancel</returns>
public bool ShowConfigDialog() {
public bool ShowConfigDialog()
{
DialogResult result = new SettingsForm().ShowDialog();
if (result == DialogResult.OK) {
if (result == DialogResult.OK)
{
return true;
}
return false;
}
}

View file

@ -24,10 +24,14 @@ using System.Drawing;
using GreenshotPlugin.Core;
using GreenshotPlugin.Interfaces;
namespace Greenshot.Plugin.Flickr {
public class FlickrDestination : AbstractDestination {
namespace Greenshot.Plugin.Flickr
{
public class FlickrDestination : AbstractDestination
{
private readonly FlickrPlugin _plugin;
public FlickrDestination(FlickrPlugin plugin) {
public FlickrDestination(FlickrPlugin plugin)
{
_plugin = plugin;
}
@ -35,20 +39,25 @@ namespace Greenshot.Plugin.Flickr {
public override string Description => Language.GetString("flickr", LangKey.upload_menu_item);
public override Image DisplayIcon {
get {
public override Image DisplayIcon
{
get
{
ComponentResourceManager resources = new ComponentResourceManager(typeof(FlickrPlugin));
return (Image)resources.GetObject("flickr");
return (Image) resources.GetObject("flickr");
}
}
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);
bool uploaded = _plugin.Upload(captureDetails, surface, out var uploadUrl);
if (uploaded) {
if (uploaded)
{
exportInformation.ExportMade = true;
exportInformation.Uri = uploadUrl;
}
ProcessExport(exportInformation, surface);
return exportInformation;
}

View file

@ -37,24 +37,31 @@ namespace Greenshot.Plugin.Flickr
/// This is the Flickr base code
/// </summary>
[Plugin("Flickr", true)]
public class FlickrPlugin : IGreenshotPlugin {
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() {
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(bool disposing) {
if (!disposing) {
protected void Dispose(bool disposing)
{
if (!disposing)
{
return;
}
if (_itemPlugInConfig == null) {
if (_itemPlugInConfig == null)
{
return;
}
_itemPlugInConfig.Dispose();
_itemPlugInConfig = null;
}
@ -62,7 +69,8 @@ namespace Greenshot.Plugin.Flickr
/// <summary>
/// Implementation of the IGreenshotPlugin.Initialize
/// </summary>
public bool Initialize() {
public bool Initialize()
{
// Register configuration (don't need the configuration itself)
_config = IniConfig.GetIniSection<FlickrConfiguration>();
_resources = new ComponentResourceManager(typeof(FlickrPlugin));
@ -79,52 +87,67 @@ namespace Greenshot.Plugin.Flickr
return true;
}
public void OnLanguageChanged(object sender, EventArgs e) {
if (_itemPlugInConfig != null) {
public void OnLanguageChanged(object sender, EventArgs e)
{
if (_itemPlugInConfig != null)
{
_itemPlugInConfig.Text = Language.GetString("flickr", LangKey.Configure);
}
}
public void Shutdown() {
public void Shutdown()
{
Log.Debug("Flickr Plugin shutdown.");
}
/// <summary>
/// Implementation of the IPlugin.Configure
/// </summary>
public void Configure() {
public void Configure()
{
_config.ShowConfigDialog();
}
public void ConfigMenuClick(object sender, EventArgs eventArgs) {
public void ConfigMenuClick(object sender, EventArgs eventArgs)
{
_config.ShowConfigDialog();
}
public bool Upload(ICaptureDetails captureDetails, ISurface surface, out string uploadUrl) {
public bool Upload(ICaptureDetails captureDetails, ISurface surface, out string uploadUrl)
{
SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(_config.UploadFormat, _config.UploadJpegQuality, false);
uploadUrl = null;
try {
try
{
string flickrUrl = null;
new PleaseWaitForm().ShowAndWait("Flickr", Language.GetString("flickr", LangKey.communication_wait),
delegate {
delegate
{
string filename = Path.GetFileName(FilenameHelper.GetFilename(_config.UploadFormat, captureDetails));
flickrUrl = FlickrUtils.UploadToFlickr(surface, outputSettings, captureDetails.Title, filename);
}
);
if (flickrUrl == null) {
if (flickrUrl == null)
{
return false;
}
uploadUrl = flickrUrl;
if (_config.AfterUploadLinkToClipBoard) {
if (_config.AfterUploadLinkToClipBoard)
{
ClipboardHelper.SetClipboardData(flickrUrl);
}
return true;
} catch (Exception e) {
}
catch (Exception e)
{
Log.Error("Error uploading.", e);
MessageBox.Show(Language.GetString("flickr", LangKey.upload_failure) + " " + e.Message);
}
return false;
}
}

View file

@ -30,21 +30,27 @@ using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
using log4net;
namespace Greenshot.Plugin.Flickr {
namespace Greenshot.Plugin.Flickr
{
/// <summary>
/// Description of FlickrUtils.
/// </summary>
public static class FlickrUtils {
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";
@ -58,7 +64,8 @@ namespace Greenshot.Plugin.Flickr {
/// <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) {
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),
@ -70,70 +77,116 @@ namespace Greenshot.Plugin.Flickr {
Token = config.FlickrToken,
TokenSecret = config.FlickrTokenSecret
};
if (string.IsNullOrEmpty(oAuth.Token)) {
if (!oAuth.Authorize()) {
if (string.IsNullOrEmpty(oAuth.Token))
{
if (!oAuth.Authorize())
{
return null;
}
if (!string.IsNullOrEmpty(oAuth.Token)) {
if (!string.IsNullOrEmpty(oAuth.Token))
{
config.FlickrToken = oAuth.Token;
}
if (!string.IsNullOrEmpty(oAuth.TokenSecret)) {
if (!string.IsNullOrEmpty(oAuth.TokenSecret))
{
config.FlickrTokenSecret = oAuth.TokenSecret;
}
IniConfig.Save();
}
try {
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" }
{
"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) }
{
"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 } };
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) {
}
catch (Exception ex)
{
LOG.Error("Upload error: ", ex);
throw;
} finally {
if (!string.IsNullOrEmpty(oAuth.Token)) {
}
finally
{
if (!string.IsNullOrEmpty(oAuth.Token))
{
config.FlickrToken = oAuth.Token;
}
if (!string.IsNullOrEmpty(oAuth.TokenSecret)) {
if (!string.IsNullOrEmpty(oAuth.TokenSecret))
{
config.FlickrTokenSecret = oAuth.TokenSecret;
}
}
}
private static string GetUrl(string response) {
try {
private static string GetUrl(string response)
{
try
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(response);
if (config.UsePageLink) {
if (config.UsePageLink)
{
XmlNodeList nodes = doc.GetElementsByTagName("url");
if (nodes.Count > 0) {
if (nodes.Count > 0)
{
var xmlNode = nodes.Item(0);
if (xmlNode != null) {
if (xmlNode != null)
{
return xmlNode.InnerText;
}
}
} else {
}
else
{
XmlNodeList nodes = doc.GetElementsByTagName("photo");
if (nodes.Count > 0) {
if (nodes.Count > 0)
{
var item = nodes.Item(0);
if (item?.Attributes != null) {
if (item?.Attributes != null)
{
string farmId = item.Attributes["farm"].Value;
string serverId = item.Attributes["server"].Value;
string photoId = item.Attributes["id"].Value;
@ -143,26 +196,36 @@ namespace Greenshot.Plugin.Flickr {
}
}
}
} catch (Exception ex) {
}
catch (Exception ex)
{
LOG.Error("Error parsing Flickr Response.", ex);
}
return null;
}
private static string GetPhotoId(string response) {
try {
private static string GetPhotoId(string response)
{
try
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(response);
XmlNodeList nodes = doc.GetElementsByTagName("photoid");
if (nodes.Count > 0) {
if (nodes.Count > 0)
{
var xmlNode = nodes.Item(0);
if (xmlNode != null) {
if (xmlNode != null)
{
return xmlNode.InnerText;
}
}
} catch (Exception ex) {
}
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,12 +19,15 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Greenshot.Plugin.Flickr.Forms {
namespace Greenshot.Plugin.Flickr.Forms
{
/// <summary>
/// Description of PasswordRequestForm.
/// </summary>
public partial class SettingsForm : FlickrForm {
public SettingsForm() {
public partial class SettingsForm : FlickrForm
{
public SettingsForm()
{
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
@ -32,6 +35,5 @@ namespace Greenshot.Plugin.Flickr.Forms {
CancelButton = buttonCancel;
AcceptButton = buttonOK;
}
}
}

View file

@ -18,8 +18,11 @@
* 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 {
namespace Greenshot.Plugin.Flickr
{
public enum LangKey
{
upload_menu_item,
upload_failure,
communication_wait,

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,12 +18,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Greenshot.Plugin.GooglePhotos.Forms {
namespace Greenshot.Plugin.GooglePhotos.Forms
{
/// <summary>
/// Description of PasswordRequestForm.
/// </summary>
public partial class SettingsForm : GooglePhotosForm {
public partial class SettingsForm : GooglePhotosForm
{
public SettingsForm()
{
//
@ -33,6 +34,5 @@ namespace Greenshot.Plugin.GooglePhotos.Forms {
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 {
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 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")]
[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;
}
public bool AddFilename { get; set; }
[IniProperty("UploadUser", Description = "The GooglePhotos user to upload to", DefaultValue = "default")]
public string UploadUser {
get;
set;
}
public string UploadUser { get; set; }
[IniProperty("UploadAlbum", Description = "The GooglePhotos album to upload to", DefaultValue = "default")]
public string UploadAlbum {
get;
set;
}
public string UploadAlbum { get; set; }
[IniProperty("RefreshToken", Description = "GooglePhotos authorization refresh Token", Encrypted = true)]
public string RefreshToken {
get;
set;
}
public string RefreshToken { get; set; }
/// <summary>
/// Not stored
/// </summary>
public string AccessToken {
get;
set;
}
public string AccessToken { get; set; }
/// <summary>
/// Not stored
/// </summary>
public DateTimeOffset AccessTokenExpires {
get;
set;
}
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() {
public bool ShowConfigDialog()
{
DialogResult result = new SettingsForm().ShowDialog();
if (result == DialogResult.OK) {
if (result == DialogResult.OK)
{
return true;
}
return false;
}
}
}

View file

@ -23,10 +23,14 @@ using System.Drawing;
using GreenshotPlugin.Core;
using GreenshotPlugin.Interfaces;
namespace Greenshot.Plugin.GooglePhotos {
public class GooglePhotosDestination : AbstractDestination {
namespace Greenshot.Plugin.GooglePhotos
{
public class GooglePhotosDestination : AbstractDestination
{
private readonly GooglePhotosPlugin _plugin;
public GooglePhotosDestination(GooglePhotosPlugin plugin) {
public GooglePhotosDestination(GooglePhotosPlugin plugin)
{
_plugin = plugin;
}
@ -34,20 +38,25 @@ namespace Greenshot.Plugin.GooglePhotos {
public override string Description => Language.GetString("googlephotos", LangKey.upload_menu_item);
public override Image DisplayIcon {
get {
public override Image DisplayIcon
{
get
{
ComponentResourceManager resources = new ComponentResourceManager(typeof(GooglePhotosPlugin));
return (Image)resources.GetObject("GooglePhotos");
return (Image) resources.GetObject("GooglePhotos");
}
}
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);
bool uploaded = _plugin.Upload(captureDetails, surface, out var uploadUrl);
if (uploaded) {
if (uploaded)
{
exportInformation.ExportMade = true;
exportInformation.Uri = uploadUrl;
}
ProcessExport(exportInformation, surface);
return exportInformation;
}

View file

@ -29,18 +29,21 @@ using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
namespace Greenshot.Plugin.GooglePhotos {
namespace Greenshot.Plugin.GooglePhotos
{
/// <summary>
/// This is the GooglePhotos base code
/// </summary>
[Plugin("GooglePhotos", true)]
public class GooglePhotosPlugin : IGreenshotPlugin {
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() {
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
@ -56,7 +59,8 @@ namespace Greenshot.Plugin.GooglePhotos {
/// <summary>
/// Implementation of the IGreenshotPlugin.Initialize
/// </summary>
public bool Initialize() {
public bool Initialize()
{
SimpleServiceProvider.Current.AddService<IDestination>(new GooglePhotosDestination(this));
// Get configuration
@ -74,13 +78,16 @@ namespace Greenshot.Plugin.GooglePhotos {
return true;
}
public void OnLanguageChanged(object sender, EventArgs e) {
if (_itemPlugInRoot != null) {
public void OnLanguageChanged(object sender, EventArgs e)
{
if (_itemPlugInRoot != null)
{
_itemPlugInRoot.Text = Language.GetString("googlephotos", LangKey.Configure);
}
}
public void Shutdown() {
public void Shutdown()
{
Log.Debug("GooglePhotos Plugin shutdown.");
Language.LanguageChanged -= OnLanguageChanged;
//host.OnImageEditorOpen -= new OnImageEditorOpenHandler(ImageEditorOpened);
@ -89,17 +96,21 @@ namespace Greenshot.Plugin.GooglePhotos {
/// <summary>
/// Implementation of the IPlugin.Configure
/// </summary>
public void Configure() {
public void Configure()
{
_config.ShowConfigDialog();
}
public void ConfigMenuClick(object sender, EventArgs eventArgs) {
public void ConfigMenuClick(object sender, EventArgs eventArgs)
{
Configure();
}
public bool Upload(ICaptureDetails captureDetails, ISurface surfaceToUpload, out string uploadUrl) {
public bool Upload(ICaptureDetails captureDetails, ISurface surfaceToUpload, out string uploadUrl)
{
SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(_config.UploadFormat, _config.UploadJpegQuality);
try {
try
{
string url = null;
new PleaseWaitForm().ShowAndWait("GooglePhotos", Language.GetString("googlephotos", LangKey.communication_wait),
delegate
@ -110,14 +121,19 @@ namespace Greenshot.Plugin.GooglePhotos {
);
uploadUrl = url;
if (uploadUrl != null && _config.AfterUploadLinkToClipBoard) {
if (uploadUrl != null && _config.AfterUploadLinkToClipBoard)
{
ClipboardHelper.SetClipboardData(uploadUrl);
}
return true;
} catch (Exception e) {
}
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,15 +26,20 @@ using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
namespace Greenshot.Plugin.GooglePhotos {
namespace Greenshot.Plugin.GooglePhotos
{
/// <summary>
/// Description of GooglePhotosUtils.
/// </summary>
public static class GooglePhotosUtils {
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 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}";
@ -46,7 +51,8 @@ namespace Greenshot.Plugin.GooglePhotos {
/// <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) {
public static string UploadToGooglePhotos(ISurface surfaceToUpload, SurfaceOutputSettings outputSettings, string title, string filename)
{
// Fill the OAuth2Settings
var settings = new OAuth2Settings
{
@ -63,18 +69,23 @@ namespace Greenshot.Plugin.GooglePhotos {
// Copy the settings from the config, which is kept in memory and on the disk
try {
try
{
var webRequest = OAuth2Helper.CreateOAuth2WebRequest(HTTPMethod.POST, string.Format(UploadUrl, Config.UploadUser, Config.UploadAlbum), settings);
if (Config.AddFilename) {
if (Config.AddFilename)
{
webRequest.Headers.Add("Slug", NetworkHelper.EscapeDataString(filename));
}
SurfaceContainer container = new SurfaceContainer(surfaceToUpload, outputSettings, filename);
container.Upload(webRequest);
string response = NetworkHelper.GetResponseAsString(webRequest);
return ParseResponse(response);
} finally {
}
finally
{
// Copy the settings back to the config, so they are stored.
Config.RefreshToken = settings.RefreshToken;
Config.AccessToken = settings.AccessToken;
@ -89,31 +100,43 @@ namespace Greenshot.Plugin.GooglePhotos {
/// </summary>
/// <param name="response"></param>
/// <returns></returns>
public static string ParseResponse(string response) {
if (response == null) {
public static string ParseResponse(string response)
{
if (response == null)
{
return null;
}
try {
try
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(response);
XmlNodeList nodes = doc.GetElementsByTagName("link", "*");
if(nodes.Count > 0) {
if (nodes.Count > 0)
{
string url = null;
foreach(XmlNode node in nodes) {
if (node.Attributes != 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")) {
if (rel != null && rel.EndsWith("canonical"))
{
break;
}
}
}
return url;
}
} catch(Exception e) {
}
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,7 +18,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Greenshot.Plugin.GooglePhotos {
namespace Greenshot.Plugin.GooglePhotos
{
public enum LangKey
{
upload_menu_item,

View file

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

View file

@ -27,18 +27,21 @@ using GreenshotPlugin.Controls;
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.Imgur.Forms {
namespace Greenshot.Plugin.Imgur.Forms
{
/// <summary>
/// Imgur history form
/// </summary>
public sealed partial class ImgurHistory : ImgurForm {
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() {
public static void ShowHistory()
{
lock (Lock)
{
if (ImgurUtils.IsHistoryLoadingNeeded())
@ -54,15 +57,18 @@ namespace Greenshot.Plugin.Imgur.Forms {
{
_instance = new ImgurHistory();
}
if (!_instance.Visible)
{
_instance.Show();
}
_instance.Redraw();
}
}
private ImgurHistory() {
private ImgurHistory()
{
ManualLanguageApply = true;
//
// The InitializeComponent() call is required for Windows Forms designer support.
@ -76,26 +82,36 @@ namespace Greenshot.Plugin.Imgur.Forms {
_columnSorter.SortColumn = 3;
_columnSorter.Order = SortOrder.Descending;
Redraw();
if (listview_imgur_uploads.Items.Count > 0) {
if (listview_imgur_uploads.Items.Count > 0)
{
listview_imgur_uploads.Items[0].Selected = true;
}
ApplyLanguage();
if (Config.Credits > 0) {
if (Config.Credits > 0)
{
Text = Text + " (" + Config.Credits + " credits)";
}
}
private void Redraw() {
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) {
string[] columns =
{
"hash", "title", "deleteHash", "Date"
};
foreach (string column in columns)
{
listview_imgur_uploads.Columns.Add(column);
}
foreach (ImgurInfo imgurInfo in Config.runtimeImgurHistory.Values) {
foreach (ImgurInfo imgurInfo in Config.runtimeImgurHistory.Values)
{
var item = new ListViewItem(imgurInfo.Hash)
{
Tag = imgurInfo
@ -105,7 +121,9 @@ namespace Greenshot.Plugin.Imgur.Forms {
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++) {
for (int i = 0; i < columns.Length; i++)
{
listview_imgur_uploads.AutoResizeColumn(i, ColumnHeaderAutoResizeStyle.ColumnContent);
}
@ -116,17 +134,22 @@ namespace Greenshot.Plugin.Imgur.Forms {
clipboardButton.Enabled = false;
}
private void Listview_imgur_uploadsSelectedIndexChanged(object sender, EventArgs e) {
private void Listview_imgur_uploadsSelectedIndexChanged(object sender, EventArgs e)
{
pictureBox1.Image = pictureBox1.ErrorImage;
if (listview_imgur_uploads.SelectedItems.Count > 0) {
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;
if (listview_imgur_uploads.SelectedItems.Count == 1)
{
ImgurInfo imgurInfo = (ImgurInfo) listview_imgur_uploads.SelectedItems[0].Tag;
pictureBox1.Image = imgurInfo.Image;
}
} else {
}
else
{
pictureBox1.Image = pictureBox1.ErrorImage;
deleteButton.Enabled = false;
openButton.Enabled = false;
@ -134,48 +157,60 @@ namespace Greenshot.Plugin.Imgur.Forms {
}
}
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);
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 {
try
{
new PleaseWaitForm().ShowAndWait("Imgur", Language.GetString("imgur", LangKey.communication_wait),
delegate {
ImgurUtils.DeleteImgurImage(imgurInfo);
}
delegate { ImgurUtils.DeleteImgurImage(imgurInfo); }
);
} catch (Exception ex) {
}
catch (Exception ex)
{
Log.Warn("Problem communicating with Imgur: ", ex);
}
imgurInfo.Dispose();
}
}
Redraw();
}
private void ClipboardButtonClick(object sender, EventArgs e) {
private void ClipboardButtonClick(object sender, EventArgs e)
{
StringBuilder links = new StringBuilder();
if (listview_imgur_uploads.SelectedItems.Count > 0) {
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;
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) {
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) {
if (result == DialogResult.Yes)
{
Config.runtimeImgurHistory.Clear();
Config.ImgurUploadHistory.Clear();
IniConfig.Save();
@ -188,21 +223,28 @@ namespace Greenshot.Plugin.Imgur.Forms {
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;
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) {
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) {
if (e.Column == _columnSorter.SortColumn)
{
// Reverse the current sort direction for this column.
_columnSorter.Order = _columnSorter.Order == SortOrder.Ascending ? SortOrder.Descending : SortOrder.Ascending;
} else {
}
else
{
// Set the column number that is to be sorted; default to ascending.
_columnSorter.SortColumn = e.Column;
_columnSorter.Order = SortOrder.Ascending;

View file

@ -21,11 +21,13 @@
using System;
namespace Greenshot.Plugin.Imgur.Forms {
namespace Greenshot.Plugin.Imgur.Forms
{
/// <summary>
/// Description of PasswordRequestForm.
/// </summary>
public partial class SettingsForm : ImgurForm {
public partial class SettingsForm : ImgurForm
{
public SettingsForm()
{
//
@ -38,7 +40,8 @@ namespace Greenshot.Plugin.Imgur.Forms {
historyButton.Enabled = ImgurUtils.IsHistoryLoadingNeeded();
}
private void ButtonHistoryClick(object sender, EventArgs e) {
private void ButtonHistoryClick(object sender, EventArgs e)
{
ImgurHistory.ShowHistory();
}
}

View file

@ -26,26 +26,33 @@ using Greenshot.Plugin.Imgur.Forms;
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.Imgur {
namespace Greenshot.Plugin.Imgur
{
/// <summary>
/// Description of ImgurConfiguration.
/// </summary>
[IniSection("Imgur", Description="Greenshot Imgur Plugin configuration")]
public class ImgurConfiguration : IniSection {
[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")]
[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")]
[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")]
[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")]
[IniProperty("AnonymousAccess", Description = "Use anonymous access to Imgur", DefaultValue = "true")]
public bool AnonymousAccess { get; set; }
[IniProperty("RefreshToken", Description = "Imgur refresh Token", Encrypted = true, ExcludeIfNull = true)]
@ -63,20 +70,19 @@ namespace Greenshot.Plugin.Imgur {
[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)")]
[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;
}
public int Credits { get; set; }
/// <summary>
/// Supply values we can't put as defaults
@ -94,7 +100,8 @@ namespace Greenshot.Plugin.Imgur {
/// A form for username/password
/// </summary>
/// <returns>bool true if OK was pressed, false if cancel</returns>
public bool ShowConfigDialog() {
public bool ShowConfigDialog()
{
SettingsForm settingsForm = new SettingsForm();
DialogResult result = settingsForm.ShowDialog();
return result == DialogResult.OK;

View file

@ -24,14 +24,17 @@ using System.Drawing;
using GreenshotPlugin.Core;
using GreenshotPlugin.Interfaces;
namespace Greenshot.Plugin.Imgur {
namespace Greenshot.Plugin.Imgur
{
/// <summary>
/// Description of ImgurDestination.
/// </summary>
public class ImgurDestination : AbstractDestination {
public class ImgurDestination : AbstractDestination
{
private readonly ImgurPlugin _plugin;
public ImgurDestination(ImgurPlugin plugin) {
public ImgurDestination(ImgurPlugin plugin)
{
_plugin = plugin;
}
@ -39,17 +42,21 @@ namespace Greenshot.Plugin.Imgur {
public override string Description => Language.GetString("imgur", LangKey.upload_menu_item);
public override Image DisplayIcon {
get {
public override Image DisplayIcon
{
get
{
ComponentResourceManager resources = new ComponentResourceManager(typeof(ImgurPlugin));
return (Image)resources.GetObject("Imgur");
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;

View file

@ -32,13 +32,10 @@ namespace Greenshot.Plugin.Imgur
{
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; }
@ -49,55 +46,24 @@ namespace Greenshot.Plugin.Imgur
}
}
public string Title
{
get;
set;
}
public string Title { get; set; }
public string ImageType
{
get;
set;
}
public string ImageType { get; set; }
public DateTime Timestamp
{
get;
set;
}
public DateTime Timestamp { get; set; }
public string Original
{
get;
set;
}
public string Original { get; set; }
public string Page
{
get;
set;
}
public string Page { get; set; }
public string SmallSquare
{
get;
set;
}
public string SmallSquare { get; set; }
public string LargeThumbnail
{
get;
set;
}
public string LargeThumbnail { get; set; }
public string DeletePage
{
get;
set;
}
public string DeletePage { get; set; }
private Image _image;
public Image Image
{
get { return _image; }
@ -129,8 +95,10 @@ namespace Greenshot.Plugin.Imgur
{
_image?.Dispose();
}
_image = null;
}
public static ImgurInfo ParseResponse(string response)
{
Log.Debug(response);
@ -154,26 +122,31 @@ namespace Greenshot.Plugin.Imgur
{
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)
{
@ -184,17 +157,20 @@ namespace Greenshot.Plugin.Imgur
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)
{
@ -205,6 +181,7 @@ namespace Greenshot.Plugin.Imgur
// 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";
}
@ -212,6 +189,7 @@ namespace Greenshot.Plugin.Imgur
{
Log.ErrorFormat("Could not parse Imgur response due to error {0}, response was: {1}", e.Message, response);
}
return imgurInfo;
}
}

View file

@ -32,37 +32,46 @@ using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
namespace Greenshot.Plugin.Imgur {
namespace Greenshot.Plugin.Imgur
{
/// <summary>
/// This is the ImgurPlugin code
/// </summary>
[Plugin("Imgur", true)]
public class ImgurPlugin : IGreenshotPlugin {
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() {
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing) {
if (disposing) {
if (_historyMenuItem != null) {
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (_historyMenuItem != null)
{
_historyMenuItem.Dispose();
_historyMenuItem = null;
}
if (_itemPlugInConfig != null) {
if (_itemPlugInConfig != null)
{
_itemPlugInConfig.Dispose();
_itemPlugInConfig = null;
}
}
}
private IEnumerable<IDestination> Destinations() {
private IEnumerable<IDestination> Destinations()
{
yield return new ImgurDestination(this);
}
@ -70,7 +79,8 @@ namespace Greenshot.Plugin.Imgur {
/// Implementation of the IGreenshotPlugin.Initialize
/// </summary>
/// <returns>true if plugin is initialized, false if not (doesn't show)</returns>
public bool Initialize() {
public bool Initialize()
{
// Get configuration
_config = IniConfig.GetIniSection<ImgurConfiguration>();
_resources = new ComponentResourceManager(typeof(ImgurPlugin));
@ -83,15 +93,11 @@ namespace Greenshot.Plugin.Imgur {
// Provide the IDestination
SimpleServiceProvider.Current.AddService(Destinations());
_historyMenuItem = new ToolStripMenuItem(Language.GetString("imgur", LangKey.history));
_historyMenuItem.Click += delegate {
ImgurHistory.ShowHistory();
};
_historyMenuItem.Click += delegate { ImgurHistory.ShowHistory(); };
itemPlugInRoot.DropDownItems.Add(_historyMenuItem);
_itemPlugInConfig = new ToolStripMenuItem(Language.GetString("imgur", LangKey.configure));
_itemPlugInConfig.Click += delegate {
_config.ShowConfigDialog();
};
_itemPlugInConfig.Click += delegate { _config.ShowConfigDialog(); };
itemPlugInRoot.DropDownItems.Add(_itemPlugInConfig);
PluginUtils.AddToContextMenu(itemPlugInRoot);
@ -102,42 +108,55 @@ namespace Greenshot.Plugin.Imgur {
return true;
}
public void OnLanguageChanged(object sender, EventArgs e) {
if (_itemPlugInConfig != null) {
public void OnLanguageChanged(object sender, EventArgs e)
{
if (_itemPlugInConfig != null)
{
_itemPlugInConfig.Text = Language.GetString("imgur", LangKey.configure);
}
if (_historyMenuItem != null) {
if (_historyMenuItem != null)
{
_historyMenuItem.Text = Language.GetString("imgur", LangKey.history);
}
}
private void UpdateHistoryMenuItem() {
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)
{
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) {
}
catch (Exception ex)
{
Log.Error("Error loading history", ex);
}
}
public virtual void Shutdown() {
public virtual void Shutdown()
{
Log.Debug("Imgur Plugin shutdown.");
Language.LanguageChanged -= OnLanguageChanged;
}
@ -145,7 +164,8 @@ namespace Greenshot.Plugin.Imgur {
/// <summary>
/// Implementation of the IPlugin.Configure
/// </summary>
public virtual void Configure() {
public virtual void Configure()
{
_config.ShowConfigDialog();
}
@ -156,9 +176,11 @@ namespace Greenshot.Plugin.Imgur {
/// <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) {
public bool Upload(ICaptureDetails captureDetails, ISurface surfaceToUpload, out string uploadUrl)
{
SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(_config.UploadFormat, _config.UploadJpegQuality, _config.UploadReduceColors);
try {
try
{
string filename = Path.GetFileName(FilenameHelper.GetFilenameFromPattern(_config.FilenamePattern, _config.UploadFormat, captureDetails));
ImgurInfo imgurInfo = null;
@ -167,7 +189,8 @@ namespace Greenshot.Plugin.Imgur {
delegate
{
imgurInfo = ImgurUtils.UploadToImgur(surfaceToUpload, outputSettings, captureDetails.Title, filename);
if (imgurInfo != null && _config.AnonymousAccess) {
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);
@ -176,11 +199,14 @@ namespace Greenshot.Plugin.Imgur {
}
);
if (imgurInfo != null) {
if (imgurInfo != null)
{
// TODO: Optimize a second call for export
using (Image tmpImage = surfaceToUpload.GetImageForExport()) {
using (Image tmpImage = surfaceToUpload.GetImageForExport())
{
imgurInfo.Image = ImageHelper.CreateThumbnail(tmpImage, 90, 90);
}
IniConfig.Save();
if (_config.UsePageLink)
@ -191,12 +217,12 @@ namespace Greenshot.Plugin.Imgur {
{
uploadUrl = imgurInfo.Original;
}
if (!string.IsNullOrEmpty(uploadUrl) && _config.CopyLinkToClipboard)
{
try
{
ClipboardHelper.SetClipboardData(uploadUrl);
}
catch (Exception ex)
{
@ -204,12 +230,16 @@ namespace Greenshot.Plugin.Imgur {
uploadUrl = null;
}
}
return true;
}
} catch (Exception e) {
}
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,11 +30,13 @@ using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
namespace Greenshot.Plugin.Imgur {
namespace Greenshot.Plugin.Imgur
{
/// <summary>
/// A collection of Imgur helper methods
/// </summary>
public static class ImgurUtils {
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>();
@ -45,14 +47,16 @@ namespace Greenshot.Plugin.Imgur {
/// <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);
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() {
public static void LoadHistory()
{
if (!IsHistoryLoadingNeeded())
{
return;
@ -61,8 +65,10 @@ namespace Greenshot.Plugin.Imgur {
bool saveNeeded = false;
// Load the ImUr history
foreach (string hash in Config.ImgurUploadHistory.Keys.ToList()) {
if (Config.runtimeImgurHistory.ContainsKey(hash)) {
foreach (string hash in Config.ImgurUploadHistory.Keys.ToList())
{
if (Config.runtimeImgurHistory.ContainsKey(hash))
{
// Already loaded
continue;
}
@ -71,41 +77,55 @@ namespace Greenshot.Plugin.Imgur {
{
var deleteHash = Config.ImgurUploadHistory[hash];
ImgurInfo imgurInfo = RetrieveImgurInfo(hash, deleteHash);
if (imgurInfo != null) {
if (imgurInfo != null)
{
RetrieveImgurThumbnail(imgurInfo);
Config.runtimeImgurHistory[hash] = imgurInfo;
} else {
}
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) {
}
catch (WebException wE)
{
bool redirected = false;
if (wE.Status == WebExceptionStatus.ProtocolError) {
HttpWebResponse response = (HttpWebResponse)wE.Response;
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) {
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) {
if (!redirected)
{
Log.Error("Problem loading ImgUr history for hash " + hash, wE);
}
} catch (Exception e) {
}
catch (Exception e)
{
Log.Error("Problem loading ImgUr history for hash " + hash, e);
}
}
if (saveNeeded) {
if (saveNeeded)
{
// Save needed changes
IniConfig.Save();
}
@ -115,7 +135,8 @@ namespace Greenshot.Plugin.Imgur {
/// Use this to make sure Imgur knows from where the upload comes.
/// </summary>
/// <param name="webRequest"></param>
private static void SetClientId(HttpWebRequest webRequest) {
private static void SetClientId(HttpWebRequest webRequest)
{
webRequest.Headers.Add("Authorization", "Client-ID " + ImgurCredentials.CONSUMER_KEY);
}
@ -128,27 +149,36 @@ namespace Greenshot.Plugin.Imgur {
/// <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) {
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;
if (title != null && Config.AddTitle)
{
otherParameters["title"] = title;
}
// add filename
if (filename != null && Config.AddFilename) {
if (filename != null && Config.AddFilename)
{
otherParameters["name"] = filename;
}
string responseString = null;
if (Config.AnonymousAccess) {
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);
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()) {
try
{
using (var requestStream = webRequest.GetRequestStream())
{
ImageOutput.SaveToStream(surfaceToUpload, requestStream, outputSettings);
}
@ -160,12 +190,15 @@ namespace Greenshot.Plugin.Imgur {
using StreamReader reader = new StreamReader(responseStream, true);
responseString = reader.ReadToEnd();
}
} catch (Exception ex) {
}
catch (Exception ex)
{
Log.Error("Upload to imgur gave an exception: ", ex);
throw;
}
} else {
}
else
{
var oauth2Settings = new OAuth2Settings
{
AuthUrlPattern = "https://api.imgur.com/oauth2/authorize?response_type=token&client_id={ClientId}&state={State}",
@ -201,10 +234,12 @@ namespace Greenshot.Plugin.Imgur {
IniConfig.Save();
}
}
if (string.IsNullOrEmpty(responseString))
{
return null;
}
return ImgurInfo.ParseResponse(responseString);
}
@ -212,11 +247,14 @@ namespace Greenshot.Plugin.Imgur {
/// Retrieve the thumbnail of an imgur image
/// </summary>
/// <param name="imgurInfo"></param>
public static void RetrieveImgurThumbnail(ImgurInfo imgurInfo) {
if (imgurInfo.SmallSquare == null) {
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;
@ -237,7 +275,8 @@ namespace Greenshot.Plugin.Imgur {
/// <param name="hash"></param>
/// <param name="deleteHash"></param>
/// <returns>ImgurInfo</returns>
public static ImgurInfo RetrieveImgurInfo(string hash, string deleteHash) {
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);
@ -254,14 +293,20 @@ 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) {
}
catch (WebException wE)
{
if (wE.Status == WebExceptionStatus.ProtocolError)
{
if (((HttpWebResponse) wE.Response).StatusCode == HttpStatusCode.NotFound)
{
return null;
}
}
throw;
}
ImgurInfo imgurInfo = null;
if (responseString != null)
{
@ -269,6 +314,7 @@ namespace Greenshot.Plugin.Imgur {
imgurInfo = ImgurInfo.ParseResponse(responseString);
imgurInfo.DeleteHash = deleteHash;
}
return imgurInfo;
}
@ -276,16 +322,19 @@ namespace Greenshot.Plugin.Imgur {
/// Delete an imgur image, this is done by specifying the delete hash
/// </summary>
/// <param name="imgurInfo"></param>
public static void DeleteImgurImage(ImgurInfo imgurInfo) {
public static void DeleteImgurImage(ImgurInfo imgurInfo)
{
Log.InfoFormat("Deleting Imgur image for {0}", imgurInfo.DeleteHash);
try {
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()) {
using (WebResponse response = webRequest.GetResponse())
{
LogRateLimitInfo(response);
var responseStream = response.GetResponseStream();
if (responseStream != null)
@ -294,15 +343,21 @@ namespace Greenshot.Plugin.Imgur {
responseString = reader.ReadToEnd();
}
}
Log.InfoFormat("Delete result: {0}", responseString);
} catch (WebException wE) {
}
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 ;
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);
@ -314,8 +369,10 @@ namespace Greenshot.Plugin.Imgur {
/// </summary>
/// <param name="nameValues"></param>
/// <param name="key"></param>
private static void LogHeader(IDictionary<string, string> nameValues, string key) {
if (nameValues.ContainsKey(key)) {
private static void LogHeader(IDictionary<string, string> nameValues, string key)
{
if (nameValues.ContainsKey(key))
{
Log.InfoFormat("{0}={1}", key, nameValues[key]);
}
}
@ -324,13 +381,17 @@ namespace Greenshot.Plugin.Imgur {
/// Log the current rate-limit information
/// </summary>
/// <param name="response"></param>
private static void LogRateLimitInfo(WebResponse response) {
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)) {
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");
@ -340,7 +401,8 @@ namespace Greenshot.Plugin.Imgur {
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)) {
if (nameValues.ContainsKey("X-RateLimit-Remaining") && int.TryParse(nameValues["X-RateLimit-Remaining"], out var credits))
{
Config.Credits = credits;
}
}

View file

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

View file

@ -114,6 +114,7 @@ namespace Greenshot.Plugin.Jira
{
cacheItemPolicy.UpdateCallback = UpdateCallback;
}
if (ActivateRemovedCallback)
{
cacheItemPolicy.RemovedCallback = RemovedCallback;

View file

@ -30,15 +30,18 @@ using GreenshotPlugin.Controls;
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.Jira.Forms {
public partial class JiraForm : Form {
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) {
public JiraForm(JiraConnector jiraConnector)
{
InitializeComponent();
Icon = GreenshotResources.GetGreenshotIcon();
AcceptButton = uploadButton;
@ -70,6 +73,7 @@ namespace Greenshot.Plugin.Jira.Forms {
{
MessageBox.Show(Language.GetFormattedString("jira", LangKey.login_error, e.Message));
}
if (_jiraConnector.IsLoggedIn)
{
var filters = await _jiraConnector.GetFavoriteFiltersAsync();
@ -79,8 +83,10 @@ namespace Greenshot.Plugin.Jira.Forms {
{
jiraFilterBox.Items.Add(filter);
}
jiraFilterBox.SelectedIndex = 0;
}
ChangeModus(true);
if (_jiraConnector.Monitor.RecentJiras.Any())
{
@ -91,44 +97,53 @@ namespace Greenshot.Plugin.Jira.Forms {
}
}
private void InitializeComponentText() {
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);
}
private void ChangeModus(bool enabled) {
private void ChangeModus(bool enabled)
{
jiraFilterBox.Enabled = enabled;
jiraListView.Enabled = enabled;
jiraFilenameBox.Enabled = enabled;
jiraCommentBox.Enabled = enabled;
}
public void SetFilename(string filename) {
public void SetFilename(string filename)
{
jiraFilenameBox.Text = filename;
}
public Issue GetJiraIssue() {
public Issue GetJiraIssue()
{
return _selectedIssue;
}
public async Task UploadAsync(IBinaryContainer attachment) {
public async Task UploadAsync(IBinaryContainer attachment)
{
attachment.Filename = jiraFilenameBox.Text;
await _jiraConnector.AttachAsync(_selectedIssue.Key, attachment);
if (!string.IsNullOrEmpty(jiraCommentBox.Text)) {
if (!string.IsNullOrEmpty(jiraCommentBox.Text))
{
await _jiraConnector.AddCommentAsync(_selectedIssue.Key, jiraCommentBox.Text);
}
}
private async void JiraFilterBox_SelectedIndexChanged(object sender, EventArgs e) {
if (_jiraConnector.IsLoggedIn) {
private async void JiraFilterBox_SelectedIndexChanged(object sender, EventArgs e)
{
if (_jiraConnector.IsLoggedIn)
{
uploadButton.Enabled = false;
var filter = (Filter)jiraFilterBox.SelectedItem;
if (filter == null) {
var filter = (Filter) jiraFilterBox.SelectedItem;
if (filter == null)
{
return;
}
IList<Issue> issues = null;
try
{
@ -141,25 +156,33 @@ namespace Greenshot.Plugin.Jira.Forms {
}
jiraListView.Items.Clear();
if (issues?.Count > 0) {
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 };
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 {
var imageList = new ImageList
{
ImageSize = scaledIconSize
};
jiraListView.SmallImageList = imageList;
jiraListView.LargeImageList = imageList;
foreach (var issue in issues) {
foreach (var issue in issues)
{
var item = new ListViewItem
{
Tag = issue
@ -187,6 +210,7 @@ namespace Greenshot.Plugin.Jira.Forms {
{
jiraListView.AutoResizeColumn(i, ColumnHeaderAutoResizeStyle.ColumnContent);
}
jiraListView.Invalidate();
jiraListView.Update();
}
@ -196,22 +220,30 @@ namespace Greenshot.Plugin.Jira.Forms {
}
}
private void JiraListView_SelectedIndexChanged(object sender, EventArgs e) {
if (jiraListView.SelectedItems.Count > 0) {
_selectedIssue = (Issue)jiraListView.SelectedItems[0].Tag;
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 {
}
else
{
uploadButton.Enabled = false;
}
}
private void JiraListView_ColumnClick(object sender, ColumnClickEventArgs e) {
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) {
if (e.Column == _columnSorter.SortColumn)
{
// Reverse the current sort direction for this column.
_columnSorter.Order = _columnSorter.Order == SortOrder.Ascending ? SortOrder.Descending : SortOrder.Ascending;
} else {
}
else
{
// Set the column number that is to be sorted; default to ascending.
_columnSorter.SortColumn = e.Column;
_columnSorter.Order = SortOrder.Ascending;
@ -221,13 +253,16 @@ namespace Greenshot.Plugin.Jira.Forms {
jiraListView.Sort();
}
private async void JiraKeyTextChanged(object sender, EventArgs e) {
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) {
if (dashIndex > 0 && jiranumber.Length > dashIndex + 1)
{
_selectedIssue = await _jiraConnector.GetIssueAsync(jiraKey.Text);
if (_selectedIssue != null) {
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,11 +19,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Greenshot.Plugin.Jira.Forms {
namespace Greenshot.Plugin.Jira.Forms
{
/// <summary>
/// Description of PasswordRequestForm.
/// </summary>
public partial class SettingsForm : JiraFormBase {
public partial class SettingsForm : JiraFormBase
{
public SettingsForm()
{
//

View file

@ -22,25 +22,27 @@
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.Jira {
namespace Greenshot.Plugin.Jira
{
/// <summary>
/// Description of JiraConfiguration.
/// </summary>
[IniSection("Jira", Description="Greenshot Jira Plugin configuration")]
public class JiraConfiguration : IniSection {
[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)]
[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")]
[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")]
[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")]
[IniProperty("UploadReduceColors", Description = "Reduce color amount of the uploaded image to 256", DefaultValue = "False")]
public bool UploadReduceColors { get; set; }
}
}

View file

@ -34,14 +34,18 @@ using Dapplo.Jira.SvgWinForms.Converters;
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.Jira {
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 {
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;
@ -57,20 +61,25 @@ namespace Greenshot.Plugin.Jira {
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() {
public void Dispose()
{
if (_jiraClient != null)
{
Logout();
}
FavIcon?.Dispose();
}
@ -99,8 +108,13 @@ namespace Greenshot.Plugin.Jira {
{
return false;
}
_jiraClient = JiraClient.Create(new Uri(JiraConfig.Url));
_jiraClient.Behaviour.SetConfig(new SvgConfiguration { Width = CoreConfig.ScaledIconSize.Width, Height = CoreConfig.ScaledIconSize.Height });
_jiraClient.Behaviour.SetConfig(new SvgConfiguration
{
Width = CoreConfig.ScaledIconSize.Width,
Height = CoreConfig.ScaledIconSize.Height
});
_jiraClient.SetBasicAuthentication(user, password);
_issueTypeBitmapCache = new IssueTypeBitmapCache(_jiraClient);
@ -126,6 +140,7 @@ namespace Greenshot.Plugin.Jira {
Log.Warn("Exception details: ", ex2);
return false;
}
return true;
}
@ -134,45 +149,58 @@ namespace Greenshot.Plugin.Jira {
/// If there are credentials, call the real login.
/// </summary>
/// <returns>Task</returns>
public async Task LoginAsync(CancellationToken cancellationToken = default) {
public async Task LoginAsync(CancellationToken cancellationToken = default)
{
Logout();
try {
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) {
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 {
try
{
credentialsDialog.Confirm(false);
} catch (ApplicationException e) {
}
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) {
}
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() {
public void Logout()
{
if (_jiraClient == null || !IsLoggedIn) return;
Monitor.Dispose();
IsLoggedIn = false;
@ -183,8 +211,10 @@ namespace Greenshot.Plugin.Jira {
/// 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) {
private async Task CheckCredentialsAsync(CancellationToken cancellationToken = default)
{
if (!IsLoggedIn)
{
await LoginAsync(cancellationToken);
}
}
@ -256,7 +286,10 @@ namespace Greenshot.Plugin.Jira {
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);
var searchResult = await _jiraClient.Issue.SearchAsync(filter.Jql, null, new[]
{
"summary", "reporter", "assignee", "created", "issuetype"
}, null, cancellationToken).ConfigureAwait(false);
return searchResult.Issues;
}

View file

@ -34,27 +34,33 @@ using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
namespace Greenshot.Plugin.Jira {
namespace Greenshot.Plugin.Jira
{
/// <summary>
/// Description of JiraDestination.
/// </summary>
public class JiraDestination : AbstractDestination {
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 Description {
public override string Description
{
get
{
if (_jiraIssue?.Fields?.Summary == null) {
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));
}
@ -64,7 +70,8 @@ namespace Greenshot.Plugin.Jira {
public override bool IsDynamic => true;
public override Image DisplayIcon {
public override Image DisplayIcon
{
get
{
Image displayIcon = null;
@ -83,16 +90,19 @@ namespace Greenshot.Plugin.Jira {
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");
displayIcon = (Image) resources.GetObject("Jira");
}
return displayIcon;
}
}
@ -100,22 +110,27 @@ namespace Greenshot.Plugin.Jira {
public override IEnumerable<IDestination> DynamicDestinations()
{
var jiraConnector = SimpleServiceProvider.Current.GetInstance<JiraConnector>();
if (jiraConnector == null || !jiraConnector.IsLoggedIn) {
if (jiraConnector == null || !jiraConnector.IsLoggedIn)
{
yield break;
}
foreach(var jiraDetails in jiraConnector.Monitor.RecentJiras)
foreach (var jiraDetails in jiraConnector.Monitor.RecentJiras)
{
yield return new JiraDestination(jiraDetails.JiraIssue);
}
}
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surfaceToUpload, ICaptureDetails captureDetails) {
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 {
if (_jiraIssue != null)
{
try
{
// Run upload in the background
new PleaseWaitForm().ShowAndWait(Description, Language.GetString("jira", LangKey.communication_wait),
async () =>
@ -128,31 +143,37 @@ namespace Greenshot.Plugin.Jira {
Log.DebugFormat("Uploaded to Jira {0}", _jiraIssue.Key);
exportInformation.ExportMade = true;
exportInformation.Uri = surfaceToUpload.UploadUrl;
} catch (Exception e) {
}
catch (Exception e)
{
MessageBox.Show(Language.GetString("jira", LangKey.upload_failure) + " " + e.Message);
}
} else {
}
else
{
var jiraForm = new JiraForm(jiraConnector);
jiraForm.SetFilename(filename);
var dialogResult = jiraForm.ShowDialog();
if (dialogResult == DialogResult.OK) {
try {
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));
}
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) {
}
catch (Exception e)
{
MessageBox.Show(Language.GetString("jira", LangKey.upload_failure) + " " + e.Message);
}
}
}
ProcessExport(exportInformation, surfaceToUpload);
return exportInformation;
}

View file

@ -31,37 +31,17 @@ namespace Greenshot.Plugin.Jira
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 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)
{

View file

@ -25,16 +25,8 @@ namespace Greenshot.Plugin.Jira
{
public class JiraEventArgs : EventArgs
{
public JiraEventTypes EventType
{
get;
set;
}
public JiraEventTypes EventType { get; set; }
public JiraDetails Details
{
get;
set;
}
public JiraDetails Details { get; set; }
}
}

View file

@ -31,7 +31,6 @@ 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.
@ -44,7 +43,9 @@ namespace Greenshot.Plugin.Jira
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>();
@ -79,6 +80,7 @@ namespace Greenshot.Plugin.Jira
{
return;
}
// free managed resources
_monitor.TitleChangeEvent -= MonitorTitleChangeEvent;
_monitor.Dispose();
@ -143,8 +145,13 @@ namespace Greenshot.Plugin.Jira
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 });
JiraEvent?.Invoke(this, new JiraEventArgs
{
Details = jiraDetails,
EventType = JiraEventTypes.DetectedNewJiraIssue
});
}
catch (Exception ex)
{
@ -163,11 +170,13 @@ namespace Greenshot.Plugin.Jira
{
return;
}
var jiraKeyMatch = _jiraKeyPattern.Match(windowTitle);
if (!jiraKeyMatch.Success)
{
return;
}
// Found a possible JIRA title
var jiraKey = jiraKeyMatch.Value;
var jiraKeyParts = jiraKey.Split('-');
@ -184,11 +193,16 @@ namespace Greenshot.Plugin.Jira
currentJiraDetails.SeenAt = DateTimeOffset.Now;
// Notify the order change
JiraEvent?.Invoke(this, new JiraEventArgs { Details = currentJiraDetails, EventType = JiraEventTypes.OrderChanged });
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
{
@ -205,6 +219,7 @@ namespace Greenshot.Plugin.Jira
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);

View file

@ -30,21 +30,25 @@ using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
using log4net;
namespace Greenshot.Plugin.Jira {
namespace Greenshot.Plugin.Jira
{
/// <summary>
/// This is the JiraPlugin base code
/// </summary>
[Plugin("Jira", true)]
public class JiraPlugin : IGreenshotPlugin {
public class JiraPlugin : IGreenshotPlugin
{
private static readonly ILog Log = LogManager.GetLogger(typeof(JiraPlugin));
private JiraConfiguration _config;
public void Dispose() {
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(bool disposing) {
protected void Dispose(bool disposing)
{
if (disposing)
{
var jiraConnector = SimpleServiceProvider.Current.GetInstance<JiraConnector>();
@ -56,7 +60,8 @@ namespace Greenshot.Plugin.Jira {
/// Implementation of the IGreenshotPlugin.Initialize
/// </summary>
/// <returns>true if plugin is initialized, false if not (doesn't show)</returns>
public bool Initialize() {
public bool Initialize()
{
// Register configuration (don't need the configuration itself)
_config = IniConfig.GetIniSection<JiraConfiguration>();
@ -94,7 +99,8 @@ namespace Greenshot.Plugin.Jira {
return true;
}
public void Shutdown() {
public void Shutdown()
{
Log.Debug("Jira Plugin shutdown.");
var jiraConnector = SimpleServiceProvider.Current.GetInstance<JiraConnector>();
jiraConnector?.Logout();
@ -103,18 +109,19 @@ namespace Greenshot.Plugin.Jira {
/// <summary>
/// Implementation of the IPlugin.Configure
/// </summary>
public void Configure() {
public void Configure()
{
string url = _config.Url;
if (ShowConfigDialog()) {
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)) {
jiraConnector.Logout();
Task.Run(async () =>
if (jiraConnector != null && jiraConnector.IsLoggedIn && !string.IsNullOrEmpty(url))
{
await jiraConnector.LoginAsync();
});
if (!url.Equals(_config.Url))
{
jiraConnector.Logout();
Task.Run(async () => { await jiraConnector.LoginAsync(); });
}
}
}
@ -132,6 +139,7 @@ namespace Greenshot.Plugin.Jira {
{
return true;
}
return false;
}
}

View file

@ -19,8 +19,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Greenshot.Plugin.Jira {
public enum LangKey {
namespace Greenshot.Plugin.Jira
{
public enum LangKey
{
upload_menu_item,
column_assignee,
column_created,

View file

@ -114,6 +114,7 @@ namespace Greenshot.Plugin.Jira
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,29 +28,37 @@ using GreenshotPlugin.Core;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
namespace Greenshot.Plugin.Office.Destinations {
namespace Greenshot.Plugin.Office.Destinations
{
/// <summary>
/// Description of PowerpointDestination.
/// </summary>
public class ExcelDestination : AbstractDestination {
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() {
static ExcelDestination()
{
ExePath = PluginUtils.GetExePath("EXCEL.EXE");
if (ExePath != null && File.Exists(ExePath)) {
if (ExePath != null && File.Exists(ExePath))
{
WindowDetails.AddProcessToExcludeFromFreeze("excel");
} else {
}
else
{
ExePath = null;
}
}
public ExcelDestination() {
public ExcelDestination()
{
}
public ExcelDestination(string workbookName) {
public ExcelDestination(string workbookName)
{
_workbookName = workbookName;
}
@ -66,31 +74,42 @@ namespace Greenshot.Plugin.Office.Destinations {
public override Image DisplayIcon => PluginUtils.GetCachedExeIcon(ExePath, !string.IsNullOrEmpty(_workbookName) ? IconWorkbook : IconApplication);
public override IEnumerable<IDestination> DynamicDestinations() {
foreach (string workbookName in ExcelExporter.GetWorkbooks()) {
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) {
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)$")) {
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) {
if (_workbookName != null)
{
ExcelExporter.InsertIntoExistingWorkbook(_workbookName, imageFile, surface.Image.Size);
} else {
}
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) {
if (createdFile)
{
ImageOutput.DeleteNamedTmpFile(imageFile);
}
return exportInformation;
}
}

View file

@ -28,8 +28,10 @@ using Greenshot.Plugin.Office.OfficeExport.Entities;
using GreenshotPlugin.Core;
using GreenshotPlugin.Interfaces;
namespace Greenshot.Plugin.Office.Destinations {
public class OneNoteDestination : AbstractDestination {
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";
@ -37,87 +39,105 @@ namespace Greenshot.Plugin.Office.Destinations {
private readonly OneNotePage page;
private readonly OneNoteExporter _oneNoteExporter = new OneNoteExporter();
static OneNoteDestination() {
static OneNoteDestination()
{
exePath = PluginUtils.GetExePath("ONENOTE.EXE");
if (exePath != null && File.Exists(exePath)) {
if (exePath != null && File.Exists(exePath))
{
WindowDetails.AddProcessToExcludeFromFreeze("onenote");
} else {
}
else
{
exePath = null;
}
}
public OneNoteDestination() {
public OneNoteDestination()
{
}
public OneNoteDestination(OneNotePage 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) {
public override string Description
{
get
{
if (page == null)
{
return "Microsoft OneNote";
} else {
}
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()) {
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) {
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails)
{
ExportInformation exportInformation = new ExportInformation(Designation, Description);
if (page == null) {
try {
if (page == null)
{
try
{
exportInformation.ExportMade = _oneNoteExporter.ExportToNewPage(surface);
} catch(Exception ex) {
}
catch (Exception ex)
{
exportInformation.ErrorMessage = ex.Message;
LOG.Error(ex);
}
} else {
try {
}
else
{
try
{
exportInformation.ExportMade = _oneNoteExporter.ExportToPage(surface, page);
} catch(Exception ex) {
}
catch (Exception ex)
{
exportInformation.ErrorMessage = ex.Message;
LOG.Error(ex);
}
}
return exportInformation;
}
}

View file

@ -32,11 +32,13 @@ using GreenshotPlugin.Interfaces.Plugin;
using Microsoft.Office.Interop.Outlook;
using Microsoft.Win32;
namespace Greenshot.Plugin.Office.Destinations {
namespace Greenshot.Plugin.Office.Destinations
{
/// <summary>
/// Description of OutlookDestination.
/// </summary>
public class OutlookDestination : AbstractDestination {
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;
@ -50,17 +52,25 @@ namespace Greenshot.Plugin.Office.Destinations {
private readonly OlObjectClass _outlookInspectorType;
private readonly OutlookEmailExporter _outlookEmailExporter = new();
static OutlookDestination() {
if (HasOutlook()) {
static OutlookDestination()
{
if (HasOutlook())
{
IsActiveFlag = true;
}
ExePath = PluginUtils.GetExePath("OUTLOOK.EXE");
if (ExePath != null && File.Exists(ExePath)) {
if (ExePath != null && File.Exists(ExePath))
{
WindowDetails.AddProcessToExcludeFromFreeze("outlook");
} else {
}
else
{
ExePath = GetOutlookExePath();
}
if (ExePath == null) {
if (ExePath == null)
{
IsActiveFlag = false;
}
}
@ -79,13 +89,16 @@ namespace Greenshot.Plugin.Office.Destinations {
{
return false;
}
return File.Exists(outlookPath);
}
public OutlookDestination() {
public OutlookDestination()
{
}
public OutlookDestination(string outlookInspectorCaption, OlObjectClass outlookInspectorType) {
public OutlookDestination(string outlookInspectorCaption, OlObjectClass outlookInspectorType)
{
_outlookInspectorCaption = outlookInspectorCaption;
_outlookInspectorType = outlookInspectorType;
}
@ -102,25 +115,32 @@ namespace Greenshot.Plugin.Office.Destinations {
public override Keys EditorShortcutKeys => Keys.Control | Keys.E;
public override Image DisplayIcon {
public override Image DisplayIcon
{
get
{
if (_outlookInspectorCaption == null)
{
return PluginUtils.GetCachedExeIcon(ExePath, IconApplication);
}
if (OlObjectClass.olAppointment.Equals(_outlookInspectorType)) {
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 IEnumerable<IDestination> DynamicDestinations() {
public override IEnumerable<IDestination> DynamicDestinations()
{
IDictionary<string, OlObjectClass> inspectorCaptions = _outlookEmailExporter.RetrievePossibleTargets();
if (inspectorCaptions != null) {
foreach (string inspectorCaption in inspectorCaptions.Keys) {
if (inspectorCaptions != null)
{
foreach (string inspectorCaption in inspectorCaptions.Keys)
{
yield return new OutlookDestination(inspectorCaption, inspectorCaptions[inspectorCaption]);
}
}
@ -133,49 +153,69 @@ namespace Greenshot.Plugin.Office.Destinations {
/// <param name="surface"></param>
/// <param name="captureDetails"></param>
/// <returns></returns>
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);
// Outlook logic
string tmpFile = captureDetails.Filename;
if (tmpFile == null || surface.Modified || !Regex.IsMatch(tmpFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$")) {
if (tmpFile == null || surface.Modified || !Regex.IsMatch(tmpFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$"))
{
tmpFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat());
} else {
}
else
{
Log.InfoFormat("Using already available file: {0}", tmpFile);
}
// Create a attachment name for the image
string attachmentName = captureDetails.Title;
if (!string.IsNullOrEmpty(attachmentName)) {
if (!string.IsNullOrEmpty(attachmentName))
{
attachmentName = attachmentName.Trim();
}
// Set default if non is set
if (string.IsNullOrEmpty(attachmentName)) {
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) {
if (_outlookInspectorCaption != null)
{
_outlookEmailExporter.ExportToInspector(_outlookInspectorCaption, tmpFile, attachmentName);
exportInformation.ExportMade = true;
} else {
if (!manuallyInitiated) {
}
else
{
if (!manuallyInitiated)
{
var inspectorCaptions = _outlookEmailExporter.RetrievePossibleTargets();
if (inspectorCaptions != null && inspectorCaptions.Count > 0) {
if (inspectorCaptions != null && inspectorCaptions.Count > 0)
{
var destinations = new List<IDestination>
{
new OutlookDestination()
};
foreach (string inspectorCaption in inspectorCaptions.Keys) {
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);
}
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,11 +29,13 @@ using GreenshotPlugin.Core;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
namespace Greenshot.Plugin.Office.Destinations {
namespace Greenshot.Plugin.Office.Destinations
{
/// <summary>
/// Description of PowerpointDestination.
/// </summary>
public class PowerpointDestination : AbstractDestination {
public class PowerpointDestination : AbstractDestination
{
private const int IconApplication = 0;
private const int IconPresentation = 1;
@ -41,30 +43,39 @@ namespace Greenshot.Plugin.Office.Destinations {
private readonly string _presentationName;
private readonly PowerpointExporter _powerpointExporter = new PowerpointExporter();
static PowerpointDestination() {
static PowerpointDestination()
{
ExePath = PluginUtils.GetExePath("POWERPNT.EXE");
if (ExePath != null && File.Exists(ExePath)) {
if (ExePath != null && File.Exists(ExePath))
{
WindowDetails.AddProcessToExcludeFromFreeze("powerpnt");
} else {
}
else
{
ExePath = null;
}
}
public PowerpointDestination() {
public PowerpointDestination()
{
}
public PowerpointDestination(string presentationName) {
public PowerpointDestination(string presentationName)
{
_presentationName = presentationName;
}
public override string Designation => "Powerpoint";
public override string Description {
public override string Description
{
get
{
if (_presentationName == null) {
if (_presentationName == null)
{
return "Microsoft Powerpoint";
}
return _presentationName;
}
}
@ -75,9 +86,12 @@ namespace Greenshot.Plugin.Office.Destinations {
public override bool IsActive => base.IsActive && ExePath != null;
public override Image DisplayIcon {
get {
if (!string.IsNullOrEmpty(_presentationName)) {
public override Image DisplayIcon
{
get
{
if (!string.IsNullOrEmpty(_presentationName))
{
return PluginUtils.GetCachedExeIcon(ExePath, IconPresentation);
}
@ -85,37 +99,55 @@ namespace Greenshot.Plugin.Office.Destinations {
}
}
public override IEnumerable<IDestination> DynamicDestinations() {
foreach (string presentationName in _powerpointExporter.GetPowerpointPresentations()) {
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) {
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)$")) {
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) {
if (_presentationName != null)
{
exportInformation.ExportMade = _powerpointExporter.ExportToPresentation(_presentationName, tmpFile, imageSize, captureDetails.Title);
} else {
if (!manuallyInitiated) {
}
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) {
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) {
}
else if (!exportInformation.ExportMade)
{
exportInformation.ExportMade = _powerpointExporter.InsertIntoNewPresentation(tmpFile, imageSize, captureDetails.Title);
}
}
ProcessExport(exportInformation, surface);
return exportInformation;
}

View file

@ -30,29 +30,35 @@ using GreenshotPlugin.Core;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
namespace Greenshot.Plugin.Office.Destinations {
namespace Greenshot.Plugin.Office.Destinations
{
/// <summary>
/// Description of EmailDestination.
/// </summary>
public class WordDestination : AbstractDestination {
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() {
static WordDestination()
{
ExePath = PluginUtils.GetExePath("WINWORD.EXE");
if (ExePath != null && !File.Exists(ExePath)) {
if (ExePath != null && !File.Exists(ExePath))
{
ExePath = null;
}
}
public WordDestination() {
public WordDestination()
{
}
public WordDestination(string wordCaption) {
public WordDestination(string wordCaption)
{
_documentCaption = wordCaption;
}
@ -68,62 +74,88 @@ namespace Greenshot.Plugin.Office.Destinations {
public override Image DisplayIcon => PluginUtils.GetCachedExeIcon(ExePath, !string.IsNullOrEmpty(_documentCaption) ? IconDocument : IconApplication);
public override IEnumerable<IDestination> DynamicDestinations() {
foreach (string wordCaption in _wordExporter.GetWordDocuments()) {
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) {
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)$")) {
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 {
if (_documentCaption != null)
{
try
{
_wordExporter.InsertIntoExistingDocument(_documentCaption, tmpFile);
exportInformation.ExportMade = true;
} catch (Exception) {
try {
}
catch (Exception)
{
try
{
_wordExporter.InsertIntoExistingDocument(_documentCaption, tmpFile);
exportInformation.ExportMade = true;
} catch (Exception ex) {
}
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) {
}
else
{
if (!manuallyInitiated)
{
var documents = _wordExporter.GetWordDocuments().ToList();
if (documents != null && documents.Count > 0) {
if (documents != null && documents.Count > 0)
{
var destinations = new List<IDestination>
{
new WordDestination()
};
foreach (string document in documents) {
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 {
try
{
_wordExporter.InsertIntoNewDocument(tmpFile, null, null);
exportInformation.ExportMade = true;
} catch(Exception) {
}
catch (Exception)
{
// Retry once, just in case
try {
try
{
_wordExporter.InsertIntoNewDocument(tmpFile, null, null);
exportInformation.ExportMade = true;
} catch (Exception ex) {
}
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 {
[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("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("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

@ -28,6 +28,7 @@ namespace Greenshot.Plugin.Office.OfficeInterop
/// Use the plain text format
/// </summary>
Text,
/// <summary>
/// Use HTML format
/// </summary>

View file

@ -28,26 +28,32 @@ namespace Greenshot.Plugin.Office.OfficeInterop
/// Office 97
/// </summary>
Office97 = 8,
/// <summary>
/// Office 2003
/// </summary>
Office2003 = 11,
/// <summary>
/// Office 2007
/// </summary>
Office2007 = 12,
/// <summary>
/// Office 2010
/// </summary>
Office2010 = 14,
/// <summary>
/// Office 2013
/// </summary>
Office2013 = 15,
/// <summary>
/// Office 2016
/// </summary>
Office2016 = 16,
/// <summary>
/// Office 2019
/// </summary>

View file

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

View file

@ -19,11 +19,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Greenshot.Plugin.Photobucket.Forms {
namespace Greenshot.Plugin.Photobucket.Forms
{
/// <summary>
/// Description of PasswordRequestForm.
/// </summary>
public partial class SettingsForm : PhotobucketForm {
public partial class SettingsForm : PhotobucketForm
{
public SettingsForm()
{
//

View file

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

View file

@ -25,26 +25,35 @@ using GreenshotPlugin.Controls;
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
namespace Greenshot.Plugin.Photobucket {
namespace Greenshot.Plugin.Photobucket
{
/// <summary>
/// Description of PhotobucketConfiguration.
/// </summary>
[IniSection("Photobucket", Description="Greenshot Photobucket Plugin configuration")]
public class PhotobucketConfiguration : IniSection {
[IniProperty("UploadFormat", Description="What file type to use for uploading", DefaultValue="png")]
[IniSection("Photobucket", Description = "Greenshot Photobucket Plugin configuration")]
public class PhotobucketConfiguration : 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")]
[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")]
[IniProperty("UploadReduceColors", Description = "Reduce color amount of the uploaded image to 256", DefaultValue = "False")]
public bool UploadReduceColors { get; set; }
[IniProperty("UsePageLink", Description = "Use pagelink instead of direct link on the clipboard", DefaultValue = "False")]
public bool UsePageLink { get; set; }
[IniProperty("Token", Description = "The Photobucket token", Encrypted=true, ExcludeIfNull=true)]
[IniProperty("Token", Description = "The Photobucket token", Encrypted = true, ExcludeIfNull = true)]
public string Token { get; set; }
[IniProperty("TokenSecret", Description = "The Photobucket token secret", Encrypted=true, ExcludeIfNull=true)]
[IniProperty("TokenSecret", Description = "The Photobucket token secret", Encrypted = true, ExcludeIfNull = true)]
public string TokenSecret { get; set; }
[IniProperty("SubDomain", Description = "The Photobucket api subdomain", Encrypted = true, ExcludeIfNull = true)]
public string SubDomain { get; set; }
[IniProperty("Username", Description = "The Photobucket api username", ExcludeIfNull = true)]
public string Username { get; set; }
@ -52,18 +61,19 @@ namespace Greenshot.Plugin.Photobucket {
/// A form for username/password
/// </summary>
/// <returns>bool true if OK was pressed, false if cancel</returns>
public bool ShowConfigDialog() {
public bool ShowConfigDialog()
{
SettingsForm settingsForm = null;
new PleaseWaitForm().ShowAndWait("Photobucket", Language.GetString("photobucket", LangKey.communication_wait),
delegate {
settingsForm = new SettingsForm();
}
delegate { settingsForm = new SettingsForm(); }
);
DialogResult result = settingsForm.ShowDialog();
if (result == DialogResult.OK) {
if (result == DialogResult.OK)
{
return true;
}
return false;
}
}

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