Modified to allow initial selection of all folders and containers
[pithos-ms-client] / trunk / Pithos.Client.WPF / Preferences / PreferencesViewModel.cs
index 3970428..2b5f249 100644 (file)
-#region
-/* -----------------------------------------------------------------------
- * <copyright file="PreferencesViewModel.cs" company="GRNet">
- * 
- * Copyright 2011-2012 GRNET S.A. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- *   1. Redistributions of source code must retain the above
- *      copyright notice, this list of conditions and the following
- *      disclaimer.
- *
- *   2. Redistributions in binary form must reproduce the above
- *      copyright notice, this list of conditions and the following
- *      disclaimer in the documentation and/or other materials
- *      provided with the distribution.
- *
- *
- * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
- * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- * The views and conclusions contained in the software and
- * documentation are those of the authors and should not be
- * interpreted as representing official policies, either expressed
- * or implied, of GRNET S.A.
- * </copyright>
- * -----------------------------------------------------------------------
- */
-#endregion
-
-
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Collections.Specialized;
-using System.ComponentModel.Composition;
-using System.Diagnostics;
-using System.IO;
-using System.Net;
-using System.Reflection;
-using System.Threading.Tasks;
-using System.Windows;
-using System.Windows.Forms;
-using Caliburn.Micro;
-using Pithos.Client.WPF.Configuration;
-using Pithos.Client.WPF.Properties;
-using Pithos.Client.WPF.SelectiveSynch;
-using Pithos.Client.WPF.Utils;
-using Pithos.Core;
-using Pithos.Interfaces;
-using System;
-using System.Linq;
-using Pithos.Network;
-using MessageBox = System.Windows.MessageBox;
-using Screen = Caliburn.Micro.Screen;
-
-namespace Pithos.Client.WPF.Preferences
-{
-    /// <summary>
-    /// The preferences screen displays user and account settings and updates the PithosMonitor
-    /// classes when account settings change.
-    /// </summary>
-    /// <remarks>
-    /// The class is a single ViewModel for all Preferences tabs. It can be broken in separate
-    /// ViewModels, one for each tab.
-    /// </remarks>
-    [Export, PartCreationPolicy(CreationPolicy.Shared)]
-    public class PreferencesViewModel : Screen
-    {
-        private readonly IEventAggregator _events;
-
-        //Logging in the Pithos client is provided by log4net
-        private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
-
-        private PithosSettings _settings;
-        public PithosSettings Settings
-        {
-            get { return _settings; }
-            set
-            {
-                _settings = value;
-                NotifyOfPropertyChange(()=>Settings);
-            }
-        }
-
-        private ObservableConcurrentCollection<AccountViewModel> _accounts;
-        public ObservableConcurrentCollection<AccountViewModel> Accounts
-        {
-            get { return _accounts; }
-            set 
-            { 
-                _accounts = value;
-                NotifyOfPropertyChange(()=>Accounts);
-            }
-        }
-        
-        public bool StartOnSystemStartup { get; set; }
-
-        public ShellViewModel Shell { get;  set; }
-        //ShellExtensionController _extensionController=new ShellExtensionController();
-
-        public PreferencesViewModel(IWindowManager windowManager, IEventAggregator events, ShellViewModel shell, PithosSettings settings, string currentTab)
-        {
-            // ReSharper disable DoNotCallOverridableMethodsInConstructor
-            //Caliburn.Micro uses DisplayName for the view's title
-            DisplayName = "Pithos+ Preferences";
-            // ReSharper restore DoNotCallOverridableMethodsInConstructor
-
-            _windowManager = windowManager;
-            _events = events;
-
-            Shell = shell;
-            
-            Settings=settings;
-            Accounts = new ObservableConcurrentCollection<AccountViewModel>();
-            if (settings.Accounts == null)
-            {
-                settings.Accounts=new AccountsCollection();
-                settings.Save();
-            }
-            var accountVMs = from account in settings.Accounts
-                             select new AccountViewModel(account);
-
-            Accounts.AddFromEnumerable(accountVMs);
-            
-            var startupPath = Environment.GetFolderPath(Environment.SpecialFolder.Startup);
-            _shortcutPath = Path.Combine(startupPath, "Pithos.lnk");
-
-
-            StartOnSystemStartup = File.Exists(_shortcutPath);
-
-            SelectedTab = currentTab;
-        }
-
-        private string _selectedTab="General";
-        public string SelectedTab
-        {
-            get { return _selectedTab; }
-            set
-            {
-                _selectedTab = value??"General";
-                NotifyOfPropertyChange(()=>SelectedTab);
-                NotifyOfPropertyChange(() => AccountTabSelected);
-            }
-        }
-
-
-        public bool AccountTabSelected
-        {
-            get { return _selectedTab == "AccountTab"; }
-        }
-        #region Preferences Properties
-
-        private bool _noProxy;
-        public bool NoProxy
-        {
-            get { return _noProxy; }
-            set
-            {
-                _noProxy = value;
-                NotifyOfPropertyChange(()=>NoProxy);
-            }
-        }
-
-
-        private bool _defaultProxy;
-
-        public bool DefaultProxy
-        {
-            get { return _defaultProxy; }
-            set
-            {
-                _defaultProxy = value;
-                NotifyOfPropertyChange(() => DefaultProxy);
-            }
-        }
-
-
-        private bool _manualProxy;
-
-        public bool ManualProxy
-        {
-            get { return _manualProxy; }
-            set
-            {
-                _manualProxy = value;
-                NotifyOfPropertyChange(() => ManualProxy);
-            }
-        }
-        #endregion
-
-
-        public int StartupDelay
-        {
-            get { return (int) Settings.StartupDelay.TotalMinutes; }
-            set
-            {
-                if (value<0)
-                    throw new ArgumentOutOfRangeException("value",Resources.PreferencesViewModel_StartupDelay_Greater_or_equal_to_0);
-                Settings.StartupDelay = TimeSpan.FromMinutes(value);
-                NotifyOfPropertyChange(()=>StartupDelay);
-            }
-        }
-       
-        #region Commands
-        
-        public bool CanSelectiveSyncFolders
-        {
-            get { return CurrentAccount != null; }
-        }
-
-        public void SelectiveSyncFolders()
-        {
-            //var monitor = Shell.Monitors[CurrentAccount.AccountKey];
-            
-
-            var model = new SelectiveSynchViewModel(/*monitor,*/_events,CurrentAccount.Account,CurrentAccount.ApiKey);
-            if (_windowManager.ShowDialog(model) == true)
-            {
-                
-            }
-        }
-
-       /* private bool _networkTracing;
-        public bool NetworkTracing
-        {
-            get { return _networkTracing; }
-            set
-            {
-                _networkTracing = value;
-                
-            }
-        }*/
-
-        public void RefreshApiKey()
-        {
-            //_events.Publish(new Notification { Title = "Authorization failed", Message = "Your API Key has probably expired. You will be directed to a page where you can renew it", Level = TraceLevel.Error });
-            if (CurrentAccount == null)
-                return;
-            try
-            {
-
-                var name = CurrentAccount.AccountName;
-
-                var loginUri = new Uri(new Uri(CurrentAccount.ServerUrl), "login");
-                var credentials = PithosAccount.RetrieveCredentials(loginUri.ToString(),name);
-                if (credentials==null)
-                    return;
-                //The server will return credentials for a different account, not just the current account
-                //We need to find the correct account first
-                var account = Accounts.First(act => act.AccountName == credentials.UserName && act.ServerUrl == CurrentAccount.ServerUrl);
-                account.ApiKey = credentials.Password;                
-                account.IsExpired = false;
-                Settings.Save();
-                TaskEx.Delay(10000).ContinueWith(_ =>Shell.MonitorAccount(account.Account));
-                NotifyOfPropertyChange(() => Accounts);
-            }
-            catch (AggregateException exc)
-            {
-                string message = String.Format("API Key retrieval failed");
-                Log.Error(message, exc.InnerException);
-                _events.Publish(new Notification { Title = "Authorization failed", Message = message, Level = TraceLevel.Error });
-            }
-            catch (Exception exc)
-            {
-                string message = String.Format("API Key retrieval failed");
-                Log.Error(message, exc);
-                _events.Publish(new Notification { Title = "Authorization failed", Message = message, Level = TraceLevel.Error });
-            }
-
-        }
-
-    
-        public void OpenLogPath()
-        {
-            Shell.OpenLogPath();
-        }
-
-        public void OpenLogConsole()
-        {
-            var logView=IoC.Get<LogConsole.LogConsoleViewModel>();            
-            _windowManager.ShowWindow(logView);
-        }
-
-        public void SaveChanges()
-        {
-            DoSave();
-            TryClose(true);
-        }
-
-        public void RejectChanges()
-        {
-            Settings.Reload();
-            TryClose(false);
-        }
-
-        public void ApplyChanges()
-        {
-            DoSave();
-        }
-
-        private void DoSave()
-        {
-            //SetStartupMode();            
-
-            //Ensure we save the settings changes first
-            foreach (var account in _accountsToRemove)
-            {
-                Settings.Accounts.Remove(account);
-            }
-
-            foreach (var account in _accountsToAdd)
-            {
-                Settings.Accounts.Add(account);    
-            }
-
-            Settings.Save();
-
-
-            try
-            {
-                foreach (var account in _accountsToRemove)
-                {
-                    Shell.RemoveMonitor(account.ServerUrl, account.AccountName);
-                    Shell.RemoveAccountFromDatabase(account);
-                }
-
-                foreach (var account in Settings.Accounts)
-                {
-                    Shell.MonitorAccount(account);
-                }
-            }                
-            finally
-            {
-                _accountsToRemove.Clear();
-                _accountsToAdd.Clear();
-            }
-
-            NotifyOfPropertyChange(()=>Settings);
-
-            if (IgnoreCertificateErrors)
-                ServicePointManager.ServerCertificateValidationCallback= (sender,certificate,chain,errors)=> true;
-            else
-            {
-                ServicePointManager.ServerCertificateValidationCallback = null;
-            }
-        }
-
-     /*   public void ChangePithosFolder()
-        {
-            var browser = new FolderBrowserDialog();
-            browser.SelectedPath = Settings.PithosPath;
-            var result = browser.ShowDialog((IWin32Window)GetView());
-            if (result == DialogResult.OK)
-            {
-                var newPath = browser.SelectedPath;
-                var accountName = CurrentAccount.AccountName;
-                var monitor = Shell.Monitors[accountName];
-                monitor.Stop();
-                
-                Shell.Monitors.Remove(accountName);
-
-                Directory.Move(Settings.PithosPath, newPath);
-                Settings.PithosPath = newPath;
-                Settings.Save();
-
-                Shell.MonitorAccount(CurrentAccount);
-
-                NotifyOfPropertyChange(() => Settings);                
-            }
-        }
-*/
-
-
-        readonly List<AccountSettings> _accountsToAdd=new List<AccountSettings>();
-       public void AddAccount()
-       {
-           var wizard = new AddAccountViewModel();
-           if (_windowManager.ShowDialog(wizard) == true)
-           {
-               string selectedPath = wizard.AccountPath;
-               var initialRootPath = wizard.ShouldCreateOkeanosFolder?
-                   Path.Combine(selectedPath, "Okeanos")
-                   :selectedPath;
-               var actualRootPath= initialRootPath;
-               if (wizard.ShouldCreateOkeanosFolder)
-               {
-                   int attempt = 1;
-                   while (Directory.Exists(actualRootPath) || File.Exists(actualRootPath))
-                   {
-                       actualRootPath = String.Format("{0} {1}", initialRootPath, attempt++);
-                   }
-               }
-
-
-
-               var account = Accounts.FirstOrDefault(act => act.AccountName == wizard.AccountName && act.ServerUrl == wizard.CurrentServer);
-               if (account != null)
-               {
-                   if (MessageBox.Show("The account you specified already exists. Do you want to update it?", "The account exists") == MessageBoxResult.Yes)
-                   {
-                       account.ApiKey = wizard.Token;
-                       account.IsExpired = false;
-                       CurrentAccount = account;
-                   }
-               }
-               else
-               {
-                   var newAccount = new AccountSettings
-                                        {
-                                            AccountName = wizard.AccountName,
-                                            ServerUrl = wizard.CurrentServer,
-                                            ApiKey = wizard.Token,
-                                            RootPath = actualRootPath,
-                                            IsActive = wizard.IsAccountActive,
-                                        };
-
-
-                   var client = new CloudFilesClient(newAccount.AccountName, newAccount.ApiKey)
-                                    {
-                                        AuthenticationUrl = newAccount.ServerUrl, UsePithos = true
-                                    };
-                   client.Authenticate();
-
-
-                   var dirs = (from container in client.ListContainers(newAccount.AccountName)
-                               from dir in client.ListObjects(newAccount.AccountName, container.Name)
-                               where container.Name != "trash"
-                               select dir)
-                               .ToTree()
-                               .Select(d=>d.Uri.ToString())
-                               .ToArray();
-                              
-                   newAccount.SelectiveFolders.AddRange(dirs);
-
-                   //TODO:Add the "pithos" container as a default selection                   
-
-                   _accountsToAdd.Add(newAccount);
-                   var accountVm = new AccountViewModel(newAccount);
-                   (Accounts as IProducerConsumerCollection<AccountViewModel>).TryAdd(accountVm);
-                   CurrentAccount = accountVm;
-               }
-               NotifyOfPropertyChange(() => Accounts);
-               NotifyOfPropertyChange(() => Settings);   
-           }
-
-
-            
-       }
-
-/*
-        public void AddPithosAccount()
-       {
-            var credentials=PithosAccount.RetrieveCredentials(null);
-            if (credentials == null)
-                return;
-            var account = Settings.Accounts.FirstOrDefault(act => act.AccountName == credentials.UserName);
-            var accountVM = new AccountViewModel(account);
-            if (account == null)
-            {
-                account=new AccountSettings{
-                    AccountName=credentials.UserName,
-                    ApiKey=credentials.Password
-                };
-                Settings.Accounts.Add(account);
-                accountVM = new AccountViewModel(account);
-                (Accounts as IProducerConsumerCollection<AccountViewModel>).TryAdd(accountVM);
-            }
-            else
-            {
-                account.ApiKey=credentials.Password;
-            }
-            //SelectedAccountIndex= Settings.Accounts.IndexOf(account);
-            CurrentAccount = accountVM;
-            NotifyOfPropertyChange(() => Accounts);
-            NotifyOfPropertyChange(()=>Settings);                       
-       }
-*/
-
-
-        readonly List<AccountSettings> _accountsToRemove = new List<AccountSettings>();
-        public void RemoveAccount()
-        {
-            Accounts.TryRemove(CurrentAccount);
-            _accountsToRemove.Add(CurrentAccount.Account);
-
-            CurrentAccount = null;
-            NotifyOfPropertyChange(() => Accounts);
-
-            
-            //NotifyOfPropertyChange("Settings.Accounts");
-        }
-
-        public bool CanRemoveAccount
-        {
-            get { return (CurrentAccount != null); }
-        }
-
-        public bool CanClearAccountCache
-        {
-            get { return (CurrentAccount != null); }
-        }
-
-        public void ClearAccountCache()
-        {
-            if (MessageBoxResult.Yes == MessageBox.Show("You are about to delete all partially downloaded files from the account's cache.\n" +
-                            " You will have to download all partially downloaded data again\n" +
-                            "This change can not be undone\n\n" +
-            "Do you wish to delete all partially downloaded data?", "Warning! Clearing account cache",
-                            MessageBoxButton.YesNo,MessageBoxImage.Question,MessageBoxResult.No))
-            {
-
-                var cachePath = Path.Combine(CurrentAccount.RootPath, FolderConstants.CacheFolder);
-                var dir = new DirectoryInfo(cachePath);
-                //The file may not exist if we just created the account
-                if (!dir.Exists)
-                    return;
-                dir.EnumerateFiles().Apply(file=>file.Delete());
-                dir.EnumerateDirectories().Apply(folder => folder.Delete(true));
-            }
-        }
-
-
-        public bool ExtensionsActivated
-        {
-            get { return Settings.ExtensionsActivated; }
-            set
-            {
-                if (Settings.ExtensionsActivated == value)
-                    return;
-
-                Settings.ExtensionsActivated = value;
-
-/*
-                if (value)
-                    _extensionController.RegisterExtensions();
-                else
-                {
-                    _extensionController.UnregisterExtensions();
-                }
-*/
-                NotifyOfPropertyChange(() => ExtensionsActivated);
-            }
-        }
-
-        public bool DebugLoggingEnabled
-        {
-            get { return Settings.DebugLoggingEnabled; }
-            set { 
-                Settings.DebugLoggingEnabled = value;
-                NotifyOfPropertyChange(()=>DebugLoggingEnabled);
-            }
-        }
-
-        public bool IgnoreCertificateErrors
-        {
-            get { return Settings.IgnoreCertificateErrors; }
-            set {
-                Settings.IgnoreCertificateErrors = value;
-                NotifyOfPropertyChange(() => IgnoreCertificateErrors);
-            }
-        }
-       
-        #endregion
-
-       /* private int _selectedAccountIndex;
-        public int SelectedAccountIndex
-        {
-            get { return _selectedAccountIndex; }
-            set
-            {
-                //var accountCount=Settings.Accounts.Count;
-                //if (accountCount == 0)
-                //    return;
-                //if (0 <= value && value < accountCount)
-                //    _selectedAccountIndex = value;
-                //else
-                //    _selectedAccountIndex = 0;
-                _selectedAccountIndex = value;
-                NotifyOfPropertyChange(() => CurrentAccount);
-                NotifyOfPropertyChange(() => CanRemoveAccount);
-                NotifyOfPropertyChange(()=>SelectedAccountIndex);
-            }
-        }*/
-
-        private AccountViewModel _currentAccount;
-        private readonly IWindowManager _windowManager;
-        private readonly string _shortcutPath;
-
-
-        
-        public AccountViewModel CurrentAccount
-        {
-            get { return _currentAccount; }
-            set
-            {
-                _currentAccount = value;
-                NotifyOfPropertyChange(()=>CurrentAccount);
-                NotifyOfPropertyChange(() => CanRemoveAccount);
-                NotifyOfPropertyChange(() => CanSelectiveSyncFolders);
-                NotifyOfPropertyChange(() => CanMoveAccountFolder);
-                NotifyOfPropertyChange(() => CanClearAccountCache);
-            }
-        }
-
-/*
-        public AccountSettings CurrentAccount
-        {
-            get {
-                if (0 <= SelectedAccountIndex && SelectedAccountIndex < Settings.Accounts.Count)                    
-                    return Settings.Accounts[SelectedAccountIndex];
-                return null;
-            }
-
-        }
-*/
-
-
-        public bool CanMoveAccountFolder
-        {
-            get { return CurrentAccount != null; }
-        }
-
-    public void MoveAccountFolder()
-    {
-
-        using (var dlg = new FolderBrowserDialog())
-        {
-            var currentFolder = CurrentAccount.RootPath;
-            dlg.SelectedPath = currentFolder;
-            //Ask the user to select a folder
-            //Note: We need a parent window here, which we retrieve with GetView            
-            var view = (Window)GetView();            
-            if (DialogResult.OK != dlg.ShowDialog(new Wpf32Window(view)))
-                return;            
-
-            var newPath= dlg.SelectedPath;                
-            //Find the account's monitor and stop it
-            PithosMonitor monitor;
-            if (Shell.Monitors.TryGetValue(CurrentAccount.AccountKey, out monitor))
-            {
-                monitor.Stop();
-
-
-                var oldPath = monitor.RootPath;
-                //The old directory may not exist eg. if we create an account for the first time
-                if (Directory.Exists(oldPath))
-                {
-                    //If it does, do the move
-
-                    //Now Create all of the directories
-                    foreach (string dirPath in Directory.EnumerateDirectories(oldPath, "*",
-                                                           SearchOption.AllDirectories))
-                        Directory.CreateDirectory(dirPath.Replace(oldPath, newPath));
-
-                    //Copy all the files
-                    foreach (string newFilePath in Directory.EnumerateFiles(oldPath, "*.*",
-                                                                            SearchOption.AllDirectories))
-                        File.Copy(newFilePath, newFilePath.Replace(oldPath, newPath));
-
-                    Log.InfoFormat("Deleting account folder {0}",oldPath);
-                    Directory.Delete(oldPath, true);
-
-                    //We also need to change the path of the existing file states
-                    monitor.MoveFileStates(oldPath, newPath);
-                }
-            }
-            //Replace the old rootpath with the new
-            CurrentAccount.RootPath = newPath;
-            //TODO: This will save all settings changes. Too coarse grained, need to fix at a later date
-            Settings.Save();            
-            //And start the monitor on the new RootPath            
-            if (monitor != null)
-            {
-                monitor.RootPath = newPath;
-                if (CurrentAccount.IsActive)
-                    monitor.Start();
-            }
-            else
-                Shell.MonitorAccount(CurrentAccount.Account);
-            //Finally, notify that the Settings, CurrentAccount have changed
-            NotifyOfPropertyChange(() => CurrentAccount);
-            NotifyOfPropertyChange(() => Settings);
-
-        }
-    }
-    }
-}
+#region\r
+/* -----------------------------------------------------------------------\r
+ * <copyright file="PreferencesViewModel.cs" company="GRNet">\r
+ * \r
+ * Copyright 2011-2012 GRNET S.A. All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or\r
+ * without modification, are permitted provided that the following\r
+ * conditions are met:\r
+ *\r
+ *   1. Redistributions of source code must retain the above\r
+ *      copyright notice, this list of conditions and the following\r
+ *      disclaimer.\r
+ *\r
+ *   2. Redistributions in binary form must reproduce the above\r
+ *      copyright notice, this list of conditions and the following\r
+ *      disclaimer in the documentation and/or other materials\r
+ *      provided with the distribution.\r
+ *\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS\r
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR\r
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF\r
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\r
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\r
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
+ * POSSIBILITY OF SUCH DAMAGE.\r
+ *\r
+ * The views and conclusions contained in the software and\r
+ * documentation are those of the authors and should not be\r
+ * interpreted as representing official policies, either expressed\r
+ * or implied, of GRNET S.A.\r
+ * </copyright>\r
+ * -----------------------------------------------------------------------\r
+ */\r
+#endregion\r
+\r
+\r
+using System.Collections.Concurrent;\r
+using System.Collections.Generic;\r
+using System.Collections.Specialized;\r
+using System.ComponentModel.Composition;\r
+using System.Diagnostics;\r
+using System.IO;\r
+using System.Net;\r
+using System.Reflection;\r
+using System.Threading.Tasks;\r
+using System.Windows;\r
+using System.Windows.Forms;\r
+using Caliburn.Micro;\r
+using Pithos.Client.WPF.Configuration;\r
+using Pithos.Client.WPF.Properties;\r
+using Pithos.Client.WPF.SelectiveSynch;\r
+using Pithos.Client.WPF.Utils;\r
+using Pithos.Core;\r
+using Pithos.Interfaces;\r
+using System;\r
+using System.Linq;\r
+using Pithos.Network;\r
+using MessageBox = System.Windows.MessageBox;\r
+using Screen = Caliburn.Micro.Screen;\r
+\r
+namespace Pithos.Client.WPF.Preferences\r
+{\r
+    /// <summary>\r
+    /// The preferences screen displays user and account settings and updates the PithosMonitor\r
+    /// classes when account settings change.\r
+    /// </summary>\r
+    /// <remarks>\r
+    /// The class is a single ViewModel for all Preferences tabs. It can be broken in separate\r
+    /// ViewModels, one for each tab.\r
+    /// </remarks>\r
+    [Export, PartCreationPolicy(CreationPolicy.Shared)]\r
+    public class PreferencesViewModel : Screen\r
+    {\r
+        private readonly IEventAggregator _events;\r
+\r
+        //Logging in the Pithos client is provided by log4net\r
+        private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);\r
+\r
+        private PithosSettings _settings;\r
+        public PithosSettings Settings\r
+        {\r
+            get { return _settings; }\r
+            set\r
+            {\r
+                _settings = value;\r
+                NotifyOfPropertyChange(()=>Settings);\r
+            }\r
+        }\r
+\r
+        private ObservableConcurrentCollection<AccountViewModel> _accounts;\r
+        public ObservableConcurrentCollection<AccountViewModel> Accounts\r
+        {\r
+            get { return _accounts; }\r
+            set \r
+            { \r
+                _accounts = value;\r
+                NotifyOfPropertyChange(()=>Accounts);\r
+            }\r
+        }\r
+        \r
+        public bool StartOnSystemStartup { get; set; }\r
+\r
+        public ShellViewModel Shell { get;  set; }\r
+        //ShellExtensionController _extensionController=new ShellExtensionController();\r
+\r
+        public PreferencesViewModel(IWindowManager windowManager, IEventAggregator events, ShellViewModel shell, PithosSettings settings, string currentTab)\r
+        {\r
+            // ReSharper disable DoNotCallOverridableMethodsInConstructor\r
+            //Caliburn.Micro uses DisplayName for the view's title\r
+            DisplayName = "Pithos+ Preferences";\r
+            // ReSharper restore DoNotCallOverridableMethodsInConstructor\r
+\r
+            _windowManager = windowManager;\r
+            _events = events;\r
+\r
+            Shell = shell;\r
+            \r
+            Settings=settings;\r
+            Accounts = new ObservableConcurrentCollection<AccountViewModel>();\r
+            if (settings.Accounts == null)\r
+            {\r
+                settings.Accounts=new AccountsCollection();\r
+                settings.Save();\r
+            }\r
+            var accountVMs = from account in settings.Accounts\r
+                             select new AccountViewModel(account);\r
+\r
+            Accounts.AddFromEnumerable(accountVMs);\r
+            \r
+            var startupPath = Environment.GetFolderPath(Environment.SpecialFolder.Startup);\r
+            _shortcutPath = Path.Combine(startupPath, "Pithos.lnk");\r
+\r
+\r
+            StartOnSystemStartup = File.Exists(_shortcutPath);\r
+\r
+            SelectedTab = currentTab;\r
+        }\r
+\r
+        private string _selectedTab="General";\r
+        public string SelectedTab\r
+        {\r
+            get { return _selectedTab; }\r
+            set\r
+            {\r
+                _selectedTab = value??"General";\r
+                NotifyOfPropertyChange(()=>SelectedTab);\r
+                NotifyOfPropertyChange(() => AccountTabSelected);\r
+            }\r
+        }\r
+\r
+\r
+        public bool AccountTabSelected\r
+        {\r
+            get { return _selectedTab == "AccountTab"; }\r
+        }\r
+        #region Preferences Properties\r
+\r
+        private bool _noProxy;\r
+        public bool NoProxy\r
+        {\r
+            get { return _noProxy; }\r
+            set\r
+            {\r
+                _noProxy = value;\r
+                NotifyOfPropertyChange(()=>NoProxy);\r
+            }\r
+        }\r
+\r
+\r
+        private bool _defaultProxy;\r
+\r
+        public bool DefaultProxy\r
+        {\r
+            get { return _defaultProxy; }\r
+            set\r
+            {\r
+                _defaultProxy = value;\r
+                NotifyOfPropertyChange(() => DefaultProxy);\r
+            }\r
+        }\r
+\r
+\r
+        private bool _manualProxy;\r
+\r
+        public bool ManualProxy\r
+        {\r
+            get { return _manualProxy; }\r
+            set\r
+            {\r
+                _manualProxy = value;\r
+                NotifyOfPropertyChange(() => ManualProxy);\r
+            }\r
+        }\r
+        #endregion\r
+\r
+\r
+        public int StartupDelay\r
+        {\r
+            get { return (int) Settings.StartupDelay.TotalMinutes; }\r
+            set\r
+            {\r
+                if (value<0)\r
+                    throw new ArgumentOutOfRangeException("value",Resources.PreferencesViewModel_StartupDelay_Greater_or_equal_to_0);\r
+                Settings.StartupDelay = TimeSpan.FromMinutes(value);\r
+                NotifyOfPropertyChange(()=>StartupDelay);\r
+            }\r
+        }\r
+       \r
+        #region Commands\r
+        \r
+        public bool CanSelectiveSyncFolders\r
+        {\r
+            get { return CurrentAccount != null; }\r
+        }\r
+\r
+        public void SelectiveSyncFolders()\r
+        {\r
+            //var monitor = Shell.Monitors[CurrentAccount.AccountKey];\r
+            \r
+\r
+            var model = new SelectiveSynchViewModel(/*monitor,*/_events,CurrentAccount.Account,CurrentAccount.ApiKey);\r
+            if (_windowManager.ShowDialog(model) == true)\r
+            {\r
+                \r
+            }\r
+        }\r
+\r
+       /* private bool _networkTracing;\r
+        public bool NetworkTracing\r
+        {\r
+            get { return _networkTracing; }\r
+            set\r
+            {\r
+                _networkTracing = value;\r
+                \r
+            }\r
+        }*/\r
+\r
+        public void RefreshApiKey()\r
+        {\r
+            //_events.Publish(new Notification { Title = "Authorization failed", Message = "Your API Key has probably expired. You will be directed to a page where you can renew it", Level = TraceLevel.Error });\r
+            if (CurrentAccount == null)\r
+                return;\r
+            try\r
+            {\r
+\r
+                var name = CurrentAccount.AccountName;\r
+\r
+                var loginUri = new Uri(new Uri(CurrentAccount.ServerUrl), "login");\r
+                var credentials = PithosAccount.RetrieveCredentials(loginUri.ToString(),name);\r
+                if (credentials==null)\r
+                    return;\r
+                //The server will return credentials for a different account, not just the current account\r
+                //We need to find the correct account first\r
+                var account = Accounts.First(act => act.AccountName == credentials.UserName && act.ServerUrl == CurrentAccount.ServerUrl);\r
+                account.ApiKey = credentials.Password;                \r
+                account.IsExpired = false;\r
+                Settings.Save();\r
+                TaskEx.Delay(10000).ContinueWith(_ =>Shell.MonitorAccount(account.Account));\r
+                NotifyOfPropertyChange(() => Accounts);\r
+            }\r
+            catch (AggregateException exc)\r
+            {\r
+                string message = String.Format("API Key retrieval failed");\r
+                Log.Error(message, exc.InnerException);\r
+                _events.Publish(new Notification { Title = "Authorization failed", Message = message, Level = TraceLevel.Error });\r
+            }\r
+            catch (Exception exc)\r
+            {\r
+                string message = String.Format("API Key retrieval failed");\r
+                Log.Error(message, exc);\r
+                _events.Publish(new Notification { Title = "Authorization failed", Message = message, Level = TraceLevel.Error });\r
+            }\r
+\r
+        }\r
+\r
+    \r
+        public void OpenLogPath()\r
+        {\r
+            Shell.OpenLogPath();\r
+        }\r
+\r
+        public void OpenLogConsole()\r
+        {\r
+            var logView=IoC.Get<LogConsole.LogConsoleViewModel>();            \r
+            _windowManager.ShowWindow(logView);\r
+        }\r
+\r
+        public void SaveChanges()\r
+        {\r
+            DoSave();\r
+            TryClose(true);\r
+        }\r
+\r
+        public void RejectChanges()\r
+        {\r
+            Settings.Reload();\r
+            TryClose(false);\r
+        }\r
+\r
+        public void ApplyChanges()\r
+        {\r
+            DoSave();\r
+        }\r
+\r
+        private void DoSave()\r
+        {\r
+            //SetStartupMode();            \r
+\r
+            //Ensure we save the settings changes first\r
+            foreach (var account in _accountsToRemove)\r
+            {\r
+                Settings.Accounts.Remove(account);\r
+            }\r
+\r
+            foreach (var account in _accountsToAdd)\r
+            {\r
+                Settings.Accounts.Add(account);    \r
+            }\r
+\r
+            Settings.Save();\r
+\r
+\r
+            try\r
+            {\r
+                foreach (var account in _accountsToRemove)\r
+                {\r
+                    Shell.RemoveMonitor(account.ServerUrl, account.AccountName);\r
+                    Shell.RemoveAccountFromDatabase(account);\r
+                }\r
+\r
+                foreach (var account in Settings.Accounts)\r
+                {\r
+                    Shell.MonitorAccount(account);\r
+                }\r
+            }                \r
+            finally\r
+            {\r
+                _accountsToRemove.Clear();\r
+                _accountsToAdd.Clear();\r
+            }\r
+\r
+            NotifyOfPropertyChange(()=>Settings);\r
+\r
+            if (IgnoreCertificateErrors)\r
+                ServicePointManager.ServerCertificateValidationCallback= (sender,certificate,chain,errors)=> true;\r
+            else\r
+            {\r
+                ServicePointManager.ServerCertificateValidationCallback = null;\r
+            }\r
+        }\r
+\r
+     /*   public void ChangePithosFolder()\r
+        {\r
+            var browser = new FolderBrowserDialog();\r
+            browser.SelectedPath = Settings.PithosPath;\r
+            var result = browser.ShowDialog((IWin32Window)GetView());\r
+            if (result == DialogResult.OK)\r
+            {\r
+                var newPath = browser.SelectedPath;\r
+                var accountName = CurrentAccount.AccountName;\r
+                var monitor = Shell.Monitors[accountName];\r
+                monitor.Stop();\r
+                \r
+                Shell.Monitors.Remove(accountName);\r
+\r
+                Directory.Move(Settings.PithosPath, newPath);\r
+                Settings.PithosPath = newPath;\r
+                Settings.Save();\r
+\r
+                Shell.MonitorAccount(CurrentAccount);\r
+\r
+                NotifyOfPropertyChange(() => Settings);                \r
+            }\r
+        }\r
+*/\r
+\r
+\r
+        readonly List<AccountSettings> _accountsToAdd=new List<AccountSettings>();\r
+       public void AddAccount()\r
+       {\r
+           var wizard = new AddAccountViewModel();\r
+           if (_windowManager.ShowDialog(wizard) == true)\r
+           {\r
+               string selectedPath = wizard.AccountPath;\r
+               var initialRootPath = wizard.ShouldCreateOkeanosFolder?\r
+                   Path.Combine(selectedPath, "Okeanos")\r
+                   :selectedPath;\r
+               var actualRootPath= initialRootPath;\r
+               if (wizard.ShouldCreateOkeanosFolder)\r
+               {\r
+                   int attempt = 1;\r
+                   while (Directory.Exists(actualRootPath) || File.Exists(actualRootPath))\r
+                   {\r
+                       actualRootPath = String.Format("{0} {1}", initialRootPath, attempt++);\r
+                   }\r
+               }\r
+\r
+\r
+\r
+               var account = Accounts.FirstOrDefault(act => act.AccountName == wizard.AccountName && act.ServerUrl == wizard.CurrentServer);\r
+               if (account != null)\r
+               {\r
+                   if (MessageBox.Show("The account you specified already exists. Do you want to update it?", "The account exists") == MessageBoxResult.Yes)\r
+                   {\r
+                       account.ApiKey = wizard.Token;\r
+                       account.IsExpired = false;\r
+                       CurrentAccount = account;\r
+                   }\r
+               }\r
+               else\r
+               {\r
+                   var newAccount = new AccountSettings\r
+                                        {\r
+                                            AccountName = wizard.AccountName,\r
+                                            ServerUrl = wizard.CurrentServer,\r
+                                            ApiKey = wizard.Token,\r
+                                            RootPath = actualRootPath,\r
+                                            IsActive = wizard.IsAccountActive,\r
+                                        };\r
+\r
+\r
+                   var client = new CloudFilesClient(newAccount.AccountName, newAccount.ApiKey)\r
+                                    {\r
+                                        AuthenticationUrl = newAccount.ServerUrl, UsePithos = true\r
+                                    };\r
+                   client.Authenticate();\r
+\r
+\r
+                   var containers = client.ListContainers(newAccount.AccountName);\r
+                   var containerUris = from container in containers\r
+                                       select String.Format(@"{0}/v1/{1}/{2}",\r
+                                            newAccount.ServerUrl, newAccount.AccountName, container.Name);\r
+                   \r
+                   newAccount.SelectiveFolders.AddRange(containerUris.ToArray());\r
+\r
+                   var objectInfos = (from container in containers\r
+                                      from dir in client.ListObjects(newAccount.AccountName, container.Name)\r
+                                      where container.Name != "trash"\r
+                                      select dir).ToList();\r
+                   var tree = objectInfos.ToTree();\r
+\r
+                   var selected = (from root in tree \r
+                                   from child in root \r
+                                   select child.Uri.ToString()).ToArray();\r
+                   newAccount.SelectiveFolders.AddRange(selected);\r
+\r
+                   \r
+                   //TODO:Add the "pithos" container as a default selection                   \r
+\r
+                   _accountsToAdd.Add(newAccount);\r
+                   var accountVm = new AccountViewModel(newAccount);\r
+                   (Accounts as IProducerConsumerCollection<AccountViewModel>).TryAdd(accountVm);\r
+                   CurrentAccount = accountVm;\r
+               }\r
+               NotifyOfPropertyChange(() => Accounts);\r
+               NotifyOfPropertyChange(() => Settings);   \r
+           }\r
+\r
+\r
+            \r
+       }\r
+\r
+/*\r
+        public void AddPithosAccount()\r
+       {\r
+            var credentials=PithosAccount.RetrieveCredentials(null);\r
+            if (credentials == null)\r
+                return;\r
+            var account = Settings.Accounts.FirstOrDefault(act => act.AccountName == credentials.UserName);\r
+            var accountVM = new AccountViewModel(account);\r
+            if (account == null)\r
+            {\r
+                account=new AccountSettings{\r
+                    AccountName=credentials.UserName,\r
+                    ApiKey=credentials.Password\r
+                };\r
+                Settings.Accounts.Add(account);\r
+                accountVM = new AccountViewModel(account);\r
+                (Accounts as IProducerConsumerCollection<AccountViewModel>).TryAdd(accountVM);\r
+            }\r
+            else\r
+            {\r
+                account.ApiKey=credentials.Password;\r
+            }\r
+            //SelectedAccountIndex= Settings.Accounts.IndexOf(account);\r
+            CurrentAccount = accountVM;\r
+            NotifyOfPropertyChange(() => Accounts);\r
+            NotifyOfPropertyChange(()=>Settings);                       \r
+       }\r
+*/\r
+\r
+\r
+        readonly List<AccountSettings> _accountsToRemove = new List<AccountSettings>();\r
+        public void RemoveAccount()\r
+        {\r
+            Accounts.TryRemove(CurrentAccount);\r
+            _accountsToRemove.Add(CurrentAccount.Account);\r
+\r
+            CurrentAccount = null;\r
+            NotifyOfPropertyChange(() => Accounts);\r
+\r
+            \r
+            //NotifyOfPropertyChange("Settings.Accounts");\r
+        }\r
+\r
+        public bool CanRemoveAccount\r
+        {\r
+            get { return (CurrentAccount != null); }\r
+        }\r
+\r
+        public bool CanClearAccountCache\r
+        {\r
+            get { return (CurrentAccount != null); }\r
+        }\r
+\r
+        public void ClearAccountCache()\r
+        {\r
+            if (MessageBoxResult.Yes == MessageBox.Show("You are about to delete all partially downloaded files from the account's cache.\n" +\r
+                            " You will have to download all partially downloaded data again\n" +\r
+                            "This change can not be undone\n\n" +\r
+            "Do you wish to delete all partially downloaded data?", "Warning! Clearing account cache",\r
+                            MessageBoxButton.YesNo,MessageBoxImage.Question,MessageBoxResult.No))\r
+            {\r
+\r
+                var cachePath = Path.Combine(CurrentAccount.RootPath, FolderConstants.CacheFolder);\r
+                var dir = new DirectoryInfo(cachePath);\r
+                //The file may not exist if we just created the account\r
+                if (!dir.Exists)\r
+                    return;\r
+                dir.EnumerateFiles().Apply(file=>file.Delete());\r
+                dir.EnumerateDirectories().Apply(folder => folder.Delete(true));\r
+            }\r
+        }\r
+\r
+\r
+        public bool ExtensionsActivated\r
+        {\r
+            get { return Settings.ExtensionsActivated; }\r
+            set\r
+            {\r
+                if (Settings.ExtensionsActivated == value)\r
+                    return;\r
+\r
+                Settings.ExtensionsActivated = value;\r
+\r
+/*\r
+                if (value)\r
+                    _extensionController.RegisterExtensions();\r
+                else\r
+                {\r
+                    _extensionController.UnregisterExtensions();\r
+                }\r
+*/\r
+                NotifyOfPropertyChange(() => ExtensionsActivated);\r
+            }\r
+        }\r
+\r
+        public bool DebugLoggingEnabled\r
+        {\r
+            get { return Settings.DebugLoggingEnabled; }\r
+            set { \r
+                Settings.DebugLoggingEnabled = value;\r
+                NotifyOfPropertyChange(()=>DebugLoggingEnabled);\r
+            }\r
+        }\r
+\r
+        public bool IgnoreCertificateErrors\r
+        {\r
+            get { return Settings.IgnoreCertificateErrors; }\r
+            set {\r
+                Settings.IgnoreCertificateErrors = value;\r
+                NotifyOfPropertyChange(() => IgnoreCertificateErrors);\r
+            }\r
+        }\r
+       \r
+        #endregion\r
+\r
+       /* private int _selectedAccountIndex;\r
+        public int SelectedAccountIndex\r
+        {\r
+            get { return _selectedAccountIndex; }\r
+            set\r
+            {\r
+                //var accountCount=Settings.Accounts.Count;\r
+                //if (accountCount == 0)\r
+                //    return;\r
+                //if (0 <= value && value < accountCount)\r
+                //    _selectedAccountIndex = value;\r
+                //else\r
+                //    _selectedAccountIndex = 0;\r
+                _selectedAccountIndex = value;\r
+                NotifyOfPropertyChange(() => CurrentAccount);\r
+                NotifyOfPropertyChange(() => CanRemoveAccount);\r
+                NotifyOfPropertyChange(()=>SelectedAccountIndex);\r
+            }\r
+        }*/\r
+\r
+        private AccountViewModel _currentAccount;\r
+        private readonly IWindowManager _windowManager;\r
+        private readonly string _shortcutPath;\r
+\r
+\r
+        \r
+        public AccountViewModel CurrentAccount\r
+        {\r
+            get { return _currentAccount; }\r
+            set\r
+            {\r
+                _currentAccount = value;\r
+                NotifyOfPropertyChange(()=>CurrentAccount);\r
+                NotifyOfPropertyChange(() => CanRemoveAccount);\r
+                NotifyOfPropertyChange(() => CanSelectiveSyncFolders);\r
+                NotifyOfPropertyChange(() => CanMoveAccountFolder);\r
+                NotifyOfPropertyChange(() => CanClearAccountCache);\r
+            }\r
+        }\r
+\r
+/*\r
+        public AccountSettings CurrentAccount\r
+        {\r
+            get {\r
+                if (0 <= SelectedAccountIndex && SelectedAccountIndex < Settings.Accounts.Count)                    \r
+                    return Settings.Accounts[SelectedAccountIndex];\r
+                return null;\r
+            }\r
+\r
+        }\r
+*/\r
+\r
+\r
+        public bool CanMoveAccountFolder\r
+        {\r
+            get { return CurrentAccount != null; }\r
+        }\r
+\r
+    public void MoveAccountFolder()\r
+    {\r
+\r
+        using (var dlg = new FolderBrowserDialog())\r
+        {\r
+            var currentFolder = CurrentAccount.RootPath;\r
+            dlg.SelectedPath = currentFolder;\r
+            //Ask the user to select a folder\r
+            //Note: We need a parent window here, which we retrieve with GetView            \r
+            var view = (Window)GetView();            \r
+            if (DialogResult.OK != dlg.ShowDialog(new Wpf32Window(view)))\r
+                return;            \r
+\r
+            var newPath= dlg.SelectedPath;                \r
+            //Find the account's monitor and stop it\r
+            PithosMonitor monitor;\r
+            if (Shell.Monitors.TryGetValue(CurrentAccount.AccountKey, out monitor))\r
+            {\r
+                monitor.Stop();\r
+\r
+\r
+                var oldPath = monitor.RootPath;\r
+                //The old directory may not exist eg. if we create an account for the first time\r
+                if (Directory.Exists(oldPath))\r
+                {\r
+                    //If it does, do the move\r
+\r
+                    //Now Create all of the directories\r
+                    foreach (string dirPath in Directory.EnumerateDirectories(oldPath, "*",\r
+                                                           SearchOption.AllDirectories))\r
+                        Directory.CreateDirectory(dirPath.Replace(oldPath, newPath));\r
+\r
+                    //Copy all the files\r
+                    foreach (string newFilePath in Directory.EnumerateFiles(oldPath, "*.*",\r
+                                                                            SearchOption.AllDirectories))\r
+                        File.Copy(newFilePath, newFilePath.Replace(oldPath, newPath));\r
+\r
+                    Log.InfoFormat("Deleting account folder {0}",oldPath);\r
+                    Directory.Delete(oldPath, true);\r
+\r
+                    //We also need to change the path of the existing file states\r
+                    monitor.MoveFileStates(oldPath, newPath);\r
+                }\r
+            }\r
+            //Replace the old rootpath with the new\r
+            CurrentAccount.RootPath = newPath;\r
+            //TODO: This will save all settings changes. Too coarse grained, need to fix at a later date\r
+            Settings.Save();            \r
+            //And start the monitor on the new RootPath            \r
+            if (monitor != null)\r
+            {\r
+                monitor.RootPath = newPath;\r
+                if (CurrentAccount.IsActive)\r
+                    monitor.Start();\r
+            }\r
+            else\r
+                Shell.MonitorAccount(CurrentAccount.Account);\r
+            //Finally, notify that the Settings, CurrentAccount have changed\r
+            NotifyOfPropertyChange(() => CurrentAccount);\r
+            NotifyOfPropertyChange(() => Settings);\r
+\r
+        }\r
+    }\r
+    }\r
+}\r