[KOE-114] Added change suppression for private events in shared folders.

This commit is contained in:
Patrick Simpson 2017-06-07 13:18:40 +02:00
parent 16cb96699e
commit d21eb821a4
17 changed files with 312 additions and 9 deletions

View File

@ -325,6 +325,8 @@
<Compile Include="Stubs\IAccount.cs" />
<Compile Include="Stubs\IAddIn.cs" />
<Compile Include="Stubs\IAddressEntry.cs" />
<Compile Include="Stubs\IInspector.cs" />
<Compile Include="Stubs\IInspectors.cs" />
<Compile Include="Stubs\IMeetingItem.cs" />
<Compile Include="Stubs\ICommandBars.cs" />
<Compile Include="Stubs\IComWrapper.cs" />
@ -344,6 +346,7 @@
<Compile Include="Stubs\OutlookWrappers\AccountWrapper.cs" />
<Compile Include="Stubs\OutlookWrappers\AddInWrapper.cs" />
<Compile Include="Stubs\OutlookWrappers\AddressEntryWrapper.cs" />
<Compile Include="Stubs\OutlookWrappers\InspectorWrapper.cs" />
<Compile Include="Stubs\OutlookWrappers\MeetingItemWrapper.cs" />
<Compile Include="Stubs\OutlookWrappers\CommandBarsWrapper.cs" />
<Compile Include="Stubs\OutlookWrappers\ExplorerWrapper.cs" />
@ -354,6 +357,7 @@
<Compile Include="Stubs\OutlookWrappers\PictureWrapper.cs" />
<Compile Include="Stubs\OutlookWrappers\RecipientsWrapper.cs" />
<Compile Include="Stubs\OutlookWrappers\RecipientWrapper.cs" />
<Compile Include="Stubs\OutlookWrappers\InspectorsWrapper.cs" />
<Compile Include="Stubs\OutlookWrappers\SignaturesWrapper.cs" />
<Compile Include="Stubs\OutlookWrappers\SignatureWrapper.cs" />
<Compile Include="Stubs\OutlookWrappers\StoresWrapper.cs" />

View File

@ -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
);

View File

@ -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);
}
/// <summary>
/// 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.
/// </summary>
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
}
}

View File

@ -1111,6 +1111,24 @@ namespace Acacia.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Changing other people&apos;s private events is not allowed..
/// </summary>
internal static string SharedFolders_PrivateEvent_Body {
get {
return ResourceManager.GetString("SharedFolders_PrivateEvent_Body", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Private event.
/// </summary>
internal static string SharedFolders_PrivateEvent_Title {
get {
return ResourceManager.GetString("SharedFolders_PrivateEvent_Title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Public folders.
/// </summary>

View File

@ -508,4 +508,10 @@
<value>Synchronisation stalled - {0}</value>
<comment>{0} will be replaced with the account name</comment>
</data>
<data name="SharedFolders_PrivateEvent_Body" xml:space="preserve">
<value>Changing other people's private events is not allowed.</value>
</data>
<data name="SharedFolders_PrivateEvent_Title" xml:space="preserve">
<value>Private event</value>
</data>
</root>

View File

@ -84,4 +84,13 @@ namespace Acacia.Stubs
CC = 2,
BCC = 3
}
// Replacement for OlSensitivity
public enum Sensitivity
{
Normal,
Personal,
Private,
Confidential
}
}

View File

@ -42,6 +42,7 @@ namespace Acacia.Stubs
OutlookUI OutlookUI { get; }
ISystemWindow Window { get; }
IExplorer GetActiveExplorer();
IInspectors GetInspectors();
#endregion

View File

@ -31,5 +31,7 @@ namespace Acacia.Stubs
DateTime End { get; set; }
string Location { get; set; }
string GlobalAppointmentId { get; }
Sensitivity Sensitivity { get; }
}
}

View File

@ -25,18 +25,17 @@ namespace Acacia.Stubs
{
public interface IExplorer : IOutlookWindow
{
/// <summary>
/// Returns the command bars.
/// </summary>
/// <returns>The command bars. The caller is responsible for disposing.</returns>
ICommandBars GetCommandBars();
/// <summary>
/// Returns the currently selected folder, or null if no folder is selected.
/// </summary>
/// <returns>The folder. The caller is responsible for disposing.</returns>
IFolder GetCurrentFolder();
/// <summary>
/// Closes the explorer window.
/// </summary>
void Close();
#region Events
// TODO: custom delegates
event NSOutlookDelegates.ExplorerEvents_10_SelectionChangeEventHandler SelectionChange;

View File

@ -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<http://www.gnu.org/licenses/>.
///
/// 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
{
/// <summary>
/// Closes the inspector window.
/// </summary>
void Close(InspectorClose mode);
/// <summary>
/// Returns the currently selected item, or null if no item is selected.
/// </summary>
/// <returns>The item. The caller is responsible for disposing.</returns>
IItem GetCurrentItem();
}
}

View File

@ -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<IInspector>
{
}
}

View File

@ -24,5 +24,11 @@ namespace Acacia.Stubs
{
public interface IOutlookWindow : IComWrapper
{
/// <summary>
/// Returns the command bars.
/// </summary>
/// <returns>The command bars. The caller is responsible for disposing.</returns>
ICommandBars GetCommandBars();
}
}

View File

@ -254,6 +254,11 @@ namespace Acacia.Stubs.OutlookWrappers
return new ExplorerWrapper(_app.ActiveExplorer());
}
public IInspectors GetInspectors()
{
return new InspectorsWrapper(_app.Inspectors);
}
#region Window handle
/// <summary>

View File

@ -57,6 +57,11 @@ namespace Acacia.Stubs.OutlookWrappers
get { return _item.GlobalAppointmentID; }
}
public Sensitivity Sensitivity
{
get { return (Sensitivity)_item.Sensitivity; }
}
#endregion
#region Wrapper methods

View File

@ -30,6 +30,11 @@ namespace Acacia.Stubs.OutlookWrappers
{
}
public void Close()
{
_item.Close();
}
public event NSOutlook.ExplorerEvents_10_SelectionChangeEventHandler SelectionChange
{
add { _item.SelectionChange += value; }

View File

@ -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<http://www.gnu.org/licenses/>.
///
/// 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<NSOutlook.Inspector>, 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<IItem>(_item.CurrentItem);
}
}
}

View File

@ -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<NSOutlook.Inspectors>, IInspectors
{
public InspectorsWrapper(NSOutlook.Inspectors item) : base(item)
{
}
public IEnumerator<IInspector> 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);
}
}
}