mirror of
https://github.com/Kopano-dev/kopano-ol-extension.git
synced 2023-10-10 13:37:40 +02:00
[KOE-17] Added replacement of placeholders in signatures based on GAB. To detect GAB changes, added the FeatureGAB.SyncFinished event, and CompletionTracker to track completion with asynchronous tasks.
This commit is contained in:
parent
e735aafc72
commit
6daac784c3
@ -326,6 +326,7 @@
|
|||||||
<Compile Include="UI\Outlook\CommandElement.cs" />
|
<Compile Include="UI\Outlook\CommandElement.cs" />
|
||||||
<Compile Include="UI\Outlook\MenuItem.cs" />
|
<Compile Include="UI\Outlook\MenuItem.cs" />
|
||||||
<Compile Include="UI\Outlook\Types.cs" />
|
<Compile Include="UI\Outlook\Types.cs" />
|
||||||
|
<Compile Include="Utils\CompletionTracker.cs" />
|
||||||
<Compile Include="Utils\DisposableWrapper.cs" />
|
<Compile Include="Utils\DisposableWrapper.cs" />
|
||||||
<Compile Include="Utils\ImageUtils.cs" />
|
<Compile Include="Utils\ImageUtils.cs" />
|
||||||
<Compile Include="Utils\RegistryUtil.cs" />
|
<Compile Include="Utils\RegistryUtil.cs" />
|
||||||
|
@ -97,9 +97,10 @@ namespace Acacia.Features.DebugSupport
|
|||||||
|
|
||||||
private void DebugCycle()
|
private void DebugCycle()
|
||||||
{
|
{
|
||||||
Tasks.Task(gab, "DebugCycle", () =>
|
// TODO: use completiontracker
|
||||||
|
Tasks.Task(null, gab, "DebugCycle", () =>
|
||||||
{
|
{
|
||||||
gab.FullResync();
|
gab.FullResync(null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +147,7 @@ namespace Acacia.Features.FreeBusy
|
|||||||
TcpClient client = listener.AcceptTcpClient();
|
TcpClient client = listener.AcceptTcpClient();
|
||||||
Interlocked.Increment(ref _requestCount);
|
Interlocked.Increment(ref _requestCount);
|
||||||
// And handle it in the UI thread to allow GAB access
|
// And handle it in the UI thread to allow GAB access
|
||||||
Tasks.Task(this, "FreeBusyHandler", () => server.HandleRequest(client));
|
Tasks.Task(null, this, "FreeBusyHandler", () => server.HandleRequest(client));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
@ -45,6 +45,14 @@ namespace Acacia.Features.GAB
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public delegate void GABSyncFinishedHandler(GABHandler gab);
|
||||||
|
public event GABSyncFinishedHandler SyncFinished;
|
||||||
|
|
||||||
|
public void OnGabSyncFinished(GABHandler gab)
|
||||||
|
{
|
||||||
|
SyncFinished?.Invoke(gab);
|
||||||
|
}
|
||||||
|
|
||||||
public static GABHandler FindGABForAccount(ZPushAccount account)
|
public static GABHandler FindGABForAccount(ZPushAccount account)
|
||||||
{
|
{
|
||||||
FeatureGAB gab = ThisAddIn.Instance.GetFeature<FeatureGAB>();
|
FeatureGAB gab = ThisAddIn.Instance.GetFeature<FeatureGAB>();
|
||||||
@ -305,7 +313,7 @@ namespace Acacia.Features.GAB
|
|||||||
|
|
||||||
#region Resync
|
#region Resync
|
||||||
|
|
||||||
internal void FullResync()
|
internal void FullResync(CompletionTracker completion)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -345,10 +353,12 @@ namespace Acacia.Features.GAB
|
|||||||
int remaining = _gabsByDomainName.Count;
|
int remaining = _gabsByDomainName.Count;
|
||||||
foreach (GABHandler gab in _gabsByDomainName.Values)
|
foreach (GABHandler gab in _gabsByDomainName.Values)
|
||||||
{
|
{
|
||||||
|
CompletionTracker partCompletion = new CompletionTracker(() => OnGabSyncFinished(gab));
|
||||||
|
// TODO: merge partCompletion into total completion
|
||||||
Logger.Instance.Debug(this, "FullResync: Starting resync: {0}", gab.DisplayName);
|
Logger.Instance.Debug(this, "FullResync: Starting resync: {0}", gab.DisplayName);
|
||||||
Tasks.Task(this, "FullResync", () =>
|
Tasks.Task(partCompletion, this, "FullResync", () =>
|
||||||
{
|
{
|
||||||
gab.FullResync();
|
gab.FullResync(partCompletion);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -662,15 +672,20 @@ namespace Acacia.Features.GAB
|
|||||||
|
|
||||||
++_processing;
|
++_processing;
|
||||||
Logger.Instance.Trace(this, "Processing GAB message: {0} - {1}", account, _processing);
|
Logger.Instance.Trace(this, "Processing GAB message: {0} - {1}", account, _processing);
|
||||||
try
|
CompletionTracker completion = new CompletionTracker(() => OnGabSyncFinished(gab));
|
||||||
|
using (completion.Begin())
|
||||||
{
|
{
|
||||||
gab.Process(item);
|
try
|
||||||
DoEmptyDeletedItems();
|
{
|
||||||
}
|
gab.Process(completion, item);
|
||||||
finally
|
// TODO: this will probably run while still processing, use completion tracker
|
||||||
{
|
DoEmptyDeletedItems();
|
||||||
Logger.Instance.Trace(this, "Processed GAB message: {0} - {1}", account, _processing);
|
}
|
||||||
--_processing;
|
finally
|
||||||
|
{
|
||||||
|
Logger.Instance.Trace(this, "Processed GAB message: {0} - {1}", account, _processing);
|
||||||
|
--_processing;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,10 +128,10 @@ namespace Acacia.Features.GAB
|
|||||||
|
|
||||||
#region Processing
|
#region Processing
|
||||||
|
|
||||||
public void FullResync()
|
public void FullResync(CompletionTracker completion)
|
||||||
{
|
{
|
||||||
ClearContacts();
|
ClearContacts();
|
||||||
Process(null);
|
Process(completion, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ClearContacts()
|
private void ClearContacts()
|
||||||
@ -167,27 +167,30 @@ namespace Acacia.Features.GAB
|
|||||||
/// Processes the GAB message(s).
|
/// Processes the GAB message(s).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="item">If specified, this item has changed. If null, means a global check should be performed</param>
|
/// <param name="item">If specified, this item has changed. If null, means a global check should be performed</param>
|
||||||
public void Process(IZPushItem item)
|
public void Process(CompletionTracker completion, IZPushItem item)
|
||||||
{
|
{
|
||||||
try
|
using (CompletionTracker.Step step = completion?.Begin())
|
||||||
{
|
{
|
||||||
if (item == null)
|
try
|
||||||
{
|
{
|
||||||
if (Folder != null)
|
if (item == null)
|
||||||
ProcessMessages();
|
{
|
||||||
|
if (Folder != null)
|
||||||
|
ProcessMessages(completion);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ProcessMessage(completion, item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
ProcessMessage(item);
|
Logger.Instance.Error(this, "Exception in GAB.Process: {0}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
Logger.Instance.Error(this, "Exception in GAB.Process: {0}", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProcessMessages()
|
private void ProcessMessages(CompletionTracker completion)
|
||||||
{
|
{
|
||||||
if (!_feature.ProcessFolder)
|
if (!_feature.ProcessFolder)
|
||||||
return;
|
return;
|
||||||
@ -207,12 +210,12 @@ namespace Acacia.Features.GAB
|
|||||||
Logger.Instance.Trace(this, "Checking chunk: {0}", item.Subject);
|
Logger.Instance.Trace(this, "Checking chunk: {0}", item.Subject);
|
||||||
if (_feature.ProcessItems2)
|
if (_feature.ProcessItems2)
|
||||||
{
|
{
|
||||||
Tasks.Task(_feature, "ProcessChunk", () =>
|
Tasks.Task(completion, _feature, "ProcessChunk", () =>
|
||||||
{
|
{
|
||||||
using (IItem item2 = Folder.GetItemById(entryId))
|
using (IItem item2 = Folder.GetItemById(entryId))
|
||||||
{
|
{
|
||||||
if (item2 != null)
|
if (item2 != null)
|
||||||
ProcessMessage((IZPushItem)item2);
|
ProcessMessage(completion, (IZPushItem)item2);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -225,7 +228,7 @@ namespace Acacia.Features.GAB
|
|||||||
public const string PROP_GAB_ID = "ZPushId";
|
public const string PROP_GAB_ID = "ZPushId";
|
||||||
public const string PROP_CURRENT_SEQUENCE = "ZPushCurrentSequence";
|
public const string PROP_CURRENT_SEQUENCE = "ZPushCurrentSequence";
|
||||||
|
|
||||||
private void ProcessMessage(IZPushItem item)
|
private void ProcessMessage(CompletionTracker completion, IZPushItem item)
|
||||||
{
|
{
|
||||||
if (!_feature.ProcessMessage)
|
if (!_feature.ProcessMessage)
|
||||||
return;
|
return;
|
||||||
@ -281,7 +284,7 @@ namespace Acacia.Features.GAB
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create the new contacts
|
// Create the new contacts
|
||||||
ProcessChunkBody(item, index);
|
ProcessChunkBody(completion, item, index);
|
||||||
|
|
||||||
// Update the state
|
// Update the state
|
||||||
SetChunkStateString(index, item.Location);
|
SetChunkStateString(index, item.Location);
|
||||||
@ -460,14 +463,14 @@ namespace Acacia.Features.GAB
|
|||||||
return value as ValueType;
|
return value as ValueType;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProcessChunkBody(IZPushItem item, ChunkIndex index)
|
private void ProcessChunkBody(CompletionTracker completion, IZPushItem item, ChunkIndex index)
|
||||||
{
|
{
|
||||||
// Process the body
|
// Process the body
|
||||||
foreach (var entry in JSONUtils.Deserialise(item.Body))
|
foreach (var entry in JSONUtils.Deserialise(item.Body))
|
||||||
{
|
{
|
||||||
string id = entry.Key;
|
string id = entry.Key;
|
||||||
Dictionary<string, object> value = (Dictionary<string, object>)entry.Value;
|
Dictionary<string, object> value = (Dictionary<string, object>)entry.Value;
|
||||||
Tasks.Task(_feature, "CreateItem", () => CreateObject(index, id, value));
|
Tasks.Task(completion, _feature, "CreateItem", () => CreateObject(index, id, value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ namespace Acacia.Features.GAB
|
|||||||
// Allow null feature for designer
|
// Allow null feature for designer
|
||||||
if (_feature != null)
|
if (_feature != null)
|
||||||
{
|
{
|
||||||
_feature.FullResync();
|
_feature.FullResync(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
/// Copyright 2017 Kopano b.v.
|
|
||||||
|
using Acacia.Features.GAB;
|
||||||
|
/// Copyright 2017 Kopano b.v.
|
||||||
///
|
///
|
||||||
/// This program is free software: you can redistribute it and/or modify
|
/// This program is free software: you can redistribute it and/or modify
|
||||||
/// it under the terms of the GNU Affero General Public License, version 3,
|
/// it under the terms of the GNU Affero General Public License, version 3,
|
||||||
@ -13,7 +15,6 @@
|
|||||||
/// along with this program.If not, see<http://www.gnu.org/licenses/>.
|
/// along with this program.If not, see<http://www.gnu.org/licenses/>.
|
||||||
///
|
///
|
||||||
/// Consult LICENSE file for details
|
/// Consult LICENSE file for details
|
||||||
|
|
||||||
using Acacia.Stubs;
|
using Acacia.Stubs;
|
||||||
using Acacia.Stubs.OutlookWrappers;
|
using Acacia.Stubs.OutlookWrappers;
|
||||||
using Acacia.Utils;
|
using Acacia.Utils;
|
||||||
@ -48,9 +49,16 @@ namespace Acacia.Features.Signatures
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
private FeatureGAB _gab;
|
||||||
|
|
||||||
public override void Startup()
|
public override void Startup()
|
||||||
{
|
{
|
||||||
Watcher.AccountDiscovered += Watcher_AccountDiscovered;
|
Watcher.AccountDiscovered += Watcher_AccountDiscovered;
|
||||||
|
_gab = ThisAddIn.Instance.GetFeature<FeatureGAB>();
|
||||||
|
if (_gab != null)
|
||||||
|
{
|
||||||
|
_gab.SyncFinished += GAB_SyncFinished;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Watcher_AccountDiscovered(ZPushAccount account)
|
private void Watcher_AccountDiscovered(ZPushAccount account)
|
||||||
@ -146,11 +154,7 @@ namespace Acacia.Features.Signatures
|
|||||||
|
|
||||||
private string StoreSignature(ISignatures signatures, ZPushAccount account, Signature signatureInfo)
|
private string StoreSignature(ISignatures signatures, ZPushAccount account, Signature signatureInfo)
|
||||||
{
|
{
|
||||||
string name = SignatureLocalName.ReplacePercentStrings(new Dictionary<string, string>
|
string name = GetSignatureName(signatures, account, signatureInfo.name);
|
||||||
{
|
|
||||||
{ "account", account.DisplayName },
|
|
||||||
{ "name", signatureInfo.name }
|
|
||||||
});
|
|
||||||
|
|
||||||
// Remove any existing signature
|
// Remove any existing signature
|
||||||
try
|
try
|
||||||
@ -176,11 +180,129 @@ namespace Acacia.Features.Signatures
|
|||||||
// Create the new signature
|
// Create the new signature
|
||||||
using (ISignature signature = signatures.Add(name))
|
using (ISignature signature = signatures.Add(name))
|
||||||
{
|
{
|
||||||
signature.SetContent(signatureInfo.content, signatureInfo.isHTML ? ISignatureFormat.HTML : ISignatureFormat.Text);
|
if (!HasPlaceholders(signatureInfo))
|
||||||
// TODO: generate text version if we get an HTML?
|
{
|
||||||
|
// Simple, set signature straight away
|
||||||
|
signature.SetContent(signatureInfo.content, signatureInfo.isHTML ? ISignatureFormat.HTML : ISignatureFormat.Text);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// There are placeholders. Create a template and hook into the GAB for patching
|
||||||
|
signature.SetContentTemplate(signatureInfo.content, signatureInfo.isHTML ? ISignatureFormat.HTML : ISignatureFormat.Text);
|
||||||
|
|
||||||
|
// Try replacing straight away
|
||||||
|
GABHandler gab = FeatureGAB.FindGABForAccount(account);
|
||||||
|
if (gab != null)
|
||||||
|
ReplacePlaceholders(gab, name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string GetSignatureName(ISignatures signatures, ZPushAccount account, string name)
|
||||||
|
{
|
||||||
|
return SignatureLocalName.ReplaceStringTokens("%", "%", new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "account", account.DisplayName },
|
||||||
|
{ "name", name }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool HasPlaceholders(Signature signature)
|
||||||
|
{
|
||||||
|
return signature.content.IndexOf("{%") >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GAB_SyncFinished(GABHandler gab)
|
||||||
|
{
|
||||||
|
ReplacePlaceholders(gab, gab.ActiveAccount.Account.SignatureNewMessage, gab.ActiveAccount.Account.SignatureNewMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReplacePlaceholders(GABHandler gab, params string[] signatures)
|
||||||
|
{
|
||||||
|
IContactItem us = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IAccount account = gab.ActiveAccount.Account;
|
||||||
|
|
||||||
|
// Look for the email address. If found, use the account associated with the GAB
|
||||||
|
using (ISearch<IContactItem> search = gab.Contacts.Search<IContactItem>())
|
||||||
|
{
|
||||||
|
search.AddField("urn:schemas:contacts:email1").SetOperation(SearchOperation.Equal, account.SmtpAddress);
|
||||||
|
IItem result = search.SearchOne();
|
||||||
|
us = result as IContactItem;
|
||||||
|
if (result != null && result != us)
|
||||||
|
result.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (string signatureName in signatures)
|
||||||
|
{
|
||||||
|
ReplacePlaceholders(gab.ActiveAccount, us, signatureName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
Logger.Instance.Error(this, "Exception in ReplacePlaceholders: {0}", e);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (us != null)
|
||||||
|
us.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReplacePlaceholders(ZPushAccount account, IContactItem us, string signatureName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(signatureName))
|
||||||
|
return;
|
||||||
|
|
||||||
|
using (ISignatures signatures = ThisAddIn.Instance.GetSignatures())
|
||||||
|
{
|
||||||
|
using (ISignature signature = signatures.Get(signatureName))
|
||||||
|
{
|
||||||
|
if (signature == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (ISignatureFormat format in Enum.GetValues(typeof(ISignatureFormat)))
|
||||||
|
{
|
||||||
|
string template = signature.GetContentTemplate(format);
|
||||||
|
if (template != null)
|
||||||
|
{
|
||||||
|
string replaced = template.ReplaceStringTokens("{%", "}", (token) =>
|
||||||
|
{
|
||||||
|
// TODO: generalise this
|
||||||
|
if (token == "firstname") return us.FirstName ?? "";
|
||||||
|
if (token == "initials") return us.Initials ?? "";
|
||||||
|
if (token == "lastname") return us.LastName ?? "";
|
||||||
|
if (token == "displayname") return us.FullName ?? "";
|
||||||
|
if (token == "title") return us.Title ?? "";
|
||||||
|
if (token == "company") return us.CompanyName ?? "";
|
||||||
|
// TODO if (token == "department") return us.;
|
||||||
|
if (token == "office") return us.OfficeLocation ?? "";
|
||||||
|
// if (token == "assistant") return us.;
|
||||||
|
if (token == "phone") return us.BusinessTelephoneNumber ?? us.MobileTelephoneNumber ?? "";
|
||||||
|
if (token == "primary_email") return us.Email1Address ?? "";
|
||||||
|
if (token == "address") return us.BusinessAddress ?? "";
|
||||||
|
if (token == "city") return us.BusinessAddressCity ?? "";
|
||||||
|
if (token == "state") return us.BusinessAddressState ?? "";
|
||||||
|
if (token == "zipcode") return us.BusinessAddressPostalCode ?? "";
|
||||||
|
if (token == "country") return us.BusinessAddressState ?? "";
|
||||||
|
if (token == "phone_business") return us.BusinessTelephoneNumber ?? "";
|
||||||
|
// TODO if (token == "phone_business2") return us.BusinessTelephoneNumber;
|
||||||
|
if (token == "phone_fax") return us.BusinessFaxNumber ?? "";
|
||||||
|
// TODO if (token == "phone_assistant") return us.FirstName;
|
||||||
|
if (token == "phone_home") return us.HomeTelephoneNumber ?? "";
|
||||||
|
//if (token == "phone_home2") return us.HomeTelephoneNumber;
|
||||||
|
if (token == "phone_mobile") return us.MobileTelephoneNumber ?? "";
|
||||||
|
if (token == "phone_pager") return us.PagerNumber ?? "";
|
||||||
|
return "";
|
||||||
|
});
|
||||||
|
signature.SetContent(replaced, format);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -8,16 +8,11 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Acacia.Native
|
namespace Acacia.Native
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Explicit)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
unsafe public struct ACCT_VARIANT
|
unsafe public struct ACCT_VARIANT
|
||||||
{
|
{
|
||||||
[FieldOffset(0)]
|
|
||||||
public uint dwType;
|
public uint dwType;
|
||||||
|
|
||||||
[FieldOffset(4)]
|
|
||||||
public uint dwAlignPad;
|
|
||||||
|
|
||||||
[FieldOffset(8)]
|
|
||||||
public char* lpszW;
|
public char* lpszW;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,6 +200,7 @@ namespace Acacia.Native.MAPI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: check this on 32 bit machines
|
||||||
[StructLayout(LayoutKind.Explicit)]
|
[StructLayout(LayoutKind.Explicit)]
|
||||||
unsafe public struct SRestriction
|
unsafe public struct SRestriction
|
||||||
{
|
{
|
||||||
|
@ -16,5 +16,7 @@ namespace Acacia.Stubs
|
|||||||
{
|
{
|
||||||
void Delete();
|
void Delete();
|
||||||
void SetContent(string content, ISignatureFormat format);
|
void SetContent(string content, ISignatureFormat format);
|
||||||
|
void SetContentTemplate(string content, ISignatureFormat format);
|
||||||
|
string GetContentTemplate(ISignatureFormat format);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,9 @@ namespace Acacia.Stubs.OutlookWrappers
|
|||||||
{
|
{
|
||||||
private static readonly string[] SUFFIXES =
|
private static readonly string[] SUFFIXES =
|
||||||
{
|
{
|
||||||
"htm", "html", "rtf", "txt"
|
"htm", "html", "rtf", "txt",
|
||||||
|
"htm.template", "html.template", "rtf.template", "txt.template",
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private static string BasePath
|
private static string BasePath
|
||||||
@ -63,18 +65,44 @@ namespace Acacia.Stubs.OutlookWrappers
|
|||||||
// TODO: additional files folder? We never create it
|
// TODO: additional files folder? We never create it
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetContent(string content, ISignatureFormat format)
|
private string GetPath(ISignatureFormat format, bool template)
|
||||||
{
|
{
|
||||||
// Determine suffix
|
// Determine suffix
|
||||||
string suffix = "txt";
|
string suffix = "txt";
|
||||||
switch(format)
|
switch (format)
|
||||||
{
|
{
|
||||||
case ISignatureFormat.HTML: suffix = "htm"; break;
|
case ISignatureFormat.HTML: suffix = "htm"; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write
|
if (template)
|
||||||
string path = GetPath(_name, suffix);
|
suffix += ".template";
|
||||||
|
|
||||||
|
return GetPath(_name, suffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetContent(string content, ISignatureFormat format)
|
||||||
|
{
|
||||||
|
string path = GetPath(format, false);
|
||||||
File.WriteAllText(path, content);
|
File.WriteAllText(path, content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetContentTemplate(string content, ISignatureFormat format)
|
||||||
|
{
|
||||||
|
string path = GetPath(format, true);
|
||||||
|
File.WriteAllText(path, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetContentTemplate(ISignatureFormat format)
|
||||||
|
{
|
||||||
|
string path = GetPath(format, true);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return File.ReadAllText(path);
|
||||||
|
}
|
||||||
|
catch(Exception)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ namespace Acacia.Stubs.OutlookWrappers
|
|||||||
// Check existing stores
|
// Check existing stores
|
||||||
foreach(NSOutlook.Store store in _item)
|
foreach(NSOutlook.Store store in _item)
|
||||||
{
|
{
|
||||||
Tasks.Task(null, "AddStore", () =>
|
Tasks.Task(null, null, "AddStore", () =>
|
||||||
{
|
{
|
||||||
StoreAdded(store);
|
StoreAdded(store);
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Acacia.Utils
|
||||||
|
{
|
||||||
|
public class CompletionTracker
|
||||||
|
{
|
||||||
|
public class Step : IDisposable
|
||||||
|
{
|
||||||
|
private readonly CompletionTracker _tracker;
|
||||||
|
|
||||||
|
public Step(CompletionTracker tracker)
|
||||||
|
{
|
||||||
|
this._tracker = tracker;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_tracker.End();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly Action _completion;
|
||||||
|
private int steps = 0;
|
||||||
|
|
||||||
|
public CompletionTracker(Action completion)
|
||||||
|
{
|
||||||
|
this._completion = completion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Begins a sub-step.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A step. This may be disposed, or End may be used</returns>
|
||||||
|
public Step Begin()
|
||||||
|
{
|
||||||
|
Interlocked.Increment(ref steps);
|
||||||
|
return new Step(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void End()
|
||||||
|
{
|
||||||
|
if (Interlocked.Decrement(ref steps) == 0)
|
||||||
|
{
|
||||||
|
// Done
|
||||||
|
_completion();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -108,20 +108,32 @@ namespace Acacia.Utils
|
|||||||
|
|
||||||
#region Formatting / Replacement
|
#region Formatting / Replacement
|
||||||
|
|
||||||
public static string ReplacePercentStrings(this string s, Dictionary<string, string> replacements)
|
public delegate string TokenReplacer(string token);
|
||||||
|
|
||||||
|
public static string ReplaceStringTokens(this string s, string open, string close, TokenReplacer replacer)
|
||||||
{
|
{
|
||||||
return Regex.Replace(s, @"%(\w+)%", (m) =>
|
return Regex.Replace(s, Regex.Escape(open) + @"(\w+)" + Regex.Escape(close), (m) =>
|
||||||
{
|
{
|
||||||
string replacement;
|
|
||||||
var key = m.Groups[1].Value;
|
var key = m.Groups[1].Value;
|
||||||
if (replacements.TryGetValue(key, out replacement))
|
string replacement = replacer(key);
|
||||||
{
|
if (replacement == null)
|
||||||
return Convert.ToString(replacement);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
return m.Groups[0].Value;
|
return m.Groups[0].Value;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return replacement;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ReplaceStringTokens(this string s, string open, string close, Dictionary<string, string> replacements)
|
||||||
|
{
|
||||||
|
return s.ReplaceStringTokens(open, close, (token) =>
|
||||||
|
{
|
||||||
|
string replacement = null;
|
||||||
|
replacements.TryGetValue(token, out replacement);
|
||||||
|
return replacement;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,12 +26,15 @@ namespace Acacia.Utils
|
|||||||
{
|
{
|
||||||
public class AcaciaTask
|
public class AcaciaTask
|
||||||
{
|
{
|
||||||
|
private readonly CompletionTracker _completion;
|
||||||
public readonly Feature Owner;
|
public readonly Feature Owner;
|
||||||
public readonly string Name;
|
public readonly string Name;
|
||||||
public readonly Action Action;
|
public readonly Action Action;
|
||||||
|
|
||||||
public AcaciaTask(Feature owner, string name, Action action)
|
public AcaciaTask(CompletionTracker completion, Feature owner, string name, Action action)
|
||||||
{
|
{
|
||||||
|
this._completion = completion;
|
||||||
|
completion?.Begin();
|
||||||
Owner = owner;
|
Owner = owner;
|
||||||
Name = name;
|
Name = name;
|
||||||
Action = action;
|
Action = action;
|
||||||
@ -62,6 +65,10 @@ namespace Acacia.Utils
|
|||||||
Logger.Instance.Error(Owner, "Exception in task {0}: {1}", Name, e);
|
Logger.Instance.Error(Owner, "Exception in task {0}: {1}", Name, e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_completion?.End();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -122,9 +129,9 @@ namespace Acacia.Utils
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Task(Feature owner, string name, Action action)
|
public static void Task(CompletionTracker completion, Feature owner, string name, Action action)
|
||||||
{
|
{
|
||||||
Task(new AcaciaTask(owner, name, action));
|
Task(new AcaciaTask(completion, owner, name, action));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Task(AcaciaTask task)
|
public static void Task(AcaciaTask task)
|
||||||
|
@ -68,13 +68,13 @@ namespace Acacia.ZPush
|
|||||||
// Process existing accounts
|
// Process existing accounts
|
||||||
foreach (IAccount account in _stores.Accounts)
|
foreach (IAccount account in _stores.Accounts)
|
||||||
{
|
{
|
||||||
Tasks.Task(null, "AccountCheck", () =>
|
Tasks.Task(null, null, "AccountCheck", () =>
|
||||||
{
|
{
|
||||||
AccountAdded(account);
|
AccountAdded(account);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Tasks.Task(null, "AccountCheckDone", () =>
|
Tasks.Task(null, null, "AccountCheckDone", () =>
|
||||||
{
|
{
|
||||||
_watcher.OnAccountsScanned();
|
_watcher.OnAccountsScanned();
|
||||||
});
|
});
|
||||||
|
@ -85,7 +85,7 @@ namespace Acacia.ZPush
|
|||||||
// Notify any listeners
|
// Notify any listeners
|
||||||
if (Available != null)
|
if (Available != null)
|
||||||
{
|
{
|
||||||
Tasks.Task(null, "Watcher_WatchingFolder", () => Available(folder));
|
Tasks.Task(null, null, "Watcher_WatchingFolder", () => Available(folder));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ namespace Acacia.ZPush
|
|||||||
// Recurse the children
|
// Recurse the children
|
||||||
foreach (IFolder subfolder in _folder.SubFolders)
|
foreach (IFolder subfolder in _folder.SubFolders)
|
||||||
{
|
{
|
||||||
Tasks.Task(null, "WatchChild", () => WatchChild(subfolder, true));
|
Tasks.Task(null, null, "WatchChild", () => WatchChild(subfolder, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ namespace Acacia.ZPush
|
|||||||
{
|
{
|
||||||
if (_actionConnection != null)
|
if (_actionConnection != null)
|
||||||
{
|
{
|
||||||
return new AcaciaTask(_owner, _name, () =>
|
return new AcaciaTask(null, _owner, _name, () =>
|
||||||
{
|
{
|
||||||
// TODO: reuse connections
|
// TODO: reuse connections
|
||||||
using (ZPushConnection con = account.Connect())
|
using (ZPushConnection con = account.Connect())
|
||||||
@ -68,7 +68,7 @@ namespace Acacia.ZPush
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return new AcaciaTask(_owner, _name, () => _action(account));
|
return new AcaciaTask(null, _owner, _name, () => _action(account));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,7 +111,7 @@ namespace Acacia.ZPush
|
|||||||
if (account.Account.HasPassword)
|
if (account.Account.HasPassword)
|
||||||
{
|
{
|
||||||
// Send an OOF request to get the OOF state and capabilities
|
// Send an OOF request to get the OOF state and capabilities
|
||||||
Tasks.Task(null, "ZPushCheck: " + account.DisplayName, () =>
|
Tasks.Task(null, null, "ZPushCheck: " + account.DisplayName, () =>
|
||||||
{
|
{
|
||||||
// TODO: if this fails, retry?
|
// TODO: if this fails, retry?
|
||||||
ActiveSync.SettingsOOF oof;
|
ActiveSync.SettingsOOF oof;
|
||||||
|
Loading…
Reference in New Issue
Block a user