Statistics
| Branch: | Revision:

root / trunk / Pithos.Client.WPF / Preferences / PreferencesViewModel.cs @ 8f44fd3a

History | View | Annotate | Download (22.7 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, PartCreationPolicy(CreationPolicy.Shared)]
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.AccountKey];
223
            
224

    
225
            var model = new SelectiveSynchViewModel(/*monitor,*/_events,CurrentAccount.Account,CurrentAccount.ApiKey);
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.ServerUrl, 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
        public bool CanClearAccountCache
476
        {
477
            get { return (CurrentAccount != null); }
478
        }
479

    
480
        public void ClearAccountCache()
481
        {
482
            if (MessageBoxResult.Yes == MessageBox.Show("You are about to delete all partially downloaded files from the account's cache.\n" +
483
                            " You will have to download all partially downloaded data again\n" +
484
                            "This change can not be undone\n\n" +
485
            "Do you wish to delete all partially downloaded data?", "Warning! Clearing account cache",
486
                            MessageBoxButton.YesNo,MessageBoxImage.Question,MessageBoxResult.No))
487
            {
488

    
489
                var cachePath = Path.Combine(CurrentAccount.RootPath, FolderConstants.CacheFolder);
490
                var dir = new DirectoryInfo(cachePath);
491
                dir.EnumerateFiles().Apply(file=>file.Delete());
492
                dir.EnumerateDirectories().Apply(folder => folder.Delete(true));
493
            }
494
        }
495

    
496

    
497
        public bool ExtensionsActivated
498
        {
499
            get { return Settings.ExtensionsActivated; }
500
            set
501
            {
502
                if (Settings.ExtensionsActivated == value)
503
                    return;
504

    
505
                Settings.ExtensionsActivated = value;
506

    
507
/*
508
                if (value)
509
                    _extensionController.RegisterExtensions();
510
                else
511
                {
512
                    _extensionController.UnregisterExtensions();
513
                }
514
*/
515
                NotifyOfPropertyChange(() => ExtensionsActivated);
516
            }
517
        }
518

    
519
        public bool DebugLoggingEnabled
520
        {
521
            get { return Settings.DebugLoggingEnabled; }
522
            set { 
523
                Settings.DebugLoggingEnabled = value;
524
                NotifyOfPropertyChange(()=>DebugLoggingEnabled);
525
            }
526
        }
527

    
528
        public bool IgnoreCertificateErrors
529
        {
530
            get { return Settings.IgnoreCertificateErrors; }
531
            set {
532
                Settings.IgnoreCertificateErrors = value;
533
                NotifyOfPropertyChange(() => IgnoreCertificateErrors);
534
            }
535
        }
536
       
537
        #endregion
538

    
539
       /* private int _selectedAccountIndex;
540
        public int SelectedAccountIndex
541
        {
542
            get { return _selectedAccountIndex; }
543
            set
544
            {
545
                //var accountCount=Settings.Accounts.Count;
546
                //if (accountCount == 0)
547
                //    return;
548
                //if (0 <= value && value < accountCount)
549
                //    _selectedAccountIndex = value;
550
                //else
551
                //    _selectedAccountIndex = 0;
552
                _selectedAccountIndex = value;
553
                NotifyOfPropertyChange(() => CurrentAccount);
554
                NotifyOfPropertyChange(() => CanRemoveAccount);
555
                NotifyOfPropertyChange(()=>SelectedAccountIndex);
556
            }
557
        }*/
558

    
559
        private AccountViewModel _currentAccount;
560
        private readonly IWindowManager _windowManager;
561
        private readonly string _shortcutPath;
562

    
563

    
564
        
565
        public AccountViewModel CurrentAccount
566
        {
567
            get { return _currentAccount; }
568
            set
569
            {
570
                _currentAccount = value;
571
                NotifyOfPropertyChange(()=>CurrentAccount);
572
                NotifyOfPropertyChange(() => CanRemoveAccount);
573
                NotifyOfPropertyChange(() => CanSelectiveSyncFolders);
574
                NotifyOfPropertyChange(() => CanMoveAccountFolder);
575
                NotifyOfPropertyChange(() => CanClearAccountCache);
576
            }
577
        }
578

    
579
/*
580
        public AccountSettings CurrentAccount
581
        {
582
            get {
583
                if (0 <= SelectedAccountIndex && SelectedAccountIndex < Settings.Accounts.Count)                    
584
                    return Settings.Accounts[SelectedAccountIndex];
585
                return null;
586
            }
587

    
588
        }
589
*/
590

    
591

    
592
        public bool CanMoveAccountFolder
593
        {
594
            get { return CurrentAccount != null; }
595
        }
596

    
597
    public void MoveAccountFolder()
598
    {
599

    
600
        using (var dlg = new FolderBrowserDialog())
601
        {
602
            var currentFolder = CurrentAccount.RootPath;
603
            dlg.SelectedPath = currentFolder;
604
            //Ask the user to select a folder
605
            //Note: We need a parent window here, which we retrieve with GetView            
606
            var view = (Window)GetView();            
607
            if (DialogResult.OK != dlg.ShowDialog(new Wpf32Window(view)))
608
                return;            
609

    
610
            var newPath= dlg.SelectedPath;                
611
            //Find the account's monitor and stop it
612
            PithosMonitor monitor;
613
            if (Shell.Monitors.TryGetValue(CurrentAccount.AccountKey, out monitor))
614
            {
615
                monitor.Stop();
616

    
617

    
618
                var oldPath = monitor.RootPath;
619
                //The old directory may not exist eg. if we create an account for the first time
620
                if (Directory.Exists(oldPath))
621
                {
622
                    //If it does, do the move
623

    
624
                    //Now Create all of the directories
625
                    foreach (string dirPath in Directory.EnumerateDirectories(oldPath, "*",
626
                                                           SearchOption.AllDirectories))
627
                        Directory.CreateDirectory(dirPath.Replace(oldPath, newPath));
628

    
629
                    //Copy all the files
630
                    foreach (string newFilePath in Directory.EnumerateFiles(oldPath, "*.*",
631
                                                                            SearchOption.AllDirectories))
632
                        File.Copy(newFilePath, newFilePath.Replace(oldPath, newPath));
633

    
634
                    Log.InfoFormat("Deleting account folder {0}",oldPath);
635
                    Directory.Delete(oldPath, true);
636

    
637
                    //We also need to change the path of the existing file states
638
                    monitor.MoveFileStates(oldPath, newPath);
639
                }
640
            }
641
            //Replace the old rootpath with the new
642
            CurrentAccount.RootPath = newPath;
643
            //TODO: This will save all settings changes. Too coarse grained, need to fix at a later date
644
            Settings.Save();            
645
            //And start the monitor on the new RootPath            
646
            if (monitor != null)
647
            {
648
                monitor.RootPath = newPath;
649
                if (CurrentAccount.IsActive)
650
                    monitor.Start();
651
            }
652
            else
653
                Shell.MonitorAccount(CurrentAccount.Account);
654
            //Finally, notify that the Settings, CurrentAccount have changed
655
            NotifyOfPropertyChange(() => CurrentAccount);
656
            NotifyOfPropertyChange(() => Settings);
657

    
658
        }
659
    }
660
    }
661
}