From 43e0d9b02d0c011cf8393bd88bae46f778d8587a Mon Sep 17 00:00:00 2001
From: Patrick Simpson
Date: Tue, 28 Feb 2017 12:14:13 +0100
Subject: [PATCH] [KOE-12] Basic implementation of reminder query modification.
For now only works when existing shares are modified, as the new shares do
not have a sync id yet. This will be added. Added SharedFoldersManager as a
wrapper around the API; it manages the API and syncing with the reminder
query. Also untested on 32-bit for now, which will most likely crash, as the
COM interface has some size-specific fields.
---
.../AcaciaZPushPlugin.csproj | 3 +-
.../SharedFolders/FeatureSharedFolders.cs | 13 +-
.../Features/SharedFolders/RemindersQuery.cs | 161 ++++++++++++++
.../SharedFolders/SharedCalendarReminders.cs | 80 -------
.../SharedFoldersDialog.Designer.cs | 22 +-
.../SharedFolders/SharedFoldersDialog.cs | 131 ++++++++----
.../SharedFolders/SharedFoldersDialog.resx | 196 +++++++++++-------
.../SharedFolders/SharedFoldersManager.cs | 122 +++++++++++
.../Features/SharedFolders/StoreTreeNode.cs | 17 +-
.../AcaciaZPushPlugin/OutlookConstants.cs | 7 +-
.../AcaciaZPushPlugin/SearchQuery.cs | 2 +-
.../API/SharedFolders/AvailableFolder.cs | 2 -
.../ZPush/API/SharedFolders/SharedFolder.cs | 14 +-
.../ZPush/API/SharedFolders/Types.cs | 7 +-
14 files changed, 557 insertions(+), 220 deletions(-)
create mode 100644 src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/RemindersQuery.cs
delete mode 100644 src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedCalendarReminders.cs
create mode 100644 src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersManager.cs
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj
index 4f0feb6..1f3a556 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj
@@ -274,7 +274,8 @@
-
+
+
UserControl
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/FeatureSharedFolders.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/FeatureSharedFolders.cs
index 38e1617..e16abf6 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/FeatureSharedFolders.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/FeatureSharedFolders.cs
@@ -58,7 +58,7 @@ namespace Acacia.Features.SharedFolders
ZPushAccount account = Watcher.Accounts.GetAccount(folder);
if (account != null)
{
- new SharedFoldersDialog(account, folder.SyncId).ShowDialog();
+ new SharedFoldersDialog(this, account, folder.SyncId).ShowDialog();
}
}
@@ -67,12 +67,21 @@ namespace Acacia.Features.SharedFolders
ZPushAccount account = Watcher.CurrentZPushAccount();
if (account != null)
{
- new SharedFoldersDialog(account).ShowDialog();
+ new SharedFoldersDialog(this, account).ShowDialog();
}
}
#endregion
+ #region Folder management
+
+ internal SharedFoldersManager Manage(ZPushAccount account)
+ {
+ return new SharedFoldersManager(this, account);
+ }
+
+ #endregion
+
#region Shared folders sync
private const string KEY_SHARES = "Shares";
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/RemindersQuery.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/RemindersQuery.cs
new file mode 100644
index 0000000..9b15b0f
--- /dev/null
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/RemindersQuery.cs
@@ -0,0 +1,161 @@
+using Acacia.Native.MAPI;
+using Acacia.Stubs;
+using Acacia.Utils;
+using Acacia.ZPush;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Acacia.Features.SharedFolders
+{
+ public class RemindersQuery : DisposableWrapper, LogContext
+ {
+ private static readonly SearchQuery.PropertyIdentifier PROP_FOLDER = new SearchQuery.PropertyIdentifier(PropTag.FromInt(0x6B20001F));
+
+ private readonly LogContext _context;
+ private readonly IFolder _folder;
+ private SearchQuery _queryRoot;
+ private SearchQuery.Or _queryCustom;
+
+ public RemindersQuery(LogContext context, IStore store)
+ {
+ this._context = context;
+ _folder = store.GetSpecialFolder(SpecialFolder.Reminders);
+ }
+
+ public bool Open()
+ {
+ if (_queryCustom != null)
+ return true;
+ try
+ {
+ _queryRoot = _folder.SearchCriteria;
+ if (!(_queryRoot is SearchQuery.And))
+ return false;
+ Logger.Instance.Trace(this, "Current query1: {0}", _queryRoot.ToString());
+
+ SearchQuery.And root = (SearchQuery.And)_queryRoot;
+ // TODO: more strict checking of query
+ if (root.Operands.Count == 3)
+ {
+ this._queryCustom = root.Operands.ElementAt(2) as SearchQuery.Or;
+ if (this._queryCustom != null)
+ {
+ // TODO: check property test
+ return true;
+ }
+ }
+
+ // We have the root, but not the custom query. Create it.
+ Logger.Instance.Debug(this, "Creating custom query");
+ Logger.Instance.Trace(this, "Current query: {0}", root.ToString());
+ _queryCustom = new SearchQuery.Or();
+
+ // Add the prefix exclusion for shared folders
+ _queryCustom.Add(
+ new SearchQuery.Not(
+ new SearchQuery.PropertyContent(
+ PROP_FOLDER, SearchQuery.ContentMatchOperation.Prefix, SearchQuery.ContentMatchModifiers.None, "S"
+ )
+ )
+ );
+
+ root.Operands.Add(_queryCustom);
+ Logger.Instance.Trace(this, "Modified query: {0}", root.ToString());
+ // Store it
+ // TODO: could store it on change only
+ _folder.SearchCriteria = root;
+ Logger.Instance.Trace(this, "Modified query2: {0}", _folder.SearchCriteria.ToString());
+ }
+ catch (Exception e)
+ {
+ Logger.Instance.Error(this, "Exception in Open: {0}", e);
+ }
+ return _queryCustom != null;
+ }
+
+ public string LogContextId
+ {
+ get
+ {
+ return _context.LogContextId;
+ }
+ }
+
+ protected override void DoRelease()
+ {
+ _folder.Dispose();
+ }
+
+ public void Commit()
+ {
+ _folder.SearchCriteria = _queryRoot;
+ }
+
+ public void UpdateReminders(SyncId folderId, bool wantReminders)
+ {
+ Logger.Instance.Trace(this, "Setting reminders for folder {0}: {1}", wantReminders, folderId);
+ string prefix = MakeFolderPrefix(folderId);
+
+ // Find existing
+ for (int i = 0; i < _queryCustom.Operands.Count;)
+ {
+ SearchQuery.PropertyContent element = _queryCustom.Operands[i] as SearchQuery.PropertyContent;
+ if (element != null && prefix == (string)element.Content)
+ {
+ Logger.Instance.Trace(this, "Found at {0}: {1}", i, folderId);
+ // Found it. If we want reminders, we're done
+ if (wantReminders)
+ return;
+
+ // Otherwise remove it. Still continue looking for others, just in case of duplicates
+ _queryCustom.Operands.RemoveAt(i);
+ }
+ else ++i;
+ }
+
+ // Not found, add if wanted
+ if (wantReminders)
+ {
+ Logger.Instance.Trace(this, "Adding reminders for {0}", folderId);
+ _queryCustom.Operands.Add(new SearchQuery.PropertyContent(
+ PROP_FOLDER, SearchQuery.ContentMatchOperation.Prefix, SearchQuery.ContentMatchModifiers.None, prefix
+ ));
+ }
+ }
+
+ public void RemoveStaleReminders(IEnumerable wanted)
+ {
+ // Collect the valid prefixes
+ HashSet prefixes = new HashSet();
+ foreach (SyncId id in wanted)
+ prefixes.Add(MakeFolderPrefix(id));
+
+ // Remove all operands for which we do not want the prefix
+ for (int i = 0; i < _queryCustom.Operands.Count;)
+ {
+ SearchQuery.PropertyContent element = _queryCustom.Operands[i] as SearchQuery.PropertyContent;
+ if (element != null)
+ {
+ string prefix = (string)element.Content;
+ if (prefixes.Contains(prefix))
+ {
+ ++i;
+ continue;
+ }
+
+ Logger.Instance.Trace(this, "Unwanted prefix at {0}: {1}", i, prefix);
+ _queryCustom.Operands.RemoveAt(i);
+ }
+ else ++i;
+ }
+ }
+
+ private string MakeFolderPrefix(SyncId folderId)
+ {
+ return folderId.ToString() + ":";
+ }
+ }
+}
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedCalendarReminders.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedCalendarReminders.cs
deleted file mode 100644
index 7db25c1..0000000
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedCalendarReminders.cs
+++ /dev/null
@@ -1,80 +0,0 @@
-using Acacia.Native.MAPI;
-using Acacia.Stubs;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Acacia.Features.SharedFolders
-{
- public class SharedCalendarReminders : LogContext
- {
- private static readonly SearchQuery.PropertyIdentifier PROP_FOLDER = new SearchQuery.PropertyIdentifier(PropTag.FromInt(0x6B20001F));
-
- private readonly LogContext _context;
- public string LogContextId
- {
- get
- {
- return _context.LogContextId;
- }
- }
-
- public SharedCalendarReminders(LogContext context)
- {
- this._context = context;
- }
-
- public void Initialise(IStore store)
- {
- using (IFolder reminders = store.GetSpecialFolder(SpecialFolder.Reminders))
- {
- SearchQuery.Or custom = FindCustomQuery(reminders, true);
- }
- }
-
- private SearchQuery.Or FindCustomQuery(IFolder reminders, bool addIfNeeded)
- {
- SearchQuery query = reminders.SearchCriteria;
- if (!(query is SearchQuery.And))
- return null;
- Logger.Instance.Trace(this, "Current query1: {0}", query.ToString());
-
- SearchQuery.And root = (SearchQuery.And)query;
- // TODO: more strict checking of query
- if (root.Operands.Count == 3)
- {
- SearchQuery.Or custom = root.Operands.ElementAt(2) as SearchQuery.Or;
- if (custom != null)
- {
- // TODO: check property test
- return custom;
- }
- }
-
- // We have the root, but not the custom query. Create it if needed.
- if (addIfNeeded)
- {
- Logger.Instance.Debug(this, "Creating custom query");
- Logger.Instance.Trace(this, "Current query: {0}", root.ToString());
- SearchQuery.Or custom = new SearchQuery.Or();
-
- // Add the prefix exclusion for shared folders
- custom.Add(
- new SearchQuery.Not(
- new SearchQuery.PropertyContent(
- PROP_FOLDER, SearchQuery.ContentMatchOperation.Prefix, SearchQuery.ContentMatchModifiers.None, "S"
- )
- )
- );
-
- root.Operands.Add(custom);
- Logger.Instance.Trace(this, "Modified query: {0}", root.ToString());
- reminders.SearchCriteria = root;
- Logger.Instance.Trace(this, "Modified query2: {0}", reminders.SearchCriteria.ToString());
- }
- return null;
- }
- }
-}
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.Designer.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.Designer.cs
index 7645b31..f93a21e 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.Designer.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.Designer.cs
@@ -47,6 +47,8 @@
this._labelPermissions = new System.Windows.Forms.Label();
this.labelPermissionsValue = new System.Windows.Forms.Label();
this.dialogButtons = new Acacia.Controls.KDialogButtons();
+ this._labelReminders = new System.Windows.Forms.Label();
+ this.checkReminders = new System.Windows.Forms.CheckBox();
this._layout.SuspendLayout();
this._mainBusyHider.SuspendLayout();
this._layoutMain.SuspendLayout();
@@ -137,8 +139,10 @@
this._layoutOptions.Controls.Add(this.textName, 1, 0);
this._layoutOptions.Controls.Add(this._labelSendAs, 0, 1);
this._layoutOptions.Controls.Add(this.checkSendAs, 1, 1);
- this._layoutOptions.Controls.Add(this._labelPermissions, 0, 2);
- this._layoutOptions.Controls.Add(this.labelPermissionsValue, 1, 2);
+ this._layoutOptions.Controls.Add(this._labelPermissions, 0, 3);
+ this._layoutOptions.Controls.Add(this.labelPermissionsValue, 1, 3);
+ this._layoutOptions.Controls.Add(this._labelReminders, 0, 2);
+ this._layoutOptions.Controls.Add(this.checkReminders, 1, 2);
this._layoutOptions.Name = "_layoutOptions";
//
// _labelName
@@ -185,6 +189,18 @@
this.dialogButtons.Name = "dialogButtons";
this.dialogButtons.Apply += new System.EventHandler(this.dialogButtons_Apply);
//
+ // _labelReminders
+ //
+ resources.ApplyResources(this._labelReminders, "_labelReminders");
+ this._labelReminders.Name = "_labelReminders";
+ //
+ // checkReminders
+ //
+ resources.ApplyResources(this.checkReminders, "checkReminders");
+ this.checkReminders.Name = "checkReminders";
+ this.checkReminders.UseVisualStyleBackColor = true;
+ this.checkReminders.CheckedChanged += new System.EventHandler(this.checkReminders_CheckedChanged);
+ //
// SharedFoldersDialog
//
resources.ApplyResources(this, "$this");
@@ -228,5 +244,7 @@
private Controls.KDialogButtons dialogButtons;
private System.Windows.Forms.TableLayoutPanel _layoutCenterGABLookup;
private UI.GABLookupControl gabLookup;
+ private System.Windows.Forms.Label _labelReminders;
+ private System.Windows.Forms.CheckBox checkReminders;
}
}
\ No newline at end of file
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.cs
index e0fc7d7..091fdff 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.cs
@@ -16,10 +16,8 @@
using Acacia.Controls;
using Acacia.Features.GAB;
-using Acacia.Stubs;
using Acacia.UI;
using Acacia.UI.Outlook;
-using Acacia.Utils;
using Acacia.ZPush;
using Acacia.ZPush.API.SharedFolders;
using System;
@@ -34,21 +32,20 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
-using System.Xml;
-using System.Xml.Serialization;
-using static Acacia.ZPush.API.SharedFolders.SharedFoldersAPI;
namespace Acacia.Features.SharedFolders
{
public partial class SharedFoldersDialog : KDialogNew
{
private readonly ZPushAccount _account;
- private SyncId _initialSyncId;
+ private readonly SharedFoldersManager _folders;
+ private readonly SyncId _initialSyncId;
private SharedFolder _initialFolder;
- public SharedFoldersDialog(ZPushAccount account, SyncId initial = null)
+ public SharedFoldersDialog(FeatureSharedFolders feature, ZPushAccount account, SyncId initial = null)
{
this._account = account;
+ this._folders = feature.Manage(account);
this._initialSyncId = initial;
InitializeComponent();
@@ -77,7 +74,7 @@ namespace Acacia.Features.SharedFolders
ShowOptions(new KTreeNode[0]);
// Set up user selector
- gabLookup.GAB = FeatureGAB.FindGABForAccount(_account);
+ gabLookup.GAB = FeatureGAB.FindGABForAccount(account);
}
#region Load and store
@@ -88,21 +85,18 @@ namespace Acacia.Features.SharedFolders
KUITask
.New((ctx) =>
{
- using (SharedFoldersAPI api = new SharedFoldersAPI(_account))
- {
- // TODO: bind cancellation token to Cancel button
- // Fetch current shares
- ICollection folders = api.GetCurrentShares(ctx.CancellationToken);
+ // TODO: bind cancellation token to Cancel button
+ // Fetch current shares
+ ICollection folders = _folders.GetCurrentShares(ctx.CancellationToken);
- // Find the initial folder if required
- if (_initialSyncId != null)
- _initialFolder = folders.FirstOrDefault(f => f.SyncId == _initialSyncId);
+ // Find the initial folder if required
+ if (_initialSyncId != null)
+ _initialFolder = folders.FirstOrDefault(f => f.SyncId == _initialSyncId);
- // Group by store and folder id
- return folders.GroupBy(f => f.Store)
- .ToDictionary(group => group.Key,
- group => group.ToDictionary(folder => folder.BackendId));
- }
+ // Group by store and folder id
+ return folders.GroupBy(f => f.Store)
+ .ToDictionary(group => group.Key,
+ group => group.ToDictionary(folder => folder.BackendId));
})
.OnSuccess(InitialiseTree, true)
.OnError((e) =>
@@ -163,27 +157,25 @@ namespace Acacia.Features.SharedFolders
BusyText = Properties.Resources.SharedFolders_Applying_Label;
KUITask.New((ctx) =>
{
- using (SharedFoldersAPI folders = new SharedFoldersAPI(_account))
+ // We reuse the same busy indicationg for all calls. A count is kept to ensure it's removed.
+ int count = 0;
+
+ foreach (StoreTreeNode storeNode in _userFolders.Values)
{
- // We reuse the same busy indicationg for all calls. A count is kept to ensure it's removed.
- int count = 0;
-
- foreach (StoreTreeNode storeNode in _userFolders.Values)
+ if (storeNode.IsDirty)
{
- if (storeNode.IsDirty)
- {
- ctx.AddBusy(1);
- ++count;
+ ctx.AddBusy(1);
+ ++count;
- folders.SetCurrentShares(storeNode.User, storeNode.CurrentShares, ctx.CancellationToken);
- }
+ _folders.SetSharesForStore(storeNode.User, storeNode.CurrentShares, ctx.CancellationToken);
}
-
- return count;
}
+
+ return count;
})
.OnSuccess((ctx, count) =>
{
+ // Update UI state
foreach (StoreTreeNode storeNode in _userFolders.Values)
if (storeNode.IsDirty)
storeNode.ChangesApplied();
@@ -255,7 +247,7 @@ namespace Acacia.Features.SharedFolders
}
// Add the node
- node = new StoreTreeNode(_account, user, user.DisplayName, currentShares ?? new Dictionary());
+ node = new StoreTreeNode(_folders, user, user.DisplayName, currentShares ?? new Dictionary());
node.DirtyChanged += UserSharesChanged;
_userFolders.Add(user, node);
kTreeFolders.RootNodes.Add(node);
@@ -333,6 +325,25 @@ namespace Acacia.Features.SharedFolders
private readonly List _optionSendAsNodes = new List();
private readonly List _optionSendAsInitial = new List();
+ private CheckState? OptionReminders
+ {
+ get
+ {
+ if (checkReminders.Visible)
+ return checkReminders.CheckState;
+ return null;
+ }
+
+ set
+ {
+ _labelReminders.Visible = checkReminders.Visible = value != null;
+ if (value != null)
+ checkReminders.CheckState = value.Value;
+ }
+ }
+ private readonly List _optionRemindersNodes = new List();
+ private readonly List _optionRemindersInitial = new List();
+
private Permission? _optionPermissions;
private Permission? OptionPermissions
{
@@ -376,10 +387,13 @@ namespace Acacia.Features.SharedFolders
_optionNameNode = null;
_optionSendAsNodes.Clear();
_optionSendAsInitial.Clear();
+ _optionRemindersNodes.Clear();
+ _optionRemindersInitial.Clear();
_optionPermissionNodes.Clear();
OptionName = null;
OptionTrackName = null;
OptionSendAs = null;
+ OptionReminders = null;
OptionPermissions = null;
foreach (KTreeNode node in nodes)
@@ -399,12 +413,18 @@ namespace Acacia.Features.SharedFolders
// Assume we will edit the name for this node; cleared below if there are multiple
_optionNameNode = folderNode;
- // Show send as if there are any mail folders
- if (folder.IsMailFolder)
+ if (folder.Type.IsMail())
{
+ // Show send as if there are any mail folders
_optionSendAsNodes.Add(folderNode);
_optionSendAsInitial.Add(folderNode.SharedFolder.FlagSendAsOwner);
}
+ else if (folder.Type.IsAppointment())
+ {
+ // Show reminders for appointment folders
+ _optionRemindersNodes.Add(folderNode);
+ _optionRemindersInitial.Add(folderNode.SharedFolder.FlagCalendarReminders);
+ }
// Show permissions for all shared nodes
_optionPermissionNodes.Add(folderNode);
@@ -448,6 +468,21 @@ namespace Acacia.Features.SharedFolders
checkSendAs.ThreeState = true;
}
}
+ // Reminders shown if any node supports it
+ if (_optionRemindersNodes.Count > 0)
+ {
+ bool reminders = _optionRemindersNodes.First().SharedFolder.FlagCalendarReminders;
+ if (_optionRemindersNodes.All(x => x.SharedFolder.FlagCalendarReminders == reminders))
+ {
+ OptionReminders = reminders ? CheckState.Checked : CheckState.Unchecked;
+ checkReminders.ThreeState = false;
+ }
+ else
+ {
+ OptionReminders = CheckState.Indeterminate;
+ checkReminders.ThreeState = true;
+ }
+ }
}
finally
{
@@ -477,7 +512,7 @@ namespace Acacia.Features.SharedFolders
// If the share name matches the folder name, track update
bool track = _optionNameNode.SharedFolder.Name == _optionNameNode.AvailableFolder.DefaultName;
- _optionNameNode.SharedFolder = _optionNameNode.SharedFolder.WithFlagUpdateShareName(track);
+ _optionNameNode.SharedFolder = _optionNameNode.SharedFolder.WithFlagTrackShareName(track);
}
}
@@ -507,6 +542,26 @@ namespace Acacia.Features.SharedFolders
}
}
+ private void checkReminders_CheckedChanged(object sender, EventArgs e)
+ {
+ for (int i = 0; i < _optionRemindersNodes.Count; ++i)
+ {
+ FolderTreeNode node = _optionRemindersNodes[i];
+ bool reminders = false;
+ switch (checkReminders.CheckState)
+ {
+ case CheckState.Checked: reminders = true; break;
+ case CheckState.Indeterminate: reminders = _optionRemindersInitial[i]; break;
+ case CheckState.Unchecked: reminders = false; break;
+ }
+
+ if (node.SharedFolder.FlagCalendarReminders != reminders)
+ {
+ node.SharedFolder = node.SharedFolder.WithFlagCalendarReminders(reminders);
+ }
+ }
+ }
+
#endregion
}
}
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.resx b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.resx
index ffdef70..256dd52 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.resx
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.resx
@@ -145,13 +145,10 @@
- 4, 0
-
-
- 4, 0, 4, 0
+ 3, 0
- 143, 36
+ 105, 31
0
@@ -190,16 +187,13 @@
NoControl
- 510, 4
-
-
- 4, 4, 4, 4
+ 380, 3
- 11, 0, 11, 0
+ 8, 0, 8, 0
- 75, 28
+ 59, 25
1
@@ -232,16 +226,13 @@
Popup
- 4, 4
-
-
- 4, 4, 4, 4
+ 3, 3
- 265, 0
+ 200, 0
- 343, 24
+ 256, 21
1
@@ -262,7 +253,7 @@
Fill
- 153, 2
+ 113, 2
2, 2, 2, 2
@@ -271,7 +262,7 @@
3
- 351, 32
+ 262, 27
2
@@ -295,16 +286,13 @@
Fill
- 4, 4
-
-
- 4, 4, 4, 4
+ 3, 3
1
- 589, 36
+ 442, 31
0
@@ -322,19 +310,16 @@
0
- <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="labelSelectUser" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="buttonOpenUser" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /><Control Name="_layoutCenterGABLookup" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="AutoSize,0,Percent,100,AutoSize,0" /><Rows Styles="Percent,100,Absolute,37" /></TableLayoutSettings>
+ <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="labelSelectUser" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="buttonOpenUser" Row="0" RowSpan="1" Column="2" ColumnSpan="1" /><Control Name="_layoutCenterGABLookup" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="AutoSize,0,Percent,100,AutoSize,0" /><Rows Styles="Percent,100,Absolute,27" /></TableLayoutSettings>
Fill
- 4, 48
-
-
- 4, 4, 4, 4
+ 3, 40
- 589, 377
+ 442, 275
1
@@ -367,13 +352,10 @@
Fill
- 4, 0
-
-
- 4, 0, 4, 0
+ 3, 0
- 102, 30
+ 82, 26
0
@@ -400,13 +382,13 @@
Fill
- 118, 4
+ 94, 3
- 8, 4, 4, 4
+ 6, 3, 3, 3
- 475, 22
+ 351, 20
1
@@ -430,13 +412,10 @@
Fill
- 4, 30
-
-
- 4, 0, 4, 0
+ 3, 26
- 102, 34
+ 82, 27
2
@@ -466,16 +445,16 @@
Left
- 118, 35
+ 94, 30
- 8, 5, 4, 4
+ 6, 4, 3, 3
- 0, 4, 0, 4
+ 0, 3, 0, 3
- 18, 25
+ 15, 20
3
@@ -499,16 +478,13 @@
Fill
- 4, 64
-
-
- 4, 0, 4, 0
+ 3, 80
- 0, 5, 0, 4
+ 0, 4, 0, 3
- 102, 26
+ 82, 20
4
@@ -541,16 +517,13 @@
MiddleLeft
- 114, 64
-
-
- 4, 0, 4, 0
+ 91, 80
- 0, 5, 0, 4
+ 0, 4, 0, 3
- 479, 26
+ 354, 20
5
@@ -573,20 +546,86 @@
5
+
+ True
+
+
+ Fill
+
+
+ 3, 53
+
+
+ 82, 27
+
+
+ 6
+
+
+ Show reminders
+
+
+ MiddleLeft
+
+
+ _labelReminders
+
+
+ System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ _layoutOptions
+
+
+ 6
+
+
+ True
+
+
+ Left
+
+
+ 94, 57
+
+
+ 6, 4, 3, 3
+
+
+ 0, 3, 0, 3
+
+
+ 15, 20
+
+
+ 7
+
+
+ checkReminders
+
+
+ System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ _layoutOptions
+
+
+ 7
+
Fill
- 0, 429
+ 0, 318
0, 0, 0, 0
- 3
+ 4
- 597, 90
+ 448, 100
2
@@ -604,7 +643,7 @@
2
- <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="_labelName" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="textName" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="_labelSendAs" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="checkSendAs" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="_labelPermissions" Row="2" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="labelPermissionsValue" Row="2" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="AutoSize,0,Percent,100" /><Rows Styles="AutoSize,0,AutoSize,0,AutoSize,0" /></TableLayoutSettings>
+ <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="_labelName" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="textName" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="_labelSendAs" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="checkSendAs" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="_labelPermissions" Row="3" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="labelPermissionsValue" Row="3" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="_labelReminders" Row="2" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="checkReminders" Row="2" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="AutoSize,0,Percent,100" /><Rows Styles="AutoSize,0,AutoSize,0,AutoSize,0,AutoSize,0" /></TableLayoutSettings>
Fill
@@ -612,14 +651,11 @@
0, 0
-
- 4, 4, 4, 4
-
3
- 597, 519
+ 448, 418
3
@@ -643,13 +679,10 @@
Fill
- 4, 4
-
-
- 4, 4, 4, 4
+ 3, 3
- 597, 519
+ 448, 418
4
@@ -679,13 +712,13 @@
Fill
- 2, 528
+ 2, 425
2, 1, 2, 1
- 601, 39
+ 450, 35
5
@@ -706,7 +739,7 @@
Fill
- 8, 7
+ 6, 6
0, 0, 0, 0
@@ -715,7 +748,7 @@
2
- 605, 568
+ 454, 461
0
@@ -733,19 +766,22 @@
0
- <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="_mainBusyHider" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="dialogButtons" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /></Controls><Columns Styles="Percent,100" /><Rows Styles="Percent,100,AutoSize,0,Absolute,25" /></TableLayoutSettings>
+ <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="_mainBusyHider" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="dialogButtons" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /></Controls><Columns Styles="Percent,100" /><Rows Styles="Percent,100,AutoSize,0,Absolute,20" /></TableLayoutSettings>
True
- 8, 16
+ 6, 13
- 621, 582
+ 466, 473
+
+
+ 2, 2, 2, 2
- 8, 7, 8, 7
+ 6, 6, 6, 6
CenterParent
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersManager.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersManager.cs
new file mode 100644
index 0000000..58cfa60
--- /dev/null
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersManager.cs
@@ -0,0 +1,122 @@
+using Acacia.Utils;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Acacia.ZPush;
+using Acacia.ZPush.API.SharedFolders;
+using System.Threading;
+using Acacia.Native.MAPI;
+
+namespace Acacia.Features.SharedFolders
+{
+ ///
+ /// Manages changes to shared folders.
+ ///
+ public class SharedFoldersManager : DisposableWrapper
+ {
+ ///
+ /// Contains 'folderid:itemid'. The folder id is used to detect shared folders.
+ /// TODO: put this in a shared lib somewhere
+ ///
+ private static readonly SearchQuery.PropertyIdentifier PROP_AS_ITEMID = new SearchQuery.PropertyIdentifier(PropTag.FromInt(0x6B20001F));
+
+ private readonly ZPushAccount _account;
+ private readonly FeatureSharedFolders _feature;
+ private readonly SharedFoldersAPI _api;
+ private RemindersQuery _query;
+
+ public SharedFoldersManager(FeatureSharedFolders featureSharedFolders, ZPushAccount account)
+ {
+ this._feature = featureSharedFolders;
+ this._account = account;
+ _api = new SharedFoldersAPI(account);
+ }
+
+ protected override void DoRelease()
+ {
+ _api.Dispose();
+ if (_query != null)
+ _query.Dispose();
+ }
+
+ #region API
+
+ ///
+ /// Sets all shares for the specified store.
+ ///
+ public void SetSharesForStore(GABUser store, ICollection shares, CancellationToken? cancel)
+ {
+ // Make sure reminders are updated as soon as possible
+ UpdateReminders(shares);
+ _api.SetCurrentShares(store, shares, cancel);
+
+ // Commit changes
+ if (_query != null)
+ _query.Commit();
+ }
+
+ public ICollection GetCurrentShares(CancellationToken? cancel)
+ {
+ // Fetch the shares
+ ICollection shares = _api.GetCurrentShares(cancel);
+
+ // Make sure reminders are disabled as soon as possible
+ UpdateReminders(shares);
+
+ // Remove any reminders from the shares that are not wanted, they are stale
+ OpenQuery()?.RemoveStaleReminders(
+ shares
+ .Where(x => x.IsSynced && x.SyncType.IsAppointment() && x.FlagCalendarReminders)
+ .Select(x => x.SyncId)
+ );
+
+ // Commit changes
+ if (_query != null)
+ _query.Commit();
+
+ return shares;
+ }
+
+ public ICollection GetStoreFolders(GABUser store)
+ {
+ return _api.GetUserFolders(store);
+ }
+
+ #endregion
+
+ #region Reminders
+
+ private void UpdateReminders(ICollection shares)
+ {
+ foreach(SharedFolder share in shares)
+ {
+ Logger.Instance.Debug(this, "UpdateReminders: {0}", share);
+ if (share.IsSynced && share.SyncType.IsAppointment())
+ {
+ OpenQuery()?.UpdateReminders(share.SyncId, share.FlagCalendarReminders);
+ }
+ }
+ }
+
+ private RemindersQuery OpenQuery()
+ {
+ if (_query == null)
+ {
+ RemindersQuery query = new RemindersQuery(_feature, _account.Account.Store);
+ if (query.Open())
+ {
+ _query = query;
+ }
+ else
+ {
+ query.Dispose();
+ }
+ }
+ return _query;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/StoreTreeNode.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/StoreTreeNode.cs
index 088e5e0..9a1610d 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/StoreTreeNode.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/StoreTreeNode.cs
@@ -41,7 +41,7 @@ namespace Acacia.Features.SharedFolders
private readonly Dictionary _initialShares;
private readonly Dictionary _currentShares;
- public StoreTreeNode(ZPushAccount account, GABUser user, string text, Dictionary currentFolders)
+ public StoreTreeNode(SharedFoldersManager folders, GABUser user, string text, Dictionary currentFolders)
:
base(text)
{
@@ -51,7 +51,7 @@ namespace Acacia.Features.SharedFolders
// cleaning up automatically any obsolote shares.
this._currentShares = new Dictionary();
- ChildLoader = new UserFolderLoader(this, account, user);
+ ChildLoader = new UserFolderLoader(this, folders, user);
ChildLoader.ReloadOnCloseOpen = true;
HasCheckBox = false;
@@ -95,7 +95,7 @@ namespace Acacia.Features.SharedFolders
SharedFolder share = new SharedFolder(folder);
// Default send as for mail folders
- if (folder.IsMailFolder)
+ if (folder.Type.IsMail())
share = share.WithFlagSendAsOwner(true);
return share;
@@ -167,21 +167,18 @@ namespace Acacia.Features.SharedFolders
public class UserFolderLoader : KTreeNodeLoader
{
- private readonly ZPushAccount _account;
+ private readonly SharedFoldersManager _folders;
public GABUser User { get; private set; }
- public UserFolderLoader(StoreTreeNode parent, ZPushAccount account, GABUser user) : base(parent)
+ public UserFolderLoader(StoreTreeNode parent, SharedFoldersManager folders, GABUser user) : base(parent)
{
- this._account = account;
+ this._folders = folders;
this.User = user;
}
protected override object DoLoadChildren(KTreeNode node)
{
- using (SharedFoldersAPI folders = new SharedFoldersAPI(_account))
- {
- return folders.GetUserFolders(User);
- }
+ return _folders.GetStoreFolders(User);
}
private class FolderComparer : IComparer
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/OutlookConstants.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/OutlookConstants.cs
index 59ba465..c7f9000 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/OutlookConstants.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/OutlookConstants.cs
@@ -199,7 +199,7 @@ namespace Acacia
SyncType.RecipientCache, // RecipientCache = 19
};
- public static bool IsMailType(SyncType type)
+ public static bool IsMail(this SyncType type)
{
return USER_SYNC_TYPES[(int)type] == SyncType.UserMail;
}
@@ -245,6 +245,11 @@ namespace Acacia
#endregion
+ public static bool IsAppointment(this SyncType type)
+ {
+ return USER_SYNC_TYPES[(int)type] == SyncType.UserAppointment;
+ }
+
#region Message classes
public const string PR_MESSAGE_CLASS = PROP + "001A" + PT_UNICODE;
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/SearchQuery.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/SearchQuery.cs
index 45e9f5d..c983843 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/SearchQuery.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/SearchQuery.cs
@@ -170,7 +170,7 @@ namespace Acacia
_operands.Add(operand);
}
- public ICollection Operands
+ public IList Operands
{
get { return _operands; }
}
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/API/SharedFolders/AvailableFolder.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/API/SharedFolders/AvailableFolder.cs
index c3411a6..82e18ee 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/API/SharedFolders/AvailableFolder.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/API/SharedFolders/AvailableFolder.cs
@@ -94,8 +94,6 @@ namespace Acacia.ZPush.API.SharedFolders
public GABUser Store { get; private set; }
- public bool IsMailFolder { get { return OutlookConstants.IsMailType(Type); } }
-
#endregion
#region Tree structure
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/API/SharedFolders/SharedFolder.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/API/SharedFolders/SharedFolder.cs
index 9679e64..248424a 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/API/SharedFolders/SharedFolder.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/API/SharedFolders/SharedFolder.cs
@@ -117,7 +117,7 @@ namespace Acacia.ZPush.API.SharedFolders
parentid = folder.ParentIdAsBackend,
name = folder.DefaultName,
type = OutlookConstants.USER_SYNC_TYPES[(int)folder.Type],
- flags = folder.IsMailFolder ? ShareFlags.SendAsOwner : ShareFlags.None
+ flags = folder.Type.IsMail() ? ShareFlags.SendAsOwner : ShareFlags.None
};
}
@@ -129,6 +129,7 @@ namespace Acacia.ZPush.API.SharedFolders
public BackendId BackendId { get { return _data.folderid; } }
public SyncId SyncId { get { return _data.syncfolderid; } }
public bool IsSynced { get { return SyncId != null; } }
+ public OutlookConstants.SyncType SyncType { get { return _data.type; } }
public Permission? Permissions
{
@@ -180,6 +181,7 @@ namespace Acacia.ZPush.API.SharedFolders
public bool FlagSendAsOwner { get { return Flags.HasFlag(ShareFlags.SendAsOwner); } }
public bool FlagUpdateShareName { get { return Flags.HasFlag(ShareFlags.TrackShareName); } }
+ public bool FlagCalendarReminders { get { return Flags.HasFlag(ShareFlags.CalendarReminders); } }
///
/// Returns a copy with the specified 'send as owner' flag.
@@ -192,11 +194,19 @@ namespace Acacia.ZPush.API.SharedFolders
///
/// Returns a copy with the specified 'update share name' flag.
///
- public SharedFolder WithFlagUpdateShareName(bool value)
+ public SharedFolder WithFlagTrackShareName(bool value)
{
return WithFlags(value ? (_data.flags | ShareFlags.TrackShareName) : (_data.flags & ~ShareFlags.TrackShareName));
}
+ ///
+ /// Returns a copy with the specified 'calendar reminders' flag.
+ ///
+ public SharedFolder WithFlagCalendarReminders(bool value)
+ {
+ return WithFlags(value ? (_data.flags | ShareFlags.CalendarReminders) : (_data.flags & ~ShareFlags.CalendarReminders));
+ }
+
#endregion
#region Standard overrides
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/API/SharedFolders/Types.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/API/SharedFolders/Types.cs
index b1b7ee0..bac3ba4 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/API/SharedFolders/Types.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/API/SharedFolders/Types.cs
@@ -40,11 +40,16 @@ namespace Acacia.ZPush.API.SharedFolders
///
TrackShareName = 2,
+ ///
+ /// Applicable to calendars only. Set to enable reminders on the shared calendar.
+ ///
+ CalendarReminders = 4,
+
///
/// The mask indicating which flag changes cause an Apply to become needed. I.e. flags not in the mask
/// are updated only if other changes are made.
///
- Mask_Apply = 1
+ Mask_Apply = 0xFFFF & ~(TrackShareName)
}
///