2 /* -----------------------------------------------------------------------
3 * <copyright file="PreferencesViewModel.cs" company="GRNet">
5 * Copyright 2011-2012 GRNET S.A. All rights reserved.
7 * Redistribution and use in source and binary forms, with or
8 * without modification, are permitted provided that the following
11 * 1. Redistributions of source code must retain the above
12 * copyright notice, this list of conditions and the following
15 * 2. Redistributions in binary form must reproduce the above
16 * copyright notice, this list of conditions and the following
17 * disclaimer in the documentation and/or other materials
18 * provided with the distribution.
21 * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
22 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
25 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
28 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
34 * The views and conclusions contained in the software and
35 * documentation are those of the authors and should not be
36 * interpreted as representing official policies, either expressed
37 * or implied, of GRNET S.A.
39 * -----------------------------------------------------------------------
44 using System.Collections.Concurrent;
45 using System.Collections.Generic;
46 using System.ComponentModel.Composition;
47 using System.Diagnostics;
49 using System.Reflection;
50 using System.Threading.Tasks;
52 using System.Windows.Forms;
54 using Pithos.Client.WPF.Configuration;
55 using Pithos.Client.WPF.Properties;
56 using Pithos.Client.WPF.SelectiveSynch;
58 using Pithos.Interfaces;
61 using Screen = Caliburn.Micro.Screen;
63 namespace Pithos.Client.WPF.Preferences
66 /// The preferences screen displays user and account settings and updates the PithosMonitor
67 /// classes when account settings change.
70 /// The class is a single ViewModel for all Preferences tabs. It can be broken in separate
71 /// ViewModels, one for each tab.
74 public class PreferencesViewModel : Screen
76 private readonly IEventAggregator _events;
78 //Logging in the Pithos client is provided by log4net
79 private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
81 private PithosSettings _settings;
82 public PithosSettings Settings
84 get { return _settings; }
88 NotifyOfPropertyChange(()=>Settings);
92 private ObservableConcurrentCollection<AccountViewModel> _accounts;
93 public ObservableConcurrentCollection<AccountViewModel> Accounts
95 get { return _accounts; }
99 NotifyOfPropertyChange(()=>Accounts);
103 public bool StartOnSystemStartup { get; set; }
105 public ShellViewModel Shell { get; set; }
106 //ShellExtensionController _extensionController=new ShellExtensionController();
108 public PreferencesViewModel(IWindowManager windowManager, IEventAggregator events, ShellViewModel shell, PithosSettings settings, string currentTab)
110 // ReSharper disable DoNotCallOverridableMethodsInConstructor
111 //Caliburn.Micro uses DisplayName for the view's title
112 DisplayName = "Pithos+ Preferences";
113 // ReSharper restore DoNotCallOverridableMethodsInConstructor
115 _windowManager = windowManager;
121 Accounts = new ObservableConcurrentCollection<AccountViewModel>();
122 if (settings.Accounts == null)
124 settings.Accounts=new AccountsCollection();
127 var accountVMs = from account in settings.Accounts
128 select new AccountViewModel(account);
130 Accounts.AddFromEnumerable(accountVMs);
132 var startupPath = Environment.GetFolderPath(Environment.SpecialFolder.Startup);
133 _shortcutPath = Path.Combine(startupPath, "Pithos.lnk");
136 StartOnSystemStartup = File.Exists(_shortcutPath);
138 SelectedTab = currentTab;
141 private string _selectedTab="General";
142 public string SelectedTab
144 get { return _selectedTab; }
147 _selectedTab = value??"General";
148 NotifyOfPropertyChange(()=>SelectedTab);
149 NotifyOfPropertyChange(() => AccountTabSelected);
154 public bool AccountTabSelected
156 get { return _selectedTab == "AccountTab"; }
158 #region Preferences Properties
160 private bool _noProxy;
163 get { return _noProxy; }
167 NotifyOfPropertyChange(()=>NoProxy);
172 private bool _defaultProxy;
174 public bool DefaultProxy
176 get { return _defaultProxy; }
179 _defaultProxy = value;
180 NotifyOfPropertyChange(() => DefaultProxy);
185 private bool _manualProxy;
187 public bool ManualProxy
189 get { return _manualProxy; }
192 _manualProxy = value;
193 NotifyOfPropertyChange(() => ManualProxy);
199 public int StartupDelay
201 get { return (int) Settings.StartupDelay.TotalMinutes; }
205 throw new ArgumentOutOfRangeException("value",Resources.PreferencesViewModel_StartupDelay_Greater_or_equal_to_0);
206 Settings.StartupDelay = TimeSpan.FromMinutes(value);
207 NotifyOfPropertyChange(()=>StartupDelay);
213 public bool CanSelectiveSyncFolders
215 get { return CurrentAccount != null; }
218 public void SelectiveSyncFolders()
220 var monitor = Shell.Monitors[CurrentAccount.AccountName];
223 var model = new SelectiveSynchViewModel(monitor,_events,CurrentAccount.Account);
224 if (_windowManager.ShowDialog(model) == true)
230 public void RefreshApiKey()
232 //_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 });
233 if (CurrentAccount == null)
238 var name = CurrentAccount.AccountName;
240 var loginUri = new Uri(new Uri(CurrentAccount.ServerUrl), "login");
241 var credentials = PithosAccount.RetrieveCredentials(loginUri.ToString(),name);
242 if (credentials==null)
244 //The server will return credentials for a different account, not just the current account
245 //We need to find the correct account first
246 var account = Accounts.First(act => act.AccountName == credentials.UserName && act.ServerUrl == ?? );
247 account.ApiKey = credentials.Password;
248 account.IsExpired = false;
250 TaskEx.Delay(10000).ContinueWith(_ =>Shell.MonitorAccount(account.Account));
251 NotifyOfPropertyChange(() => Accounts);
253 catch (AggregateException exc)
255 string message = String.Format("API Key retrieval failed");
256 Log.Error(message, exc.InnerException);
257 _events.Publish(new Notification { Title = "Authorization failed", Message = message, Level = TraceLevel.Error });
259 catch (Exception exc)
261 string message = String.Format("API Key retrieval failed");
262 Log.Error(message, exc);
263 _events.Publish(new Notification { Title = "Authorization failed", Message = message, Level = TraceLevel.Error });
269 public void OpenLogPath()
274 public void OpenLogConsole()
276 var logView=IoC.Get<LogConsole.LogConsoleViewModel>();
277 _windowManager.ShowWindow(logView);
280 public void SaveChanges()
286 public void RejectChanges()
292 public void ApplyChanges()
297 private void DoSave()
301 //Ensure we save the settings changes first
302 foreach (var account in _accountsToRemove)
304 Settings.Accounts.Remove(account);
307 foreach (var account in _accountsToAdd)
309 Settings.Accounts.Add(account);
317 foreach (var account in _accountsToRemove)
319 Shell.RemoveMonitor(account.AccountName);
320 Shell.RemoveAccountFromDatabase(account);
323 foreach (var account in Settings.Accounts)
325 Shell.MonitorAccount(account);
330 _accountsToRemove.Clear();
331 _accountsToAdd.Clear();
334 NotifyOfPropertyChange(()=>Settings);
337 /* public void ChangePithosFolder()
339 var browser = new FolderBrowserDialog();
340 browser.SelectedPath = Settings.PithosPath;
341 var result = browser.ShowDialog((IWin32Window)GetView());
342 if (result == DialogResult.OK)
344 var newPath = browser.SelectedPath;
345 var accountName = CurrentAccount.AccountName;
346 var monitor = Shell.Monitors[accountName];
349 Shell.Monitors.Remove(accountName);
351 Directory.Move(Settings.PithosPath, newPath);
352 Settings.PithosPath = newPath;
355 Shell.MonitorAccount(CurrentAccount);
357 NotifyOfPropertyChange(() => Settings);
363 readonly List<AccountSettings> _accountsToAdd=new List<AccountSettings>();
364 public void AddAccount()
366 var wizard = new AddAccountViewModel();
367 if (_windowManager.ShowDialog(wizard) == true)
369 string selectedPath = wizard.AccountPath;
370 var initialRootPath = wizard.ShouldCreateOkeanosFolder?
371 Path.Combine(selectedPath, "Okeanos")
373 var actualRootPath= initialRootPath;
374 if (wizard.ShouldCreateOkeanosFolder)
377 while (Directory.Exists(actualRootPath) || File.Exists(actualRootPath))
379 actualRootPath = String.Format("{0} {1}", initialRootPath, attempt++);
382 ### Check that the account does not already exist
384 var newAccount = new AccountSettings
386 AccountName = wizard.AccountName,
387 ServerUrl=wizard.CurrentServer,
389 RootPath = actualRootPath,
390 IsActive=wizard.IsAccountActive
392 _accountsToAdd.Add(newAccount);
393 var accountVm = new AccountViewModel(newAccount);
394 (Accounts as IProducerConsumerCollection<AccountViewModel>).TryAdd(accountVm);
395 CurrentAccount = accountVm;
396 NotifyOfPropertyChange(() => Accounts);
397 NotifyOfPropertyChange(() => Settings);
405 public void AddPithosAccount()
407 var credentials=PithosAccount.RetrieveCredentials(null);
408 if (credentials == null)
410 var account = Settings.Accounts.FirstOrDefault(act => act.AccountName == credentials.UserName);
411 var accountVM = new AccountViewModel(account);
414 account=new AccountSettings{
415 AccountName=credentials.UserName,
416 ApiKey=credentials.Password
418 Settings.Accounts.Add(account);
419 accountVM = new AccountViewModel(account);
420 (Accounts as IProducerConsumerCollection<AccountViewModel>).TryAdd(accountVM);
424 account.ApiKey=credentials.Password;
426 //SelectedAccountIndex= Settings.Accounts.IndexOf(account);
427 CurrentAccount = accountVM;
428 NotifyOfPropertyChange(() => Accounts);
429 NotifyOfPropertyChange(()=>Settings);
434 readonly List<AccountSettings> _accountsToRemove = new List<AccountSettings>();
435 public void RemoveAccount()
437 Accounts.TryRemove(CurrentAccount);
438 _accountsToRemove.Add(CurrentAccount.Account);
440 CurrentAccount = null;
441 NotifyOfPropertyChange(() => Accounts);
444 //NotifyOfPropertyChange("Settings.Accounts");
447 public bool CanRemoveAccount
449 get { return (CurrentAccount != null); }
454 public bool ExtensionsActivated
456 get { return Settings.ExtensionsActivated; }
459 if (Settings.ExtensionsActivated == value)
462 Settings.ExtensionsActivated = value;
466 _extensionController.RegisterExtensions();
469 _extensionController.UnregisterExtensions();
472 NotifyOfPropertyChange(() => ExtensionsActivated);
476 public bool DebugLoggingEnabled
478 get { return Settings.DebugLoggingEnabled; }
480 Settings.DebugLoggingEnabled = value;
481 NotifyOfPropertyChange(()=>DebugLoggingEnabled);
487 /* private int _selectedAccountIndex;
488 public int SelectedAccountIndex
490 get { return _selectedAccountIndex; }
493 //var accountCount=Settings.Accounts.Count;
494 //if (accountCount == 0)
496 //if (0 <= value && value < accountCount)
497 // _selectedAccountIndex = value;
499 // _selectedAccountIndex = 0;
500 _selectedAccountIndex = value;
501 NotifyOfPropertyChange(() => CurrentAccount);
502 NotifyOfPropertyChange(() => CanRemoveAccount);
503 NotifyOfPropertyChange(()=>SelectedAccountIndex);
507 private AccountViewModel _currentAccount;
508 private readonly IWindowManager _windowManager;
509 private readonly string _shortcutPath;
513 public AccountViewModel CurrentAccount
515 get { return _currentAccount; }
518 _currentAccount = value;
519 NotifyOfPropertyChange(()=>CurrentAccount);
520 NotifyOfPropertyChange(() => CanRemoveAccount);
521 NotifyOfPropertyChange(() => CanSelectiveSyncFolders);
522 NotifyOfPropertyChange(() => CanMoveAccountFolder);
527 public AccountSettings CurrentAccount
530 if (0 <= SelectedAccountIndex && SelectedAccountIndex < Settings.Accounts.Count)
531 return Settings.Accounts[SelectedAccountIndex];
539 public bool CanMoveAccountFolder
541 get { return CurrentAccount != null; }
544 public void MoveAccountFolder()
547 using (var dlg = new FolderBrowserDialog())
549 var currentFolder = CurrentAccount.RootPath;
550 dlg.SelectedPath = currentFolder;
551 //Ask the user to select a folder
552 //Note: We need a parent window here, which we retrieve with GetView
553 var view = (Window)GetView();
554 if (DialogResult.OK != dlg.ShowDialog(new Wpf32Window(view)))
557 var newPath= dlg.SelectedPath;
558 //Find the account's monitor and stop it
559 PithosMonitor monitor;
560 if (Shell.Monitors.TryGetValue(CurrentAccount.AccountName, out monitor))
565 var oldPath = monitor.RootPath;
566 //The old directory may not exist eg. if we create an account for the first time
567 if (Directory.Exists(oldPath))
569 //If it does, do the move
571 //Now Create all of the directories
572 foreach (string dirPath in Directory.EnumerateDirectories(oldPath, "*",
573 SearchOption.AllDirectories))
574 Directory.CreateDirectory(dirPath.Replace(oldPath, newPath));
577 foreach (string newFilePath in Directory.EnumerateFiles(oldPath, "*.*",
578 SearchOption.AllDirectories))
579 File.Copy(newFilePath, newFilePath.Replace(oldPath, newPath));
581 Log.InfoFormat("Deleting account folder {0}",oldPath);
582 Directory.Delete(oldPath, true);
584 //We also need to change the path of the existing file states
585 monitor.MoveFileStates(oldPath, newPath);
588 //Replace the old rootpath with the new
589 CurrentAccount.RootPath = newPath;
590 //TODO: This will save all settings changes. Too coarse grained, need to fix at a later date
592 //And start the monitor on the new RootPath
595 monitor.RootPath = newPath;
596 if (CurrentAccount.IsActive)
600 Shell.MonitorAccount(CurrentAccount.Account);
601 //Finally, notify that the Settings, CurrentAccount have changed
602 NotifyOfPropertyChange(() => CurrentAccount);
603 NotifyOfPropertyChange(() => Settings);