[KOE-113][KOE-24] Added functionality to Sync dialog. Added ability to delete .ost during restart to perform a full resync.

This commit is contained in:
Patrick Simpson 2017-05-17 16:35:05 +02:00
parent 420868106c
commit c5fcf62e35
19 changed files with 477 additions and 38 deletions

View File

@ -294,6 +294,7 @@
<DependentUpon>SignaturesSettings.cs</DependentUpon>
</Compile>
<Compile Include="Features\SyncState\FeatureSyncState.cs" />
<Compile Include="Features\SyncState\SyncState.cs" />
<Compile Include="Features\SyncState\SyncStateDialog.cs">
<SubType>Form</SubType>
</Compile>

View File

@ -100,7 +100,7 @@ namespace Acacia.Features.DebugSupport
// TODO: use completiontracker
Tasks.Task(null, gab, "DebugCycle", () =>
{
gab.FullResync(null);
gab.FullResync(null, null);
});
}

View File

@ -335,10 +335,16 @@ namespace Acacia.Features.GAB
#region Resync
internal void FullResync(CompletionTracker completion)
/// <summary>
/// Performs a full resync.
/// </summary>
/// <param name="completion">The completion tracker, or null.</param>
/// <param name="accounts">The accounts to resync, or null to resync all</param>
internal void FullResync(CompletionTracker completion, ZPushAccount[] accounts)
{
try
{
// TODO: implement per-account resyncing
Logger.Instance.Trace(this, "FullResync begin: {0}", _processing);
BeginProcessing();

View File

@ -50,7 +50,7 @@ namespace Acacia.Features.GAB
// Allow null feature for designer
if (_feature != null)
{
_feature.FullResync(null);
_feature.FullResync(null, null);
}
}
}

View File

@ -106,7 +106,12 @@ namespace Acacia.Features.Signatures
internal void ResyncAll()
{
foreach(ZPushAccount account in Watcher.Accounts.GetAccounts())
Resync(Watcher.Accounts.GetAccounts().ToArray());
}
internal void Resync(ZPushAccount[] accounts)
{
foreach (ZPushAccount account in accounts)
{
if (account.Confirmed == ZPushAccount.ConfirmationType.IsZPush)
{

View File

@ -29,6 +29,9 @@ using Acacia.UI.Outlook;
using System.Drawing;
using Acacia.ZPush.Connect;
using Acacia.ZPush.Connect.Soap;
using Acacia.Features.GAB;
using Acacia.Features.Signatures;
using Acacia.UI;
// Prevent field assignment warnings
#pragma warning disable 0649
@ -253,12 +256,14 @@ namespace Acacia.Features.SyncState
{
long total = 0;
long done = 0;
foreach(ContentData content in Content.Values)
IsSyncing = false;
foreach (ContentData content in Content.Values)
{
if (content.IsSyncing)
{
total += content.Sync.total;
done += content.Sync.done;
IsSyncing = true;
}
}
@ -278,6 +283,12 @@ namespace Acacia.Features.SyncState
private set;
}
public bool IsSyncing
{
get;
private set;
}
#endregion
}
@ -320,7 +331,6 @@ namespace Acacia.Features.SyncState
}
}
// Calculate progress and update
if (done == 0)
_state.SyncProgress = total == 0 ? 1 : 0;
@ -332,5 +342,126 @@ namespace Acacia.Features.SyncState
{
new SyncStateDialog(this).ShowDialog();
}
private class SyncStateImpl : SyncState
{
private readonly FeatureSyncState _feature;
private readonly ZPushAccount[] _accounts;
private readonly bool[] _canResync;
// Additional features for syncing
private readonly FeatureGAB _featureGAB = ThisAddIn.Instance.GetFeature<FeatureGAB>();
private readonly FeatureSignatures _featureSignatures = ThisAddIn.Instance.GetFeature<FeatureSignatures>();
public SyncStateImpl(FeatureSyncState feature, ZPushAccount[] accounts)
{
this._feature = feature;
this._accounts = accounts;
_canResync = new bool[Enum.GetNames(typeof(ResyncOption)).Length];
_canResync[(int)ResyncOption.GAB] = _featureGAB != null;
_canResync[(int)ResyncOption.Signatures] = _featureSignatures != null;
_canResync[(int)ResyncOption.ServerData] = ThisAddIn.Instance.Watcher.Sync.Enabled;
_canResync[(int)ResyncOption.Full] = true;
Update();
}
public long Done
{
get;
private set;
}
public bool IsSyncing
{
get;
private set;
}
public long Remaining
{
get;
private set;
}
public long Total
{
get;
private set;
}
public bool CanResync(ResyncOption option)
{
// TODO: check if outlook is not offline?
return _canResync[(int)option];
}
public bool Resync(ResyncOption option)
{
if (!CanResync(option))
return true;
_canResync [(int)option] = false;
switch(option)
{
case ResyncOption.GAB:
// TODO: use completion tracker if not synching
_featureGAB.FullResync(null, _accounts);
return false;
case ResyncOption.Signatures:
ProgressDialog.Execute("SignaturesSync",
(ct) =>
{
_featureSignatures.Resync(_accounts);
return 0;
}
);
return true;
case ResyncOption.ServerData:
ProgressDialog.Execute("ServerSync",
(ct) =>
{
ThisAddIn.Instance.Watcher.Sync.ExecuteTasks(_accounts);
return 0;
}
);
return true;
case ResyncOption.Full:
ThisAddIn.Instance.RestartResync(_accounts);
return true;
}
return true;
}
public void Update()
{
Total = 0;
Done = 0;
IsSyncing = false;
foreach (ZPushAccount account in _accounts)
{
DeviceDetails details = account.GetFeatureData<DeviceDetails>(_feature, null);
if (details != null)
{
Total += details.Total;
Done += details.Done;
if (details.IsSyncing)
IsSyncing = true;
}
}
Remaining = Total - Done;
}
}
/// <summary>
/// Returns a SyncState for the specified account, or all accounts.
/// </summary>
/// <param name="account">The account, or null to fetch a SyncState for all accounts</param>
public SyncState GetSyncState(ZPushAccount account)
{
return new SyncStateImpl(this, account == null ? Watcher.Accounts.GetAccounts().ToArray() : new ZPushAccount[] { account });
}
}
}

View File

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Acacia.Features.SyncState
{
public enum ResyncOption
{
GAB, Signatures, ServerData, Full
}
public interface SyncState
{
/// <summary>
/// Returns the total amount of items to sync.
/// </summary>
long Total { get; }
/// <summary>
/// Returns the number of remaining items to sync.
/// </summary>
long Remaining { get; }
/// <summary>
/// Returns the number of items already synced;
/// </summary>
long Done { get; }
bool IsSyncing { get; }
/// <summary>
/// Resynchronises the specified option.
/// </summary>
/// <param name="option"></param>
/// <returns>True if the task is complete, false if it is still running.</returns>
bool Resync(ResyncOption option);
bool CanResync(ResyncOption option);
void Update();
}
}

View File

@ -99,6 +99,7 @@
this.comboAccounts.Items.AddRange(new object[] {
resources.GetString("comboAccounts.Items")});
this.comboAccounts.Name = "comboAccounts";
this.comboAccounts.SelectedIndexChanged += new System.EventHandler(this.comboAccounts_SelectedIndexChanged);
//
// _labelProgress
//
@ -132,7 +133,7 @@
this.buttonGAB.Tag = "";
this.buttonGAB.UseVisualStyleBackColor = true;
this.buttonGAB.ShowHint += new Acacia.Controls.KHintButton.HintEventHandler(this.ShowHint);
this.buttonGAB.Click += new System.EventHandler(this.button1_Click);
this.buttonGAB.Click += new System.EventHandler(this.SyncButton_Click);
//
// buttonSignatures
//
@ -140,6 +141,7 @@
this.buttonSignatures.Name = "buttonSignatures";
this.buttonSignatures.UseVisualStyleBackColor = true;
this.buttonSignatures.ShowHint += new Acacia.Controls.KHintButton.HintEventHandler(this.ShowHint);
this.buttonSignatures.Click += new System.EventHandler(this.SyncButton_Click);
//
// buttonServerData
//
@ -147,6 +149,7 @@
this.buttonServerData.Name = "buttonServerData";
this.buttonServerData.UseVisualStyleBackColor = true;
this.buttonServerData.ShowHint += new Acacia.Controls.KHintButton.HintEventHandler(this.ShowHint);
this.buttonServerData.Click += new System.EventHandler(this.SyncButton_Click);
//
// buttonFullResync
//
@ -154,6 +157,7 @@
this.buttonFullResync.Name = "buttonFullResync";
this.buttonFullResync.UseVisualStyleBackColor = true;
this.buttonFullResync.ShowHint += new Acacia.Controls.KHintButton.HintEventHandler(this.ShowHint);
this.buttonFullResync.Click += new System.EventHandler(this.buttonFullResync_Click);
//
// _labelResyncOption
//
@ -171,7 +175,6 @@
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "SyncStateDialog";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.SettingsDialog_FormClosing);
this._layout.ResumeLayout(false);
this._layout.PerformLayout();
this._layoutMain.ResumeLayout(false);

View File

@ -31,10 +31,19 @@ namespace Acacia.Features.SyncState
public partial class SyncStateDialog : KDialogNew
{
private readonly FeatureSyncState _feature;
private SyncState _syncState;
private readonly Button[] _syncButtons;
public SyncStateDialog(FeatureSyncState feature)
{
InitializeComponent();
// Ensure these are in sync with ResyncOption
_syncButtons = new Button[]
{
buttonGAB, buttonSignatures, buttonServerData, buttonFullResync
};
this._feature = feature;
comboAccounts.SelectedIndex = 0;
@ -43,22 +52,89 @@ namespace Acacia.Features.SyncState
comboAccounts.Items.Add(account);
}
private void buttonApply_Click(object sender, EventArgs e)
{
}
private void SettingsDialog_FormClosing(object sender, FormClosingEventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
}
private void ShowHint(object sender, KHintButton.HintEventArgs e)
{
_labelResyncOption.Text = e.Hint ?? string.Empty;
}
private void comboAccounts_SelectedIndexChanged(object sender, EventArgs e)
{
_syncState = _feature.GetSyncState(comboAccounts.SelectedItem as ZPushAccount);
UpdateUI();
}
private static readonly string[] OPTION_TEXT =
{
Properties.Resources.SyncState_Resync_Body_GAB,
string.Empty,
string.Empty,
string.Empty
};
private void SyncButton_Click(object sender, EventArgs e)
{
int i = Array.IndexOf(_syncButtons, sender);
if (i >= 0)
{
Cursor = Cursors.WaitCursor;
try
{
ResyncOption option = (ResyncOption)i;
bool done = _syncState.Resync(option);
UpdateUI();
Cursor = null;
if (!done && !string.IsNullOrEmpty(OPTION_TEXT[i]))
{
// Feedback
MessageBox.Show(this,
OPTION_TEXT[i],
Properties.Resources.SyncState_Resync_Caption,
MessageBoxButtons.OK,
MessageBoxIcon.Information
);
}
}
finally
{
Cursor = null;
}
}
}
private void buttonFullResync_Click(object sender, EventArgs e)
{
if (MessageBox.Show(this,
Properties.Resources.SyncState_FullResync_Body,
Properties.Resources.SyncState_FullResync_Caption,
MessageBoxButtons.YesNo,
MessageBoxIcon.Warning
) == DialogResult.Yes)
{
SyncButton_Click(sender, e);
}
}
private void UpdateUI()
{
// Set up the UI
foreach (ResyncOption option in Enum.GetValues(typeof(ResyncOption)))
{
_syncButtons[(int)option].Enabled = _syncState.CanResync(option);
}
if (_syncState.IsSyncing)
{
textRemaining.Text = _syncState.Remaining.ToString();
progress.Value = (int)(_syncState.Done * 100.0 / _syncState.Total);
}
else
{
textRemaining.Text = Properties.Resources.Ribbon_SyncState_Label_Done;
progress.Value = 100;
}
}
}
}

View File

@ -370,7 +370,7 @@
<value>3, 79</value>
</data>
<data name="_labelResync.Size" type="System.Drawing.Size, System.Drawing">
<value>77, 29</value>
<value>77, 35</value>
</data>
<data name="_labelResync.TabIndex" type="System.Int32, mscorlib">
<value>8</value>
@ -411,8 +411,11 @@
<data name="buttonGAB.Location" type="System.Drawing.Point, System.Drawing">
<value>86, 82</value>
</data>
<data name="buttonGAB.Padding" type="System.Windows.Forms.Padding, System.Windows.Forms">
<value>3, 3, 3, 3</value>
</data>
<data name="buttonGAB.Size" type="System.Drawing.Size, System.Drawing">
<value>242, 23</value>
<value>242, 29</value>
</data>
<data name="buttonGAB.TabIndex" type="System.Int32, mscorlib">
<value>0</value>
@ -448,10 +451,13 @@
<value>NoControl</value>
</data>
<data name="buttonSignatures.Location" type="System.Drawing.Point, System.Drawing">
<value>86, 111</value>
<value>86, 117</value>
</data>
<data name="buttonSignatures.Padding" type="System.Windows.Forms.Padding, System.Windows.Forms">
<value>3, 3, 3, 3</value>
</data>
<data name="buttonSignatures.Size" type="System.Drawing.Size, System.Drawing">
<value>242, 23</value>
<value>242, 29</value>
</data>
<data name="buttonSignatures.TabIndex" type="System.Int32, mscorlib">
<value>1</value>
@ -487,10 +493,13 @@
<value>NoControl</value>
</data>
<data name="buttonServerData.Location" type="System.Drawing.Point, System.Drawing">
<value>86, 140</value>
<value>86, 152</value>
</data>
<data name="buttonServerData.Padding" type="System.Windows.Forms.Padding, System.Windows.Forms">
<value>3, 3, 3, 3</value>
</data>
<data name="buttonServerData.Size" type="System.Drawing.Size, System.Drawing">
<value>242, 23</value>
<value>242, 29</value>
</data>
<data name="buttonServerData.TabIndex" type="System.Int32, mscorlib">
<value>2</value>
@ -526,10 +535,13 @@
<value>NoControl</value>
</data>
<data name="buttonFullResync.Location" type="System.Drawing.Point, System.Drawing">
<value>86, 169</value>
<value>86, 187</value>
</data>
<data name="buttonFullResync.Padding" type="System.Windows.Forms.Padding, System.Windows.Forms">
<value>3, 3, 3, 3</value>
</data>
<data name="buttonFullResync.Size" type="System.Drawing.Size, System.Drawing">
<value>242, 23</value>
<value>242, 29</value>
</data>
<data name="buttonFullResync.TabIndex" type="System.Int32, mscorlib">
<value>3</value>
@ -553,13 +565,13 @@
<value>Fill</value>
</data>
<data name="_labelResyncOption.Location" type="System.Drawing.Point, System.Drawing">
<value>86, 195</value>
<value>86, 219</value>
</data>
<data name="_labelResyncOption.Padding" type="System.Windows.Forms.Padding, System.Windows.Forms">
<value>0, 6, 0, 0</value>
</data>
<data name="_labelResyncOption.Size" type="System.Drawing.Size, System.Drawing">
<value>242, 83</value>
<value>242, 59</value>
</data>
<data name="_labelResyncOption.TabIndex" type="System.Int32, mscorlib">
<value>4</value>

View File

@ -895,6 +895,24 @@ namespace Acacia.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to The server data is being synchronised..
/// </summary>
internal static string ServerSync_Label {
get {
return ResourceManager.GetString("ServerSync_Label", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Server data.
/// </summary>
internal static string ServerSync_Title {
get {
return ResourceManager.GetString("ServerSync_Title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Unable to open the shared folder. Please ensure you have permission to open the shared folder..
/// </summary>
@ -1129,6 +1147,42 @@ namespace Acacia.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to To fully resynchronise, Outlook must be restarted and all local data will be removed. Fetching all the data from the server again may take some time. Are you sure you want to resynchronise all data?.
/// </summary>
internal static string SyncState_FullResync_Body {
get {
return ResourceManager.GetString("SyncState_FullResync_Body", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Full Resynchronisation.
/// </summary>
internal static string SyncState_FullResync_Caption {
get {
return ResourceManager.GetString("SyncState_FullResync_Caption", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The Global Address Book has been scheduled for resynchronisation..
/// </summary>
internal static string SyncState_Resync_Body_GAB {
get {
return ResourceManager.GetString("SyncState_Resync_Body_GAB", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Resynchronisation.
/// </summary>
internal static string SyncState_Resync_Caption {
get {
return ResourceManager.GetString("SyncState_Resync_Caption", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Kopano.
/// </summary>

View File

@ -476,4 +476,22 @@
<data name="Ribbon_SyncState_Supertip" xml:space="preserve">
<value>Open the "Synchronisation" dialog, in which the synchronisation state can be viewed and managed.</value>
</data>
<data name="SyncState_FullResync_Body" xml:space="preserve">
<value>To fully resynchronise, Outlook must be restarted and all local data will be removed. Fetching all the data from the server again may take some time. Are you sure you want to resynchronise all data?</value>
</data>
<data name="SyncState_FullResync_Caption" xml:space="preserve">
<value>Full Resynchronisation</value>
</data>
<data name="SyncState_Resync_Body_GAB" xml:space="preserve">
<value>The Global Address Book has been scheduled for resynchronisation.</value>
</data>
<data name="SyncState_Resync_Caption" xml:space="preserve">
<value>Resynchronisation</value>
</data>
<data name="ServerSync_Label" xml:space="preserve">
<value>The server data is being synchronised.</value>
</data>
<data name="ServerSync_Title" xml:space="preserve">
<value>Server data</value>
</data>
</root>

View File

@ -49,6 +49,8 @@ namespace Acacia.Stubs
string DomainName { get; }
string BackingFilePath { get; }
// TODO: this is really a Z-Push thing, but it's here to store it in the registry
string LocalSignaturesHash
{

View File

@ -66,6 +66,13 @@ namespace Acacia.Stubs
/// Restarts the application
/// </summary>
void Restart(bool closeWindows);
/// <summary>
/// Restarts the application and removes the specified account files.
/// </summary>
/// <param name="accounts"></param>
void RestartResync(ZPushAccount[] accounts);
void Quit(bool closeWindows);
void InvokeUI(Action action);

View File

@ -94,6 +94,29 @@ namespace Acacia.Stubs.OutlookWrappers
}
}
public string BackingFilePath
{
get
{
byte[] bytes = (byte[])Registry.GetValue(_regPath, OutlookConstants.REG_VAL_EAS_STOREID, null);
// Find the last index of 00
int start = bytes.Length - 2;
while (start > 2)
{
if (bytes[start - 1] == 0 && bytes[start - 2] == 0)
break;
--start;
}
if (start <= 2)
return null;
return System.Text.Encoding.Unicode.GetString(bytes, start, bytes.Length - start - 2);
}
}
public string DisplayName
{
get

View File

@ -156,6 +156,11 @@ namespace Acacia.Stubs.OutlookWrappers
}
public void Restart(bool closeWindows)
{
DoRestart(closeWindows, null);
}
private void DoRestart(bool closeWindows, string additionalCommandLine)
{
// Can not use the assembly location, as that is in the GAC
string codeBase = Assembly.GetExecutingAssembly().CodeBase;
@ -174,6 +179,11 @@ namespace Acacia.Stubs.OutlookWrappers
commandLine += " /profile " + Util.QuoteCommandLine(ProfileName);
}
if (!string.IsNullOrEmpty(additionalCommandLine))
{
commandLine = commandLine + " " + additionalCommandLine;
}
// Run that
Process process = new Process();
process.StartInfo = new ProcessStartInfo(path, Process.GetCurrentProcess().Id + " " + commandLine);
@ -183,6 +193,20 @@ namespace Acacia.Stubs.OutlookWrappers
Quit(closeWindows);
}
public void RestartResync(ZPushAccount[] accounts)
{
string commandLine = "";
foreach(ZPushAccount account in accounts)
{
string path = account.Account.BackingFilePath;
if (!string.IsNullOrEmpty(path) && System.IO.Path.GetExtension(path) == ".ost")
{
commandLine += "/cleankoe " + Util.QuoteCommandLine(path);
}
}
DoRestart(true, commandLine);
}
public void Quit(bool closeWindows)
{
if (closeWindows)

View File

@ -134,9 +134,12 @@ namespace Acacia.Utils
Task(new AcaciaTask(completion, owner, name, action));
}
public static void Task(AcaciaTask task)
public static void Task(AcaciaTask task, bool synchronous = false)
{
Executor.AddTask(task);
if (synchronous)
task.Execute();
else
Executor.AddTask(task);
}
}
}

View File

@ -198,14 +198,20 @@ namespace Acacia.ZPush
// Execute tasks for all accounts
foreach (ZPushAccount account in _watcher.Accounts.GetAccounts())
ExecuteTasks(account);
ExecuteTasks(account, false);
}
public void ExecuteTasks(ZPushAccount[] accounts, bool synchronous = false)
{
foreach (ZPushAccount account in accounts)
ExecuteTasks(account, synchronous);
}
/// <summary>
/// Executes the tasks for the specified ZPush account. The tasks are pushed into the system
/// task queue.
/// </summary>
private void ExecuteTasks(ZPushAccount account)
private void ExecuteTasks(ZPushAccount account, bool synchronous = false)
{
// Don't contact the network if Outlook is offline
if (ThisAddIn.Instance.IsOffline)
@ -214,7 +220,7 @@ namespace Acacia.ZPush
// Execute the tasks for the account
foreach (SyncTask task in _tasks)
{
Tasks.Task(task.GetInstance(account));
Tasks.Task(task.GetInstance(account), synchronous);
}
}

View File

@ -41,7 +41,7 @@ namespace OutlookRestarter
static void Main(string[] args)
{
string procPath = args[1];
var procArgs = args.Skip(2);
List<string> procArgs = args.Skip(2).ToList();
try
{
// Attempt waiting for the process to finish
@ -51,9 +51,33 @@ namespace OutlookRestarter
}
finally
{
List<string> useArgs = new List<string>();
for (int i = 0; i < procArgs.Count; ++i)
{
if (procArgs[i] == "/cleankoe")
{
++i;
string path = procArgs[i];
if (System.IO.Path.GetExtension(path) == ".ost")
{
// Delete it
try
{
System.IO.File.Delete(path);
}
catch (Exception) { }
}
}
else
{
useArgs.Add(procArgs[i]);
}
}
File.WriteAllLines("c:\\temp\\ol.txt", useArgs);
string argsString = string.Join(" ", useArgs);
// Start the process
Process process = new Process();
process.StartInfo = new ProcessStartInfo(procPath, string.Join(" ", procArgs));
process.StartInfo = new ProcessStartInfo(procPath, argsString);
process.Start();
}
}