Statistics
| Branch: | Revision:

root / trunk / Pithos.Client.WPF / Preferences / PreferencesViewModel.cs @ e394ef0f

History | View | Annotate | Download (21.6 kB)

1
#region
2
/* -----------------------------------------------------------------------
3
 * <copyright file="PreferencesViewModel.cs" company="GRNet">
4
 * 
5
 * Copyright 2011-2012 GRNET S.A. All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or
8
 * without modification, are permitted provided that the following
9
 * conditions are met:
10
 *
11
 *   1. Redistributions of source code must retain the above
12
 *      copyright notice, this list of conditions and the following
13
 *      disclaimer.
14
 *
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.
19
 *
20
 *
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.
33
 *
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.
38
 * </copyright>
39
 * -----------------------------------------------------------------------
40
 */
41
#endregion
42

    
43

    
44
using System.Collections.Concurrent;
45
using System.Collections.Generic;
46
using System.ComponentModel.Composition;
47
using System.Diagnostics;
48
using System.IO;
49
using System.Net;
50
using System.Reflection;
51
using System.Threading.Tasks;
52
using System.Windows;
53
using System.Windows.Forms;
54
using Caliburn.Micro;
55
using Pithos.Client.WPF.Configuration;
56
using Pithos.Client.WPF.Properties;
57
using Pithos.Client.WPF.SelectiveSynch;
58
using Pithos.Core;
59
using Pithos.Interfaces;
60
using System;
61
using System.Linq;
62
using MessageBox = System.Windows.MessageBox;
63
using Screen = Caliburn.Micro.Screen;
64

    
65
namespace Pithos.Client.WPF.Preferences
66
{
67
    /// <summary>
68
    /// The preferences screen displays user and account settings and updates the PithosMonitor
69
    /// classes when account settings change.
70
    /// </summary>
71
    /// <remarks>
72
    /// The class is a single ViewModel for all Preferences tabs. It can be broken in separate
73
    /// ViewModels, one for each tab.
74
    /// </remarks>
75
    [Export]
76
    public class PreferencesViewModel : Screen
77
    {
78
        private readonly IEventAggregator _events;
79

    
80
        //Logging in the Pithos client is provided by log4net
81
        private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
82

    
83
        private PithosSettings _settings;
84
        public PithosSettings Settings
85
        {
86
            get { return _settings; }
87
            set
88
            {
89
                _settings = value;
90
                NotifyOfPropertyChange(()=>Settings);
91
            }
92
        }
93

    
94
        private ObservableConcurrentCollection<AccountViewModel> _accounts;
95
        public ObservableConcurrentCollection<AccountViewModel> Accounts
96
        {
97
            get { return _accounts; }
98
            set 
99
            { 
100
                _accounts = value;
101
                NotifyOfPropertyChange(()=>Accounts);
102
            }
103
        }
104
        
105
        public bool StartOnSystemStartup { get; set; }
106

    
107
        public ShellViewModel Shell { get;  set; }
108
        //ShellExtensionController _extensionController=new ShellExtensionController();
109

    
110
        public PreferencesViewModel(IWindowManager windowManager, IEventAggregator events, ShellViewModel shell, PithosSettings settings, string currentTab)
111
        {
112
            // ReSharper disable DoNotCallOverridableMethodsInConstructor
113
            //Caliburn.Micro uses DisplayName for the view's title
114
            DisplayName = "Pithos+ Preferences";
115
            // ReSharper restore DoNotCallOverridableMethodsInConstructor
116

    
117
            _windowManager = windowManager;
118
            _events = events;
119

    
120
            Shell = shell;
121
            
122
            Settings=settings;
123
            Accounts = new ObservableConcurrentCollection<AccountViewModel>();
124
            if (settings.Accounts == null)
125
            {
126
                settings.Accounts=new AccountsCollection();
127
                settings.Save();
128
            }
129
            var accountVMs = from account in settings.Accounts
130
                             select new AccountViewModel(account);
131

    
132
            Accounts.AddFromEnumerable(accountVMs);
133
            
134
            var startupPath = Environment.GetFolderPath(Environment.SpecialFolder.Startup);
135
            _shortcutPath = Path.Combine(startupPath, "Pithos.lnk");
136

    
137

    
138
            StartOnSystemStartup = File.Exists(_shortcutPath);
139

    
140
            SelectedTab = currentTab;
141
        }
142

    
143
        private string _selectedTab="General";
144
        public string SelectedTab
145
        {
146
            get { return _selectedTab; }
147
            set
148
            {
149
                _selectedTab = value??"General";
150
                NotifyOfPropertyChange(()=>SelectedTab);
151
                NotifyOfPropertyChange(() => AccountTabSelected);
152
            }
153
        }
154

    
155

    
156
        public bool AccountTabSelected
157
        {
158
            get { return _selectedTab == "AccountTab"; }
159
        }
160
        #region Preferences Properties
161

    
162
        private bool _noProxy;
163
        public bool NoProxy
164
        {
165
            get { return _noProxy; }
166
            set
167
            {
168
                _noProxy = value;
169
                NotifyOfPropertyChange(()=>NoProxy);
170
            }
171
        }
172

    
173

    
174
        private bool _defaultProxy;
175

    
176
        public bool DefaultProxy
177
        {
178
            get { return _defaultProxy; }
179
            set
180
            {
181
                _defaultProxy = value;
182
                NotifyOfPropertyChange(() => DefaultProxy);
183
            }
184
        }
185

    
186

    
187
        private bool _manualProxy;
188

    
189
        public bool ManualProxy
190
        {
191
            get { return _manualProxy; }
192
            set
193
            {
194
                _manualProxy = value;
195
                NotifyOfPropertyChange(() => ManualProxy);
196
            }
197
        }
198
        #endregion
199

    
200

    
201
        public int StartupDelay
202
        {
203
            get { return (int) Settings.StartupDelay.TotalMinutes; }
204
            set
205
            {
206
                if (value<0)
207
                    throw new ArgumentOutOfRangeException("value",Resources.PreferencesViewModel_StartupDelay_Greater_or_equal_to_0);
208
                Settings.StartupDelay = TimeSpan.FromMinutes(value);
209
                NotifyOfPropertyChange(()=>StartupDelay);
210
            }
211
        }
212
       
213
        #region Commands
214
        
215
        public bool CanSelectiveSyncFolders
216
        {
217
            get { return CurrentAccount != null; }
218
        }
219

    
220
        public void SelectiveSyncFolders()
221
        {
222
            var monitor = Shell.Monitors[CurrentAccount.AccountName];
223
            
224

    
225
            var model = new SelectiveSynchViewModel(monitor,_events,CurrentAccount.Account);
226
            if (_windowManager.ShowDialog(model) == true)
227
            {
228
                
229
            }
230
        }
231

    
232
        public void RefreshApiKey()
233
        {
234
            //_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 });
235
            if (CurrentAccount == null)
236
                return;
237
            try
238
            {
239

    
240
                var name = CurrentAccount.AccountName;
241

    
242
                var loginUri = new Uri(new Uri(CurrentAccount.ServerUrl), "login");
243
                var credentials = PithosAccount.RetrieveCredentials(loginUri.ToString(),name);
244
                if (credentials==null)
245
                    return;
246
                //The server will return credentials for a different account, not just the current account
247
                //We need to find the correct account first
248
                var account = Accounts.First(act => act.AccountName == credentials.UserName && act.ServerUrl == CurrentAccount.ServerUrl);
249
                account.ApiKey = credentials.Password;                
250
                account.IsExpired = false;
251
                Settings.Save();
252
                TaskEx.Delay(10000).ContinueWith(_ =>Shell.MonitorAccount(account.Account));
253
                NotifyOfPropertyChange(() => Accounts);
254
            }
255
            catch (AggregateException exc)
256
            {
257
                string message = String.Format("API Key retrieval failed");
258
                Log.Error(message, exc.InnerException);
259
                _events.Publish(new Notification { Title = "Authorization failed", Message = message, Level = TraceLevel.Error });
260
            }
261
            catch (Exception exc)
262
            {
263
                string message = String.Format("API Key retrieval failed");
264
                Log.Error(message, exc);
265
                _events.Publish(new Notification { Title = "Authorization failed", Message = message, Level = TraceLevel.Error });
266
            }
267

    
268
        }
269

    
270
    
271
        public void OpenLogPath()
272
        {
273
            Shell.OpenLogPath();
274
        }
275

    
276
        public void OpenLogConsole()
277
        {
278
            var logView=IoC.Get<LogConsole.LogConsoleViewModel>();            
279
            _windowManager.ShowWindow(logView);
280
        }
281

    
282
        public void SaveChanges()
283
        {
284
            DoSave();
285
            TryClose(true);
286
        }
287

    
288
        public void RejectChanges()
289
        {
290
            Settings.Reload();
291
            TryClose(false);
292
        }
293

    
294
        public void ApplyChanges()
295
        {
296
            DoSave();
297
        }
298

    
299
        private void DoSave()
300
        {
301
            //SetStartupMode();            
302

    
303
            //Ensure we save the settings changes first
304
            foreach (var account in _accountsToRemove)
305
            {
306
                Settings.Accounts.Remove(account);
307
            }
308

    
309
            foreach (var account in _accountsToAdd)
310
            {
311
                Settings.Accounts.Add(account);    
312
            }
313

    
314
            Settings.Save();
315

    
316

    
317
            try
318
            {
319
                foreach (var account in _accountsToRemove)
320
                {
321
                    Shell.RemoveMonitor(account.AccountName);
322
                    Shell.RemoveAccountFromDatabase(account);
323
                }
324

    
325
                foreach (var account in Settings.Accounts)
326
                {
327
                    Shell.MonitorAccount(account);
328
                }
329
            }                
330
            finally
331
            {
332
                _accountsToRemove.Clear();
333
                _accountsToAdd.Clear();
334
            }
335

    
336
            NotifyOfPropertyChange(()=>Settings);
337

    
338
            if (IgnoreCertificateErrors)
339
                ServicePointManager.ServerCertificateValidationCallback= (sender,certificate,chain,errors)=> true;
340
            else
341
            {
342
                ServicePointManager.ServerCertificateValidationCallback = null;
343
            }
344
        }
345

    
346
     /*   public void ChangePithosFolder()
347
        {
348
            var browser = new FolderBrowserDialog();
349
            browser.SelectedPath = Settings.PithosPath;
350
            var result = browser.ShowDialog((IWin32Window)GetView());
351
            if (result == DialogResult.OK)
352
            {
353
                var newPath = browser.SelectedPath;
354
                var accountName = CurrentAccount.AccountName;
355
                var monitor = Shell.Monitors[accountName];
356
                monitor.Stop();
357
                
358
                Shell.Monitors.Remove(accountName);
359

    
360
                Directory.Move(Settings.PithosPath, newPath);
361
                Settings.PithosPath = newPath;
362
                Settings.Save();
363

    
364
                Shell.MonitorAccount(CurrentAccount);
365

    
366
                NotifyOfPropertyChange(() => Settings);                
367
            }
368
        }
369
*/
370

    
371

    
372
        readonly List<AccountSettings> _accountsToAdd=new List<AccountSettings>();
373
       public void AddAccount()
374
       {
375
           var wizard = new AddAccountViewModel();
376
           if (_windowManager.ShowDialog(wizard) == true)
377
           {
378
               string selectedPath = wizard.AccountPath;
379
               var initialRootPath = wizard.ShouldCreateOkeanosFolder?
380
                   Path.Combine(selectedPath, "Okeanos")
381
                   :selectedPath;
382
               var actualRootPath= initialRootPath;
383
               if (wizard.ShouldCreateOkeanosFolder)
384
               {
385
                   int attempt = 1;
386
                   while (Directory.Exists(actualRootPath) || File.Exists(actualRootPath))
387
                   {
388
                       actualRootPath = String.Format("{0} {1}", initialRootPath, attempt++);
389
                   }
390
               }
391

    
392

    
393

    
394
               var account = Accounts.FirstOrDefault(act => act.AccountName == wizard.AccountName && act.ServerUrl == wizard.CurrentServer);
395
               if (account != null)
396
               {
397
                   if (MessageBox.Show("The account you specified already exists. Do you want to update it?","The account exists") == MessageBoxResult.Yes)
398
                   {
399
                       account.ApiKey = wizard.Token;
400
                       account.IsExpired = false;
401
                       CurrentAccount = account;
402
                   }
403
               }
404
               else
405
               {
406
                   var newAccount = new AccountSettings
407
                                        {
408
                                            AccountName = wizard.AccountName,
409
                                            ServerUrl = wizard.CurrentServer,
410
                                            ApiKey = wizard.Token,
411
                                            RootPath = actualRootPath,
412
                                            IsActive = wizard.IsAccountActive
413
                                        };
414
                   _accountsToAdd.Add(newAccount);
415
                   var accountVm = new AccountViewModel(newAccount);
416
                   (Accounts as IProducerConsumerCollection<AccountViewModel>).TryAdd(accountVm);
417
                   CurrentAccount = accountVm;
418
               }
419
               NotifyOfPropertyChange(() => Accounts);
420
               NotifyOfPropertyChange(() => Settings);   
421
           }
422

    
423

    
424
            
425
       }
426

    
427
/*
428
        public void AddPithosAccount()
429
       {
430
            var credentials=PithosAccount.RetrieveCredentials(null);
431
            if (credentials == null)
432
                return;
433
            var account = Settings.Accounts.FirstOrDefault(act => act.AccountName == credentials.UserName);
434
            var accountVM = new AccountViewModel(account);
435
            if (account == null)
436
            {
437
                account=new AccountSettings{
438
                    AccountName=credentials.UserName,
439
                    ApiKey=credentials.Password
440
                };
441
                Settings.Accounts.Add(account);
442
                accountVM = new AccountViewModel(account);
443
                (Accounts as IProducerConsumerCollection<AccountViewModel>).TryAdd(accountVM);
444
            }
445
            else
446
            {
447
                account.ApiKey=credentials.Password;
448
            }
449
            //SelectedAccountIndex= Settings.Accounts.IndexOf(account);
450
            CurrentAccount = accountVM;
451
            NotifyOfPropertyChange(() => Accounts);
452
            NotifyOfPropertyChange(()=>Settings);                       
453
       }
454
*/
455

    
456

    
457
        readonly List<AccountSettings> _accountsToRemove = new List<AccountSettings>();
458
        public void RemoveAccount()
459
        {
460
            Accounts.TryRemove(CurrentAccount);
461
            _accountsToRemove.Add(CurrentAccount.Account);
462

    
463
            CurrentAccount = null;
464
            NotifyOfPropertyChange(() => Accounts);
465

    
466
            
467
            //NotifyOfPropertyChange("Settings.Accounts");
468
        }
469

    
470
        public bool CanRemoveAccount
471
        {
472
            get { return (CurrentAccount != null); }
473
        }
474

    
475

    
476

    
477
        public bool ExtensionsActivated
478
        {
479
            get { return Settings.ExtensionsActivated; }
480
            set
481
            {
482
                if (Settings.ExtensionsActivated == value)
483
                    return;
484

    
485
                Settings.ExtensionsActivated = value;
486

    
487
/*
488
                if (value)
489
                    _extensionController.RegisterExtensions();
490
                else
491
                {
492
                    _extensionController.UnregisterExtensions();
493
                }
494
*/
495
                NotifyOfPropertyChange(() => ExtensionsActivated);
496
            }
497
        }
498

    
499
        public bool DebugLoggingEnabled
500
        {
501
            get { return Settings.DebugLoggingEnabled; }
502
            set { 
503
                Settings.DebugLoggingEnabled = value;
504
                NotifyOfPropertyChange(()=>DebugLoggingEnabled);
505
            }
506
        }
507

    
508
        public bool IgnoreCertificateErrors
509
        {
510
            get { return Settings.IgnoreCertificateErrors; }
511
            set {
512
                Settings.IgnoreCertificateErrors = value;
513
                NotifyOfPropertyChange(() => IgnoreCertificateErrors);
514
            }
515
        }
516
       
517
        #endregion
518

    
519
       /* private int _selectedAccountIndex;
520
        public int SelectedAccountIndex
521
        {
522
            get { return _selectedAccountIndex; }
523
            set
524
            {
525
                //var accountCount=Settings.Accounts.Count;
526
                //if (accountCount == 0)
527
                //    return;
528
                //if (0 <= value && value < accountCount)
529
                //    _selectedAccountIndex = value;
530
                //else
531
                //    _selectedAccountIndex = 0;
532
                _selectedAccountIndex = value;
533
                NotifyOfPropertyChange(() => CurrentAccount);
534
                NotifyOfPropertyChange(() => CanRemoveAccount);
535
                NotifyOfPropertyChange(()=>SelectedAccountIndex);
536
            }
537
        }*/
538

    
539
        private AccountViewModel _currentAccount;
540
        private readonly IWindowManager _windowManager;
541
        private readonly string _shortcutPath;
542

    
543

    
544
        
545
        public AccountViewModel CurrentAccount
546
        {
547
            get { return _currentAccount; }
548
            set
549
            {
550
                _currentAccount = value;
551
                NotifyOfPropertyChange(()=>CurrentAccount);
552
                NotifyOfPropertyChange(() => CanRemoveAccount);
553
                NotifyOfPropertyChange(() => CanSelectiveSyncFolders);
554
                NotifyOfPropertyChange(() => CanMoveAccountFolder);
555
            }
556
        }
557

    
558
/*
559
        public AccountSettings CurrentAccount
560
        {
561
            get {
562
                if (0 <= SelectedAccountIndex && SelectedAccountIndex < Settings.Accounts.Count)                    
563
                    return Settings.Accounts[SelectedAccountIndex];
564
                return null;
565
            }
566

    
567
        }
568
*/
569

    
570

    
571
        public bool CanMoveAccountFolder
572
        {
573
            get { return CurrentAccount != null; }
574
        }
575

    
576
    public void MoveAccountFolder()
577
    {
578

    
579
        using (var dlg = new FolderBrowserDialog())
580
        {
581
            var currentFolder = CurrentAccount.RootPath;
582
            dlg.SelectedPath = currentFolder;
583
            //Ask the user to select a folder
584
            //Note: We need a parent window here, which we retrieve with GetView            
585
            var view = (Window)GetView();            
586
            if (DialogResult.OK != dlg.ShowDialog(new Wpf32Window(view)))
587
                return;            
588

    
589
            var newPath= dlg.SelectedPath;                
590
            //Find the account's monitor and stop it
591
            PithosMonitor monitor;
592
            if (Shell.Monitors.TryGetValue(CurrentAccount.AccountName, out monitor))
593
            {
594
                monitor.Stop();
595

    
596

    
597
                var oldPath = monitor.RootPath;
598
                //The old directory may not exist eg. if we create an account for the first time
599
                if (Directory.Exists(oldPath))
600
                {
601
                    //If it does, do the move
602

    
603
                    //Now Create all of the directories
604
                    foreach (string dirPath in Directory.EnumerateDirectories(oldPath, "*",
605
                                                           SearchOption.AllDirectories))
606
                        Directory.CreateDirectory(dirPath.Replace(oldPath, newPath));
607

    
608
                    //Copy all the files
609
                    foreach (string newFilePath in Directory.EnumerateFiles(oldPath, "*.*",
610
                                                                            SearchOption.AllDirectories))
611
                        File.Copy(newFilePath, newFilePath.Replace(oldPath, newPath));
612

    
613
                    Log.InfoFormat("Deleting account folder {0}",oldPath);
614
                    Directory.Delete(oldPath, true);
615

    
616
                    //We also need to change the path of the existing file states
617
                    monitor.MoveFileStates(oldPath, newPath);
618
                }
619
            }
620
            //Replace the old rootpath with the new
621
            CurrentAccount.RootPath = newPath;
622
            //TODO: This will save all settings changes. Too coarse grained, need to fix at a later date
623
            Settings.Save();            
624
            //And start the monitor on the new RootPath            
625
            if (monitor != null)
626
            {
627
                monitor.RootPath = newPath;
628
                if (CurrentAccount.IsActive)
629
                    monitor.Start();
630
            }
631
            else
632
                Shell.MonitorAccount(CurrentAccount.Account);
633
            //Finally, notify that the Settings, CurrentAccount have changed
634
            NotifyOfPropertyChange(() => CurrentAccount);
635
            NotifyOfPropertyChange(() => Settings);
636

    
637
        }
638
    }
639
    }
640
}