Statistics
| Branch: | Revision:

root / trunk / Pithos.Client.WPF / Preferences / PreferencesViewModel.cs @ 84a3db1b

History | View | Annotate | Download (20.3 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.Reflection;
50
using System.Threading.Tasks;
51
using System.Windows;
52
using System.Windows.Forms;
53
using Caliburn.Micro;
54
using Pithos.Client.WPF.Configuration;
55
using Pithos.Client.WPF.Properties;
56
using Pithos.Client.WPF.SelectiveSynch;
57
using Pithos.Core;
58
using Pithos.Interfaces;
59
using System;
60
using System.Linq;
61
using Screen = Caliburn.Micro.Screen;
62

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

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

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

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

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

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

    
115
            _windowManager = windowManager;
116
            _events = events;
117

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

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

    
135

    
136
            StartOnSystemStartup = File.Exists(_shortcutPath);
137

    
138
            SelectedTab = currentTab;
139
        }
140

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

    
153

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

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

    
171

    
172
        private bool _defaultProxy;
173

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

    
184

    
185
        private bool _manualProxy;
186

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

    
198

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

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

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

    
230
        public void RefreshApiKey()
231
        {
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)
234
                return;
235
            try
236
            {
237

    
238
                var name = CurrentAccount.AccountName;
239

    
240
                var loginUri = new Uri(new Uri(CurrentAccount.ServerUrl), "login");
241
                var credentials = PithosAccount.RetrieveCredentials(loginUri.ToString(),name);
242
                if (credentials==null)
243
                    return;
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;
249
                Settings.Save();
250
                TaskEx.Delay(10000).ContinueWith(_ =>Shell.MonitorAccount(account.Account));
251
                NotifyOfPropertyChange(() => Accounts);
252
            }
253
            catch (AggregateException exc)
254
            {
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 });
258
            }
259
            catch (Exception exc)
260
            {
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 });
264
            }
265

    
266
        }
267

    
268
    
269
        public void OpenLogPath()
270
        {
271
            Shell.OpenLogPath();
272
        }
273

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

    
280
        public void SaveChanges()
281
        {
282
            DoSave();
283
            TryClose(true);
284
        }
285

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

    
292
        public void ApplyChanges()
293
        {
294
            DoSave();
295
        }
296

    
297
        private void DoSave()
298
        {
299
            //SetStartupMode();            
300

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

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

    
312
            Settings.Save();
313

    
314

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

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

    
334
            NotifyOfPropertyChange(()=>Settings);
335
        }
336

    
337
     /*   public void ChangePithosFolder()
338
        {
339
            var browser = new FolderBrowserDialog();
340
            browser.SelectedPath = Settings.PithosPath;
341
            var result = browser.ShowDialog((IWin32Window)GetView());
342
            if (result == DialogResult.OK)
343
            {
344
                var newPath = browser.SelectedPath;
345
                var accountName = CurrentAccount.AccountName;
346
                var monitor = Shell.Monitors[accountName];
347
                monitor.Stop();
348
                
349
                Shell.Monitors.Remove(accountName);
350

    
351
                Directory.Move(Settings.PithosPath, newPath);
352
                Settings.PithosPath = newPath;
353
                Settings.Save();
354

    
355
                Shell.MonitorAccount(CurrentAccount);
356

    
357
                NotifyOfPropertyChange(() => Settings);                
358
            }
359
        }
360
*/
361

    
362

    
363
        readonly List<AccountSettings> _accountsToAdd=new List<AccountSettings>();
364
       public void AddAccount()
365
       {
366
           var wizard = new AddAccountViewModel();
367
           if (_windowManager.ShowDialog(wizard) == true)
368
           {
369
               string selectedPath = wizard.AccountPath;
370
               var initialRootPath = wizard.ShouldCreateOkeanosFolder?
371
                   Path.Combine(selectedPath, "Okeanos")
372
                   :selectedPath;
373
               var actualRootPath= initialRootPath;
374
               if (wizard.ShouldCreateOkeanosFolder)
375
               {
376
                   int attempt = 1;
377
                   while (Directory.Exists(actualRootPath) || File.Exists(actualRootPath))
378
                   {
379
                       actualRootPath = String.Format("{0} {1}", initialRootPath, attempt++);
380
                   }
381
               }
382
               ### Check that the account does not already exist
383

    
384
               var newAccount = new AccountSettings
385
                                    {
386
                                        AccountName = wizard.AccountName,
387
                                        ServerUrl=wizard.CurrentServer,
388
                                        ApiKey=wizard.Token,
389
                                        RootPath = actualRootPath,
390
                                        IsActive=wizard.IsAccountActive
391
                                    };
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);   
398
           }
399

    
400

    
401
            
402
       }
403

    
404
/*
405
        public void AddPithosAccount()
406
       {
407
            var credentials=PithosAccount.RetrieveCredentials(null);
408
            if (credentials == null)
409
                return;
410
            var account = Settings.Accounts.FirstOrDefault(act => act.AccountName == credentials.UserName);
411
            var accountVM = new AccountViewModel(account);
412
            if (account == null)
413
            {
414
                account=new AccountSettings{
415
                    AccountName=credentials.UserName,
416
                    ApiKey=credentials.Password
417
                };
418
                Settings.Accounts.Add(account);
419
                accountVM = new AccountViewModel(account);
420
                (Accounts as IProducerConsumerCollection<AccountViewModel>).TryAdd(accountVM);
421
            }
422
            else
423
            {
424
                account.ApiKey=credentials.Password;
425
            }
426
            //SelectedAccountIndex= Settings.Accounts.IndexOf(account);
427
            CurrentAccount = accountVM;
428
            NotifyOfPropertyChange(() => Accounts);
429
            NotifyOfPropertyChange(()=>Settings);                       
430
       }
431
*/
432

    
433

    
434
        readonly List<AccountSettings> _accountsToRemove = new List<AccountSettings>();
435
        public void RemoveAccount()
436
        {
437
            Accounts.TryRemove(CurrentAccount);
438
            _accountsToRemove.Add(CurrentAccount.Account);
439

    
440
            CurrentAccount = null;
441
            NotifyOfPropertyChange(() => Accounts);
442

    
443
            
444
            //NotifyOfPropertyChange("Settings.Accounts");
445
        }
446

    
447
        public bool CanRemoveAccount
448
        {
449
            get { return (CurrentAccount != null); }
450
        }
451

    
452

    
453

    
454
        public bool ExtensionsActivated
455
        {
456
            get { return Settings.ExtensionsActivated; }
457
            set
458
            {
459
                if (Settings.ExtensionsActivated == value)
460
                    return;
461

    
462
                Settings.ExtensionsActivated = value;
463

    
464
/*
465
                if (value)
466
                    _extensionController.RegisterExtensions();
467
                else
468
                {
469
                    _extensionController.UnregisterExtensions();
470
                }
471
*/
472
                NotifyOfPropertyChange(() => ExtensionsActivated);
473
            }
474
        }
475

    
476
        public bool DebugLoggingEnabled
477
        {
478
            get { return Settings.DebugLoggingEnabled; }
479
            set { 
480
                Settings.DebugLoggingEnabled = value;
481
                NotifyOfPropertyChange(()=>DebugLoggingEnabled);
482
            }
483
        }
484
       
485
        #endregion
486

    
487
       /* private int _selectedAccountIndex;
488
        public int SelectedAccountIndex
489
        {
490
            get { return _selectedAccountIndex; }
491
            set
492
            {
493
                //var accountCount=Settings.Accounts.Count;
494
                //if (accountCount == 0)
495
                //    return;
496
                //if (0 <= value && value < accountCount)
497
                //    _selectedAccountIndex = value;
498
                //else
499
                //    _selectedAccountIndex = 0;
500
                _selectedAccountIndex = value;
501
                NotifyOfPropertyChange(() => CurrentAccount);
502
                NotifyOfPropertyChange(() => CanRemoveAccount);
503
                NotifyOfPropertyChange(()=>SelectedAccountIndex);
504
            }
505
        }*/
506

    
507
        private AccountViewModel _currentAccount;
508
        private readonly IWindowManager _windowManager;
509
        private readonly string _shortcutPath;
510

    
511

    
512
        
513
        public AccountViewModel CurrentAccount
514
        {
515
            get { return _currentAccount; }
516
            set
517
            {
518
                _currentAccount = value;
519
                NotifyOfPropertyChange(()=>CurrentAccount);
520
                NotifyOfPropertyChange(() => CanRemoveAccount);
521
                NotifyOfPropertyChange(() => CanSelectiveSyncFolders);
522
                NotifyOfPropertyChange(() => CanMoveAccountFolder);
523
            }
524
        }
525

    
526
/*
527
        public AccountSettings CurrentAccount
528
        {
529
            get {
530
                if (0 <= SelectedAccountIndex && SelectedAccountIndex < Settings.Accounts.Count)                    
531
                    return Settings.Accounts[SelectedAccountIndex];
532
                return null;
533
            }
534

    
535
        }
536
*/
537

    
538

    
539
        public bool CanMoveAccountFolder
540
        {
541
            get { return CurrentAccount != null; }
542
        }
543

    
544
    public void MoveAccountFolder()
545
    {
546

    
547
        using (var dlg = new FolderBrowserDialog())
548
        {
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)))
555
                return;            
556

    
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))
561
            {
562
                monitor.Stop();
563

    
564

    
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))
568
                {
569
                    //If it does, do the move
570

    
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));
575

    
576
                    //Copy all the files
577
                    foreach (string newFilePath in Directory.EnumerateFiles(oldPath, "*.*",
578
                                                                            SearchOption.AllDirectories))
579
                        File.Copy(newFilePath, newFilePath.Replace(oldPath, newPath));
580

    
581
                    Log.InfoFormat("Deleting account folder {0}",oldPath);
582
                    Directory.Delete(oldPath, true);
583

    
584
                    //We also need to change the path of the existing file states
585
                    monitor.MoveFileStates(oldPath, newPath);
586
                }
587
            }
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
591
            Settings.Save();            
592
            //And start the monitor on the new RootPath            
593
            if (monitor != null)
594
            {
595
                monitor.RootPath = newPath;
596
                if (CurrentAccount.IsActive)
597
                    monitor.Start();
598
            }
599
            else
600
                Shell.MonitorAccount(CurrentAccount.Account);
601
            //Finally, notify that the Settings, CurrentAccount have changed
602
            NotifyOfPropertyChange(() => CurrentAccount);
603
            NotifyOfPropertyChange(() => Settings);
604

    
605
        }
606
    }
607
    }
608
}