From d21eb821a4fcedf389ca91595d44c754670b4c11 Mon Sep 17 00:00:00 2001
From: Patrick Simpson
Date: Wed, 7 Jun 2017 13:18:40 +0200
Subject: [PATCH] [KOE-114] Added change suppression for private events in
shared folders.
---
.../AcaciaZPushPlugin.csproj | 4 +
.../Features/GAB/FeatureGAB.cs | 6 +-
.../SharedFolders/FeatureSharedFolders.cs | 106 ++++++++++++++++++
.../Properties/Resources.Designer.cs | 18 +++
.../Properties/Resources.resx | 6 +
.../AcaciaZPushPlugin/Stubs/Enums.cs | 9 ++
.../AcaciaZPushPlugin/Stubs/IAddIn.cs | 1 +
.../Stubs/IAppointmentItem.cs | 2 +
.../AcaciaZPushPlugin/Stubs/IExplorer.cs | 11 +-
.../AcaciaZPushPlugin/Stubs/IInspector.cs | 47 ++++++++
.../AcaciaZPushPlugin/Stubs/IInspectors.cs | 12 ++
.../AcaciaZPushPlugin/Stubs/IOutlookWindow.cs | 6 +
.../Stubs/OutlookWrappers/AddInWrapper.cs | 5 +
.../OutlookWrappers/AppointmentItemWrapper.cs | 5 +
.../Stubs/OutlookWrappers/ExplorerWrapper.cs | 5 +
.../Stubs/OutlookWrappers/InspectorWrapper.cs | 48 ++++++++
.../OutlookWrappers/InspectorsWrapper.cs | 30 +++++
17 files changed, 312 insertions(+), 9 deletions(-)
create mode 100644 src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IInspector.cs
create mode 100644 src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IInspectors.cs
create mode 100644 src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/InspectorWrapper.cs
create mode 100644 src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/InspectorsWrapper.cs
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj
index ea3dcfe..51e22ce 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj
@@ -325,6 +325,8 @@
+
+
@@ -344,6 +346,7 @@
+
@@ -354,6 +357,7 @@
+
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/GAB/FeatureGAB.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/GAB/FeatureGAB.cs
index 6c1471b..800d996 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/GAB/FeatureGAB.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/GAB/FeatureGAB.cs
@@ -330,9 +330,9 @@ namespace Acacia.Features.GAB
// Show message and cancel event
// TODO: show on active inspector if used
// [KOE-108] If the window is not specified as parent, it sometimes doesn't show
- var res = MessageBox.Show(ThisAddIn.Instance.Window,
- StringUtil.GetResourceString("GABEvent_Body"),
- StringUtil.GetResourceString("GABEvent_Title"),
+ MessageBox.Show(ThisAddIn.Instance.Window,
+ Properties.Resources.GABEvent_Body,
+ Properties.Resources.GABEvent_Title,
MessageBoxButtons.OK,
MessageBoxIcon.Warning
);
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/FeatureSharedFolders.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/FeatureSharedFolders.cs
index a0d6c11..fe20d52 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/FeatureSharedFolders.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/FeatureSharedFolders.cs
@@ -26,6 +26,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using System.Windows.Forms;
using static Acacia.DebugOptions;
namespace Acacia.Features.SharedFolders
@@ -67,6 +68,9 @@ namespace Acacia.Features.SharedFolders
// Sync state
Watcher.Sync.AddTask(this, Name, AdditionalFolders_Sync);
+
+ // Private shared appointment
+ SetupPrivateAppointmentSuppression();
}
#region UI
@@ -158,7 +162,109 @@ namespace Acacia.Features.SharedFolders
return share;
}
+ public static bool IsSharedFolder(IFolder folder)
+ {
+ string id = (string)folder.GetProperty(OutlookConstants.PR_ZPUSH_FOLDER_ID);
+ return id?.StartsWith("S") == true;
+ }
+
#endregion
+ #region Private appointments
+
+ [AcaciaOption("If enabled, modifications to private appointments in shared calendars are suppressed. " +
+ "This should only be disabled for debug purposes.")]
+ public bool SuppressModificationsPrivateAppointments
+ {
+ get { return GetOption(OPTION_SUPPRESS_MODIFICATIONS_PRIVATE_APPOINTMENTS); }
+ set { SetOption(OPTION_SUPPRESS_MODIFICATIONS_PRIVATE_APPOINTMENTS, value); }
+ }
+ private static readonly BoolOption OPTION_SUPPRESS_MODIFICATIONS_PRIVATE_APPOINTMENTS =
+ new BoolOption("SuppressModificationsPrivateAppointments", true);
+
+ // TODO: this is largely duplicated from FeatureGAB, separate into helper
+ private void SetupPrivateAppointmentSuppression()
+ {
+ if (SuppressModificationsPrivateAppointments && MailEvents != null)
+ {
+ MailEvents.BeforeDelete += SuppressEventHandler_Delete;
+ MailEvents.PropertyChange += SuppressEventHandler_PropertyChange;
+ MailEvents.Write += SuppressEventHandler_Write;
+ }
+ }
+
+
+ private void SuppressEventHandler_Delete(IItem item, ref bool cancel)
+ {
+ SuppressEventHandler(item, false, ref cancel);
+ }
+
+ ///
+ /// When copying a contact from the GAB to a local folder, Outlook raises the Write event on
+ /// the original. To detect this, we set this id on every property change (which is signalled before
+ /// write), and only suppress if anything has actually changed. If we suppress, the flag is cleared again.
+ ///
+ private string _propertyChangeId;
+
+ private void SuppressEventHandler_PropertyChange(IItem item, string propertyName)
+ {
+ if (_propertyChangeId == item.EntryID)
+ return;
+ _propertyChangeId = item.EntryID;
+ }
+
+ private void SuppressEventHandler_Write(IItem item, ref bool cancel)
+ {
+ if (_propertyChangeId == item.EntryID)
+ {
+ SuppressEventHandler(item, true, ref cancel);
+ _propertyChangeId = null;
+ }
+ }
+
+ private void SuppressEventHandler(IItem item, bool findInspector, ref bool cancel)
+ {
+ // Check if it's an appointment
+ IAppointmentItem appointment = item as IAppointmentItem;
+ if (appointment == null)
+ return;
+
+ // Check if it's private. Confidential is also considered private
+ if (appointment.Sensitivity < Sensitivity.Private)
+ return;
+
+ // Check if in a shared folder
+ using (IFolder parent = item.Parent)
+ {
+ if (parent == null || !IsSharedFolder(parent))
+ return;
+ }
+
+ if (findInspector)
+ {
+ // Find and close any inspector
+ foreach (IInspector inspector in ThisAddIn.Instance.GetInspectors())
+ {
+ using (IItem inspectItem = inspector.GetCurrentItem())
+ {
+ if (appointment.EntryID == inspectItem.EntryID)
+ {
+ inspector.Close(InspectorClose.Discard);
+ }
+ }
+ }
+ }
+
+ // Private appointment in a shared folder, suppress
+ cancel = true;
+ MessageBox.Show(ThisAddIn.Instance.Window,
+ Properties.Resources.SharedFolders_PrivateEvent_Body,
+ Properties.Resources.SharedFolders_PrivateEvent_Title,
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Warning
+ );
+ }
+
+ #endregion
}
}
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Properties/Resources.Designer.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Properties/Resources.Designer.cs
index dd440d8..2187695 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Properties/Resources.Designer.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Properties/Resources.Designer.cs
@@ -1111,6 +1111,24 @@ namespace Acacia.Properties {
}
}
+ ///
+ /// Looks up a localized string similar to Changing other people's private events is not allowed..
+ ///
+ internal static string SharedFolders_PrivateEvent_Body {
+ get {
+ return ResourceManager.GetString("SharedFolders_PrivateEvent_Body", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Private event.
+ ///
+ internal static string SharedFolders_PrivateEvent_Title {
+ get {
+ return ResourceManager.GetString("SharedFolders_PrivateEvent_Title", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Public folders.
///
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Properties/Resources.resx b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Properties/Resources.resx
index b519732..6e77c5c 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Properties/Resources.resx
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Properties/Resources.resx
@@ -508,4 +508,10 @@
Synchronisation stalled - {0}
{0} will be replaced with the account name
+
+ Changing other people's private events is not allowed.
+
+
+ Private event
+
\ No newline at end of file
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/Enums.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/Enums.cs
index deadd05..5dcf084 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/Enums.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/Enums.cs
@@ -84,4 +84,13 @@ namespace Acacia.Stubs
CC = 2,
BCC = 3
}
+
+ // Replacement for OlSensitivity
+ public enum Sensitivity
+ {
+ Normal,
+ Personal,
+ Private,
+ Confidential
+ }
}
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IAddIn.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IAddIn.cs
index 473da0c..f88b778 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IAddIn.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IAddIn.cs
@@ -42,6 +42,7 @@ namespace Acacia.Stubs
OutlookUI OutlookUI { get; }
ISystemWindow Window { get; }
IExplorer GetActiveExplorer();
+ IInspectors GetInspectors();
#endregion
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IAppointmentItem.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IAppointmentItem.cs
index 3f088da..8b069eb 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IAppointmentItem.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IAppointmentItem.cs
@@ -31,5 +31,7 @@ namespace Acacia.Stubs
DateTime End { get; set; }
string Location { get; set; }
string GlobalAppointmentId { get; }
+
+ Sensitivity Sensitivity { get; }
}
}
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IExplorer.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IExplorer.cs
index 45424dc..1e88ce7 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IExplorer.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IExplorer.cs
@@ -25,18 +25,17 @@ namespace Acacia.Stubs
{
public interface IExplorer : IOutlookWindow
{
- ///
- /// Returns the command bars.
- ///
- /// The command bars. The caller is responsible for disposing.
- ICommandBars GetCommandBars();
-
///
/// Returns the currently selected folder, or null if no folder is selected.
///
/// The folder. The caller is responsible for disposing.
IFolder GetCurrentFolder();
+ ///
+ /// Closes the explorer window.
+ ///
+ void Close();
+
#region Events
// TODO: custom delegates
event NSOutlookDelegates.ExplorerEvents_10_SelectionChangeEventHandler SelectionChange;
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IInspector.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IInspector.cs
new file mode 100644
index 0000000..8b45327
--- /dev/null
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IInspector.cs
@@ -0,0 +1,47 @@
+/// Copyright 2017 Kopano b.v.
+///
+/// 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,
+/// as published by the Free Software Foundation.
+///
+/// This program is distributed in the hope that it will be useful,
+/// but WITHOUT ANY WARRANTY; without even the implied warranty of
+/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
+/// GNU Affero General Public License for more details.
+///
+/// You should have received a copy of the GNU Affero General Public License
+/// along with this program.If not, see.
+///
+/// Consult LICENSE file for details
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using NSOutlookDelegates = Microsoft.Office.Interop.Outlook;
+
+namespace Acacia.Stubs
+{
+ public enum InspectorClose
+ {
+ Save,
+ Discard,
+ PromptForSave
+ }
+
+ public interface IInspector : IOutlookWindow
+ {
+
+ ///
+ /// Closes the inspector window.
+ ///
+ void Close(InspectorClose mode);
+
+ ///
+ /// Returns the currently selected item, or null if no item is selected.
+ ///
+ /// The item. The caller is responsible for disposing.
+ IItem GetCurrentItem();
+ }
+}
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IInspectors.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IInspectors.cs
new file mode 100644
index 0000000..2c0ae86
--- /dev/null
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IInspectors.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Acacia.Stubs
+{
+ public interface IInspectors : IEnumerable
+ {
+ }
+}
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IOutlookWindow.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IOutlookWindow.cs
index 6ba89e5..44e2205 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IOutlookWindow.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IOutlookWindow.cs
@@ -24,5 +24,11 @@ namespace Acacia.Stubs
{
public interface IOutlookWindow : IComWrapper
{
+ ///
+ /// Returns the command bars.
+ ///
+ /// The command bars. The caller is responsible for disposing.
+ ICommandBars GetCommandBars();
+
}
}
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/AddInWrapper.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/AddInWrapper.cs
index 33b2d01..74d51d9 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/AddInWrapper.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/AddInWrapper.cs
@@ -254,6 +254,11 @@ namespace Acacia.Stubs.OutlookWrappers
return new ExplorerWrapper(_app.ActiveExplorer());
}
+ public IInspectors GetInspectors()
+ {
+ return new InspectorsWrapper(_app.Inspectors);
+ }
+
#region Window handle
///
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/AppointmentItemWrapper.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/AppointmentItemWrapper.cs
index a866794..8a77c90 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/AppointmentItemWrapper.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/AppointmentItemWrapper.cs
@@ -57,6 +57,11 @@ namespace Acacia.Stubs.OutlookWrappers
get { return _item.GlobalAppointmentID; }
}
+ public Sensitivity Sensitivity
+ {
+ get { return (Sensitivity)_item.Sensitivity; }
+ }
+
#endregion
#region Wrapper methods
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/ExplorerWrapper.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/ExplorerWrapper.cs
index e400a28..02f05f7 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/ExplorerWrapper.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/ExplorerWrapper.cs
@@ -30,6 +30,11 @@ namespace Acacia.Stubs.OutlookWrappers
{
}
+ public void Close()
+ {
+ _item.Close();
+ }
+
public event NSOutlook.ExplorerEvents_10_SelectionChangeEventHandler SelectionChange
{
add { _item.SelectionChange += value; }
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/InspectorWrapper.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/InspectorWrapper.cs
new file mode 100644
index 0000000..6e907f8
--- /dev/null
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/InspectorWrapper.cs
@@ -0,0 +1,48 @@
+/// Copyright 2017 Kopano b.v.
+///
+/// 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,
+/// as published by the Free Software Foundation.
+///
+/// This program is distributed in the hope that it will be useful,
+/// but WITHOUT ANY WARRANTY; without even the implied warranty of
+/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
+/// GNU Affero General Public License for more details.
+///
+/// You should have received a copy of the GNU Affero General Public License
+/// along with this program.If not, see.
+///
+/// Consult LICENSE file for details
+
+using Acacia.Utils;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using NSOutlook = Microsoft.Office.Interop.Outlook;
+
+namespace Acacia.Stubs.OutlookWrappers
+{
+ class InspectorWrapper : ComWrapper, IInspector
+ {
+ public InspectorWrapper(NSOutlook.Inspector item) : base(item)
+ {
+ }
+
+ public void Close(InspectorClose mode)
+ {
+ _item.Close((NSOutlook.OlInspectorClose)mode);
+ }
+
+ public ICommandBars GetCommandBars()
+ {
+ return new CommandBarsWrapper(_item.CommandBars);
+ }
+
+ public IItem GetCurrentItem()
+ {
+ return Mapping.Wrap(_item.CurrentItem);
+ }
+ }
+}
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/InspectorsWrapper.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/InspectorsWrapper.cs
new file mode 100644
index 0000000..a808c75
--- /dev/null
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/InspectorsWrapper.cs
@@ -0,0 +1,30 @@
+using Acacia.Utils;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using NSOutlook = Microsoft.Office.Interop.Outlook;
+using System.Collections;
+
+namespace Acacia.Stubs.OutlookWrappers
+{
+ class InspectorsWrapper: ComWrapper, IInspectors
+ {
+ public InspectorsWrapper(NSOutlook.Inspectors item) : base(item)
+ {
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ foreach (NSOutlook.Inspector inspector in _item)
+ yield return new InspectorWrapper(inspector);
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ foreach (NSOutlook.Inspector inspector in _item)
+ yield return new InspectorWrapper(inspector);
+ }
+ }
+}