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 // -----------------------------------------------------------------------
46 using System.Collections.Concurrent;
47 using System.ComponentModel.Composition;
48 using System.Diagnostics;
51 using System.Threading.Tasks;
53 using System.Windows.Forms;
55 using Pithos.Client.WPF.Configuration;
56 using Pithos.Client.WPF.SelectiveSynch;
58 using Pithos.Interfaces;
61 using Screen = Caliburn.Micro.Screen;
63 namespace Pithos.Client.WPF.Preferences
66 /// TODO: Update summary.
69 public class PreferencesViewModel : Screen
71 private IEventAggregator _events;
73 //Logging in the Pithos client is provided by log4net
74 private static readonly log4net.ILog Log = log4net.LogManager.GetLogger("Pithos");
76 private PithosSettings _settings;
77 public PithosSettings Settings
79 get { return _settings; }
83 NotifyOfPropertyChange(()=>Settings);
87 private ObservableConcurrentCollection<AccountViewModel> _accounts;
88 public ObservableConcurrentCollection<AccountViewModel> Accounts
90 get { return _accounts; }
94 NotifyOfPropertyChange(()=>Accounts);
98 public bool StartOnSystemStartup { get; set; }
100 public ShellViewModel Shell { get; set; }
101 //ShellExtensionController _extensionController=new ShellExtensionController();
103 public PreferencesViewModel(IWindowManager windowManager, IEventAggregator events, ShellViewModel shell, PithosSettings settings, string currentTab)
105 _windowManager = windowManager;
108 DisplayName = "Pithos Preferences";
112 Accounts = new ObservableConcurrentCollection<AccountViewModel>();
113 if (settings.Accounts == null)
115 settings.Accounts=new AccountsCollection();
118 var accountVMs = from account in settings.Accounts
119 select new AccountViewModel(account);
121 Accounts.AddFromEnumerable(accountVMs);
123 var startupPath = Environment.GetFolderPath(Environment.SpecialFolder.Startup);
124 _shortcutPath = Path.Combine(startupPath, "Pithos.lnk");
127 StartOnSystemStartup = File.Exists(_shortcutPath);
129 SelectedTab = currentTab;
132 private string _selectedTab="General";
133 public string SelectedTab
135 get { return _selectedTab; }
138 _selectedTab = value??"General";
139 NotifyOfPropertyChange(()=>SelectedTab);
140 NotifyOfPropertyChange(() => AccountTabSelected);
145 public bool AccountTabSelected
147 get { return _selectedTab == "AccountTab"; }
149 #region Preferences Properties
151 private bool _noProxy;
154 get { return _noProxy; }
158 NotifyOfPropertyChange(()=>NoProxy);
163 private bool _defaultProxy;
165 public bool DefaultProxy
167 get { return _defaultProxy; }
170 _defaultProxy = value;
171 NotifyOfPropertyChange(() => DefaultProxy);
176 private bool _manualProxy;
178 public bool ManualProxy
180 get { return _manualProxy; }
183 _manualProxy = value;
184 NotifyOfPropertyChange(() => ManualProxy);
190 public int StartupDelay
192 get { return (int) Settings.StartupDelay.TotalMinutes; }
196 throw new ArgumentOutOfRangeException("value","The Startup Delay must be greater or equal to 0");
197 Settings.StartupDelay = TimeSpan.FromMinutes(value);
198 NotifyOfPropertyChange(()=>StartupDelay);
204 public bool CanSelectiveSyncFolders
206 get { return CurrentAccount != null; }
209 public void SelectiveSyncFolders()
211 var monitor = Shell.Monitors[CurrentAccount.AccountName];
214 var model = new SelectiveSynchViewModel(monitor,_events,CurrentAccount.Account);
215 if (_windowManager.ShowDialog(model) == true)
221 public async Task RefreshApiKey()
223 _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 });
225 //var userName = CurrentAccount.AccountName;
229 var credentials = await PithosAccount.RetrieveCredentials(Settings.PithosLoginUrl);
231 var account = Accounts.First(act => act.AccountName == credentials.UserName);
232 //The server may return credentials for a different account
233 var monitor = Shell.Monitors[account.AccountName];
234 account.ApiKey = credentials.Password;
235 monitor.ApiKey = credentials.Password;
236 account.IsExpired = false;
238 TaskEx.Delay(10000).ContinueWith(_ =>Shell.MonitorAccount(account.Account));
239 NotifyOfPropertyChange(() => Accounts);
241 catch (AggregateException exc)
243 string message = String.Format("API Key retrieval failed");
244 Log.Error(message, exc.InnerException);
245 _events.Publish(new Notification { Title = "Authorization failed", Message = message, Level = TraceLevel.Error });
247 catch (Exception exc)
249 string message = String.Format("API Key retrieval failed");
250 Log.Error(message, exc);
251 _events.Publish(new Notification { Title = "Authorization failed", Message = message, Level = TraceLevel.Error });
257 public void SaveChanges()
263 public void RejectChanges()
269 public void ApplyChanges()
274 private void DoSave()
279 foreach (var account in Settings.Accounts)
281 Shell.MonitorAccount(account);
284 NotifyOfPropertyChange(()=>Settings);
287 /* public void ChangePithosFolder()
289 var browser = new FolderBrowserDialog();
290 browser.SelectedPath = Settings.PithosPath;
291 var result = browser.ShowDialog((IWin32Window)GetView());
292 if (result == DialogResult.OK)
294 var newPath = browser.SelectedPath;
295 var accountName = CurrentAccount.AccountName;
296 var monitor = Shell.Monitors[accountName];
299 Shell.Monitors.Remove(accountName);
301 Directory.Move(Settings.PithosPath, newPath);
302 Settings.PithosPath = newPath;
305 Shell.MonitorAccount(CurrentAccount);
307 NotifyOfPropertyChange(() => Settings);
314 public void AddAccount()
316 var wizard = new AddAccountViewModel();
317 if (_windowManager.ShowDialog(wizard) == true)
319 string selectedPath = wizard.AccountPath;
320 var initialRootPath = Path.Combine(selectedPath, "Okeanos");
321 var actualRootPath= initialRootPath;
323 while (Directory.Exists(actualRootPath) || File.Exists(actualRootPath))
325 actualRootPath = String.Format("{0} {1}", initialRootPath,attempt++);
328 var newAccount = new AccountSettings
330 AccountName = wizard.AccountName,
331 ServerUrl=wizard.CurrentServer,
333 RootPath = actualRootPath,
334 IsActive=wizard.IsAccountActive
336 Settings.Accounts.Add(newAccount);
337 var accountVM = new AccountViewModel(newAccount);
338 (Accounts as IProducerConsumerCollection<AccountViewModel>).TryAdd(accountVM);
339 CurrentAccount = accountVM;
340 NotifyOfPropertyChange(() => Accounts);
341 NotifyOfPropertyChange(() => Settings);
348 public async void AddPithosAccount()
350 var credentials=await PithosAccount.RetrieveCredentials(Settings.PithosLoginUrl);
351 var account = Settings.Accounts.FirstOrDefault(act => act.AccountName == credentials.UserName);
352 var accountVM = new AccountViewModel(account);
355 account=new AccountSettings{
356 AccountName=credentials.UserName,
357 ApiKey=credentials.Password
359 Settings.Accounts.Add(account);
360 accountVM = new AccountViewModel(account);
361 (Accounts as IProducerConsumerCollection<AccountViewModel>).TryAdd(accountVM);
365 account.ApiKey=credentials.Password;
367 //SelectedAccountIndex= Settings.Accounts.IndexOf(account);
368 CurrentAccount = accountVM;
369 NotifyOfPropertyChange(() => Accounts);
370 NotifyOfPropertyChange(()=>Settings);
373 public void RemoveAccount()
375 var accountName = CurrentAccount.AccountName;
376 Settings.Accounts.Remove(CurrentAccount.Account);
378 Accounts.TryRemove(CurrentAccount);
381 CurrentAccount = null;
382 //Accounts = Settings.Accounts;
384 Shell.RemoveMonitor(accountName);
385 NotifyOfPropertyChange(() => Accounts);
386 NotifyOfPropertyChange(() => Settings);
388 //NotifyOfPropertyChange("Settings.Accounts");
391 public bool CanRemoveAccount
393 get { return (CurrentAccount != null); }
398 public bool ExtensionsActivated
400 get { return Settings.ExtensionsActivated; }
403 if (Settings.ExtensionsActivated == value)
406 Settings.ExtensionsActivated = value;
410 _extensionController.RegisterExtensions();
413 _extensionController.UnregisterExtensions();
416 NotifyOfPropertyChange(() => ExtensionsActivated);
423 /* private int _selectedAccountIndex;
424 public int SelectedAccountIndex
426 get { return _selectedAccountIndex; }
429 //var accountCount=Settings.Accounts.Count;
430 //if (accountCount == 0)
432 //if (0 <= value && value < accountCount)
433 // _selectedAccountIndex = value;
435 // _selectedAccountIndex = 0;
436 _selectedAccountIndex = value;
437 NotifyOfPropertyChange(() => CurrentAccount);
438 NotifyOfPropertyChange(() => CanRemoveAccount);
439 NotifyOfPropertyChange(()=>SelectedAccountIndex);
443 private AccountViewModel _currentAccount;
444 private IWindowManager _windowManager;
445 private string _shortcutPath;
449 public AccountViewModel CurrentAccount
451 get { return _currentAccount; }
454 _currentAccount = value;
455 NotifyOfPropertyChange(()=>CurrentAccount);
456 NotifyOfPropertyChange(() => CanRemoveAccount);
457 NotifyOfPropertyChange(() => CanSelectiveSyncFolders);
458 NotifyOfPropertyChange(() => CanMoveAccountFolder);
463 public AccountSettings CurrentAccount
466 if (0 <= SelectedAccountIndex && SelectedAccountIndex < Settings.Accounts.Count)
467 return Settings.Accounts[SelectedAccountIndex];
475 public bool CanMoveAccountFolder
477 get { return CurrentAccount != null; }
480 public void MoveAccountFolder()
483 using (var dlg = new FolderBrowserDialog())
485 var currentFolder = CurrentAccount.RootPath;
486 dlg.SelectedPath = currentFolder;
487 //Ask the user to select a folder
488 //Note: We need a parent window here, which we retrieve with GetView
489 var view = (Window)GetView();
490 if (DialogResult.OK != dlg.ShowDialog(new Wpf32Window(view)))
493 var newPath= dlg.SelectedPath;
494 //Find the account's monitor and stop it
495 PithosMonitor monitor;
496 if (Shell.Monitors.TryGetValue(CurrentAccount.AccountName, out monitor))
501 var oldPath = monitor.RootPath;
502 //The old directory may not exist eg. if we create an account for the first time
503 if (Directory.Exists(oldPath))
505 //If it does, do the move
507 //Now Create all of the directories
508 foreach (string dirPath in Directory.EnumerateDirectories(oldPath, "*",
509 SearchOption.AllDirectories))
510 Directory.CreateDirectory(dirPath.Replace(oldPath, newPath));
513 foreach (string newFilePath in Directory.EnumerateFiles(oldPath, "*.*",
514 SearchOption.AllDirectories))
515 File.Copy(newFilePath, newFilePath.Replace(oldPath, newPath));
517 Directory.Delete(oldPath, true);
519 //We also need to change the path of the existing file states
521 monitor.MoveFileStates(oldPath, newPath);
524 //Replace the old rootpath with the new
525 CurrentAccount.RootPath = newPath;
526 //TODO: This will save all settings changes. Too coarse grained, need to fix at a later date
528 //And start the monitor on the new RootPath
531 monitor.RootPath = newPath;
532 if (CurrentAccount.IsActive)
536 Shell.MonitorAccount(CurrentAccount.Account);
537 //Finally, notify that the Settings, CurrentAccount have changed
538 NotifyOfPropertyChange(() => CurrentAccount);
539 NotifyOfPropertyChange(() => Settings);