diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj index 3d89561..45fe95a 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj @@ -457,6 +457,7 @@ + diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/FeatureSharedFolders.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/FeatureSharedFolders.cs index 9c2755e..7886259 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/FeatureSharedFolders.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/FeatureSharedFolders.cs @@ -17,6 +17,7 @@ using Acacia.Features.SecondaryContacts; using Acacia.Stubs; using Acacia.UI; using Acacia.UI.Outlook; +using Acacia.Utils; using Acacia.ZPush; using Acacia.ZPush.API.SharedFolders; using Acacia.ZPush.Connect; @@ -56,6 +57,13 @@ namespace Acacia.Features.SharedFolders } private static readonly BoolOption OPTION_REMINDERS_KEEP_RUNNING = new BoolOption("RemindersKeepRunning", true); + [AcaciaOption("The format for local names of shared folders. May contain contact fields and %foldername%.")] + public string DefaultFolderNameFormat + { + get { return RegistryUtil.GetConfigValue("SharedFolders", "DefaultFolderNameFormat", "%foldername% - %username%"); } + set { RegistryUtil.SetConfigValue("SharedFolders", "DefaultFolderNameFormat", value, Microsoft.Win32.RegistryValueKind.String); } + } + #endregion public override void Startup() diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/FolderTreeNode.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/FolderTreeNode.cs index a20a1fa..16c3230 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/FolderTreeNode.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/FolderTreeNode.cs @@ -58,6 +58,14 @@ namespace Acacia.Features.SharedFolders base.OnCheckStateChanged(); } + public string DefaultName + { + get + { + return _store.DefaultNameForFolder(AvailableFolder); + } + } + public AvailableFolder AvailableFolder { get { return _folder; } } public bool IsShared { get { return CheckState != System.Windows.Forms.CheckState.Unchecked; } } diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.cs index a39422f..9d0f93d 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersDialog.cs @@ -260,7 +260,8 @@ namespace Acacia.Features.SharedFolders } // Add the node - node = new StoreTreeNode(_folders, user, user.DisplayName, currentShares ?? new Dictionary()); + node = new StoreTreeNode(_folders, gabLookup.GAB, + user, user.DisplayName, currentShares ?? new Dictionary()); node.DirtyChanged += UserSharesChanged; _userFolders.Add(user, node); kTreeFolders.RootNodes.Add(node); @@ -524,7 +525,7 @@ namespace Acacia.Features.SharedFolders _optionNameNode.SharedFolder = _optionNameNode.SharedFolder.WithName(textName.Text); // If the share name matches the folder name, track update - bool track = _optionNameNode.SharedFolder.Name == _optionNameNode.AvailableFolder.DefaultName; + bool track = _optionNameNode.SharedFolder.Name == _optionNameNode.DefaultName; _optionNameNode.SharedFolder = _optionNameNode.SharedFolder.WithFlagTrackShareName(track); } } diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersManager.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersManager.cs index e87fc3e..adbe510 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersManager.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedFoldersManager.cs @@ -41,6 +41,8 @@ namespace Acacia.Features.SharedFolders _query.Dispose(); } + public FeatureSharedFolders Feature { get { return _feature; } } + #region API /// diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/StoreTreeNode.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/StoreTreeNode.cs index 1b236c9..d0e3c39 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/StoreTreeNode.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/StoreTreeNode.cs @@ -26,6 +26,7 @@ using System.Threading; using Acacia.ZPush.API.SharedFolders; using Acacia.ZPush.Connect; using Acacia.Native; +using Acacia.Features.GAB; namespace Acacia.Features.SharedFolders { @@ -41,11 +42,19 @@ namespace Acacia.Features.SharedFolders private readonly Dictionary _initialShares; private readonly Dictionary _currentShares; - public StoreTreeNode(SharedFoldersManager folders, GABUser user, string text, Dictionary currentFolders) + private readonly FeatureSharedFolders _feature; + private readonly GABHandler _gab; + private readonly GABUser _user; + + public StoreTreeNode(SharedFoldersManager folders, GABHandler gab, GABUser user, string text, + Dictionary currentFolders) : base(text) { this._initialShares = currentFolders; + this._feature = folders.Feature; + this._gab = gab; + this._user = user; // Create an empty current state. When loading the nodes, the shares will be added. This has the benefit of // cleaning up automatically any obsolote shares. @@ -92,7 +101,7 @@ namespace Acacia.Features.SharedFolders private SharedFolder CreateDefaultShare(AvailableFolder folder) { - SharedFolder share = new SharedFolder(folder); + SharedFolder share = new SharedFolder(folder, DefaultNameForFolder(folder)); // Default send as for mail folders if (folder.Type.IsMail()) @@ -109,16 +118,44 @@ namespace Acacia.Features.SharedFolders } } + internal string DefaultNameForFolder(AvailableFolder folder) + { + // Default include the store name in root folders + if (folder.ParentId.IsNone) + { + if (folder.DefaultName == null) + { + using (ContactStringReplacer replacer = ContactStringReplacer.FromGAB(_gab, _user)) + { + replacer.TokenOpen = "%"; + replacer.TokenClose = "%"; + replacer.UnknownReplacer = (token) => + { + if (token == "foldername") + return folder.Name; + return ""; + }; + folder.DefaultName = replacer.Replace(_feature.DefaultFolderNameFormat); + } + } + return folder.DefaultName; + } + else + { + return folder.Name; + } + } + private SharedFolder GetInitialShareState(AvailableFolder folder) { SharedFolder state; if (_initialShares.TryGetValue(folder.BackendId, out state)) { // If the folder has been renamed, update if we're tracing it. - if (state.Name != folder.DefaultName) + if (state.Name != DefaultNameForFolder(folder)) { if (state.FlagUpdateShareName) - state = state.WithName(folder.DefaultName); + state = state.WithName(DefaultNameForFolder(folder)); } return state; } diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/Signatures/FeatureSignatures.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/Signatures/FeatureSignatures.cs index ffdf610..e2c0576 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/Signatures/FeatureSignatures.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/Signatures/FeatureSignatures.cs @@ -295,27 +295,16 @@ namespace Acacia.Features.Signatures } private void ReplacePlaceholders(GABHandler gab, params string[] signatures) - { - IContactItem us = null; + { + ContactStringReplacer replacer = null; try { - IAccount account = gab.ActiveAccount.Account; - - // Look for the email address. If found, use the account associated with the GAB - using (ISearch search = gab.Contacts.Search()) - { - search.AddField("urn:schemas:contacts:customerid").SetOperation(SearchOperation.Equal, account.UserName); - IItem result = search.SearchOne(); - us = result as IContactItem; - if (result != null && result != us) - result.Dispose(); - } - - if (us != null) + replacer = ContactStringReplacer.FindUs(gab); + if (replacer != null) { foreach (string signatureName in signatures) { - ReplacePlaceholders(gab.ActiveAccount, us, signatureName); + ReplacePlaceholders(replacer, signatureName); } } } @@ -325,12 +314,12 @@ namespace Acacia.Features.Signatures } finally { - if (us != null) - us.Dispose(); + if (replacer != null) + replacer.Dispose(); } } - private void ReplacePlaceholders(ZPushAccount account, IContactItem us, string signatureName) + private void ReplacePlaceholders(ContactStringReplacer replacer, string signatureName) { if (string.IsNullOrEmpty(signatureName)) return; @@ -347,35 +336,7 @@ namespace Acacia.Features.Signatures 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.JobTitle ?? ""; - 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 ""; - }); + string replaced = replacer.Replace(template); signature.SetContent(replaced, format); } } diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/API/SharedFolders/AvailableFolder.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/API/SharedFolders/AvailableFolder.cs index 89daed6..5e3d779 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/API/SharedFolders/AvailableFolder.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/API/SharedFolders/AvailableFolder.cs @@ -78,22 +78,15 @@ namespace Acacia.ZPush.API.SharedFolders public string Name { get { return _data.displayname; } } - public string DefaultName - { - get - { - // Default include the store name in root folders - if (ParentId.IsNone) - return Name + " - " + Store.UserName; - else - return Name; - } - } - public OutlookConstants.SyncType Type { get { return _data.type; } } public GABUser Store { get; private set; } + /// + /// Cache for DefaultName, used by StoreTreeNode. + /// + public string DefaultName { get; set; } + #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 c74521a..c7fa0ca 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/API/SharedFolders/SharedFolder.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/API/SharedFolders/SharedFolder.cs @@ -95,14 +95,14 @@ namespace Acacia.ZPush.API.SharedFolders /// /// Creates an instances for the specified folder. /// - public SharedFolder(AvailableFolder folder) + public SharedFolder(AvailableFolder folder, string name) { _data = new SoapData() { store = folder.Store.UserName, folderid = folder.BackendId, parentid = folder.ParentIdAsBackend, - name = folder.DefaultName, + name = name, type = OutlookConstants.USER_SYNC_TYPES[(int)folder.Type], flags = folder.Type.IsMail() ? ShareFlags.SendAsOwner : ShareFlags.None }; diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/ContactStringReplacer.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/ContactStringReplacer.cs new file mode 100644 index 0000000..1c4e6de --- /dev/null +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/ContactStringReplacer.cs @@ -0,0 +1,127 @@ +using Acacia.Features.GAB; +using Acacia.Stubs; +using Acacia.Utils; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Acacia.ZPush +{ + /// + /// Replaces placeholders in a string with information from a contact. + /// + public class ContactStringReplacer + : + IDisposable + { + private readonly IContactItem _contact; + + public string TokenOpen + { + get; + set; + } + + public string TokenClose + { + get; + set; + } + + public delegate string TokenReplacer(string token); + + public TokenReplacer UnknownReplacer + { + get; + set; + } + + public ContactStringReplacer(IContactItem contact) + { + this._contact = contact; + TokenOpen = "{%"; + TokenClose = "}"; + } + + public static ContactStringReplacer FindUs(GABHandler gab) + { + // Look for the email address. If found, use the account associated with the GAB + using (ISearch search = gab.Contacts.Search()) + { + IAccount account = gab.ActiveAccount.Account; + + search.AddField("urn:schemas:contacts:customerid").SetOperation(SearchOperation.Equal, account.UserName); + IItem result = search.SearchOne(); + IContactItem us = result as IContactItem; + if (result != null && result != us) + { + result.Dispose(); + return null; + } + + if (us == null) + return null; + + return new ContactStringReplacer(us); + } + } + + public static ContactStringReplacer FromGAB(GABHandler gab, GABUser user) + { + using (ISearch search = gab.Contacts.Search()) + { + search.AddField("urn:schemas:contacts:customerid").SetOperation(SearchOperation.Equal, user.UserName); + IItem result = search.SearchOne(); + IContactItem contact = result as IContactItem; + if (result != null && result != contact) + result.Dispose(); + + return new ContactStringReplacer(contact); + } + + } + + public void Dispose() + { + _contact.Dispose(); + } + + public string Replace(string template) + { + string replaced = template.ReplaceStringTokens(TokenOpen, TokenClose, (token) => + { + if (token == "firstname") return _contact.FirstName ?? ""; + if (token == "initials") return _contact.Initials ?? ""; + if (token == "lastname") return _contact.LastName ?? ""; + if (token == "displayname") return _contact.FullName ?? ""; + if (token == "username") return _contact.CustomerID ?? ""; + if (token == "title") return _contact.JobTitle ?? ""; + if (token == "company") return _contact.CompanyName ?? ""; + if (token == "office") return _contact.OfficeLocation ?? ""; + if (token == "phone") return _contact.BusinessTelephoneNumber ?? _contact.MobileTelephoneNumber ?? ""; + if (token == "primary_email") return _contact.Email1Address ?? ""; + if (token == "address") return _contact.BusinessAddress ?? ""; + if (token == "city") return _contact.BusinessAddressCity ?? ""; + if (token == "state") return _contact.BusinessAddressState ?? ""; + if (token == "zipcode") return _contact.BusinessAddressPostalCode ?? ""; + if (token == "country") return _contact.BusinessAddressState ?? ""; + if (token == "phone_business") return _contact.BusinessTelephoneNumber ?? ""; + if (token == "phone_fax") return _contact.BusinessFaxNumber ?? ""; + if (token == "phone_home") return _contact.HomeTelephoneNumber ?? ""; + if (token == "phone_mobile") return _contact.MobileTelephoneNumber ?? ""; + if (token == "phone_pager") return _contact.PagerNumber ?? ""; + return GetUnknownToken(token); + }); + return replaced; + } + + private string GetUnknownToken(string token) + { + if (UnknownReplacer != null) + return UnknownReplacer(token); + return ""; + } + } +}