Statistics
| Branch: | Revision:

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

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

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

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

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

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

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

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

    
114
            _windowManager = windowManager;
115
            _events = events;
116

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

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

    
134

    
135
            StartOnSystemStartup = File.Exists(_shortcutPath);
136

    
137
            SelectedTab = currentTab;
138
        }
139

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

    
152

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

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

    
170

    
171
        private bool _defaultProxy;
172

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

    
183

    
184
        private bool _manualProxy;
185

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

    
197

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

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

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

    
229
        public async Task RefreshApiKey()
230
        {
231
            _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 });
232

    
233
            try
234
            {
235

    
236
                var credentials = await PithosAccount.RetrieveCredentials(Settings.PithosLoginUrl);
237
                //The server will return credentials for a different account, not just the current account
238
                //We need to find the correct account first
239
                var account = Accounts.First(act => act.AccountName == credentials.UserName);
240
                account.ApiKey = credentials.Password;                
241
                account.IsExpired = false;
242
                Settings.Save();
243
                TaskEx.Delay(10000).ContinueWith(_ =>Shell.MonitorAccount(account.Account));
244
                NotifyOfPropertyChange(() => Accounts);
245
            }
246
            catch (AggregateException exc)
247
            {
248
                string message = String.Format("API Key retrieval failed");
249
                Log.Error(message, exc.InnerException);
250
                _events.Publish(new Notification { Title = "Authorization failed", Message = message, Level = TraceLevel.Error });
251
            }
252
            catch (Exception exc)
253
            {
254
                string message = String.Format("API Key retrieval failed");
255
                Log.Error(message, exc);
256
                _events.Publish(new Notification { Title = "Authorization failed", Message = message, Level = TraceLevel.Error });
257
            }
258

    
259
        }
260

    
261
    
262
        public void SaveChanges()
263
        {
264
            DoSave();
265
            TryClose(true);
266
        }
267

    
268
        public void RejectChanges()
269
        {
270
            Settings.Reload();
271
            TryClose(false);
272
        }
273

    
274
        public void ApplyChanges()
275
        {
276
            DoSave();
277
        }
278

    
279
        private void DoSave()
280
        {
281
            //SetStartupMode();            
282

    
283
            //Ensure we save the settings changes first
284
            foreach (var account in _accountsToRemove)
285
            {
286
                Settings.Accounts.Remove(account);
287
            }
288

    
289
            foreach (var account in _accountsToAdd)
290
            {
291
                Settings.Accounts.Add(account);    
292
            }
293
            
294
            Settings.Save();
295

    
296

    
297
            foreach (var account in _accountsToRemove)
298
            {
299
                Shell.RemoveMonitor(account.AccountName);
300
            }
301

    
302
            foreach (var account in Settings.Accounts)
303
            {                                
304
                Shell.MonitorAccount(account);
305
            }
306

    
307
            NotifyOfPropertyChange(()=>Settings);
308
        }
309

    
310
     /*   public void ChangePithosFolder()
311
        {
312
            var browser = new FolderBrowserDialog();
313
            browser.SelectedPath = Settings.PithosPath;
314
            var result = browser.ShowDialog((IWin32Window)GetView());
315
            if (result == DialogResult.OK)
316
            {
317
                var newPath = browser.SelectedPath;
318
                var accountName = CurrentAccount.AccountName;
319
                var monitor = Shell.Monitors[accountName];
320
                monitor.Stop();
321
                
322
                Shell.Monitors.Remove(accountName);
323

    
324
                Directory.Move(Settings.PithosPath, newPath);
325
                Settings.PithosPath = newPath;
326
                Settings.Save();
327

    
328
                Shell.MonitorAccount(CurrentAccount);
329

    
330
                NotifyOfPropertyChange(() => Settings);                
331
            }
332
        }
333
*/
334

    
335

    
336
        readonly List<AccountSettings> _accountsToAdd=new List<AccountSettings>();
337
       public void AddAccount()
338
       {
339
           var wizard = new AddAccountViewModel();
340
           if (_windowManager.ShowDialog(wizard) == true)
341
           {
342
               string selectedPath = wizard.AccountPath;
343
               var initialRootPath = Path.Combine(selectedPath, "Okeanos");
344
               var actualRootPath= initialRootPath;
345
               int attempt = 1;
346
               while (Directory.Exists(actualRootPath) || File.Exists(actualRootPath))
347
               {
348
                   actualRootPath = String.Format("{0} {1}", initialRootPath,attempt++);
349
               }
350

    
351
               var newAccount = new AccountSettings
352
                                    {
353
                                        AccountName = wizard.AccountName,
354
                                        ServerUrl=wizard.CurrentServer,
355
                                        ApiKey=wizard.Token,
356
                                        RootPath = actualRootPath,
357
                                        IsActive=wizard.IsAccountActive
358
                                    };
359
               _accountsToAdd.Add(newAccount);
360
               var accountVm = new AccountViewModel(newAccount);
361
               (Accounts as IProducerConsumerCollection<AccountViewModel>).TryAdd(accountVm);
362
               CurrentAccount = accountVm;
363
               NotifyOfPropertyChange(() => Accounts);
364
               NotifyOfPropertyChange(() => Settings);   
365
           }
366

    
367

    
368
            
369
       }
370

    
371
        public async void AddPithosAccount()
372
       {
373
            var credentials=await PithosAccount.RetrieveCredentials(Settings.PithosLoginUrl);
374
            var account = Settings.Accounts.FirstOrDefault(act => act.AccountName == credentials.UserName);
375
            var accountVM = new AccountViewModel(account);
376
            if (account == null)
377
            {
378
                account=new AccountSettings{
379
                    AccountName=credentials.UserName,
380
                    ApiKey=credentials.Password
381
                };
382
                Settings.Accounts.Add(account);
383
                accountVM = new AccountViewModel(account);
384
                (Accounts as IProducerConsumerCollection<AccountViewModel>).TryAdd(accountVM);
385
            }
386
            else
387
            {
388
                account.ApiKey=credentials.Password;
389
            }
390
            //SelectedAccountIndex= Settings.Accounts.IndexOf(account);
391
            CurrentAccount = accountVM;
392
            NotifyOfPropertyChange(() => Accounts);
393
            NotifyOfPropertyChange(()=>Settings);                       
394
       }
395

    
396

    
397
        readonly List<AccountSettings> _accountsToRemove = new List<AccountSettings>();
398
        public void RemoveAccount()
399
        {
400
            Accounts.TryRemove(CurrentAccount);
401
            _accountsToRemove.Add(CurrentAccount.Account);
402

    
403
            CurrentAccount = null;
404
            NotifyOfPropertyChange(() => Accounts);
405

    
406
            
407
            //NotifyOfPropertyChange("Settings.Accounts");
408
        }
409

    
410
        public bool CanRemoveAccount
411
        {
412
            get { return (CurrentAccount != null); }
413
        }
414

    
415

    
416

    
417
        public bool ExtensionsActivated
418
        {
419
            get { return Settings.ExtensionsActivated; }
420
            set
421
            {
422
                if (Settings.ExtensionsActivated == value)
423
                    return;
424

    
425
                Settings.ExtensionsActivated = value;
426

    
427
/*
428
                if (value)
429
                    _extensionController.RegisterExtensions();
430
                else
431
                {
432
                    _extensionController.UnregisterExtensions();
433
                }
434
*/
435
                NotifyOfPropertyChange(() => ExtensionsActivated);
436
            }
437
        }
438

    
439
       
440
        #endregion
441

    
442
       /* private int _selectedAccountIndex;
443
        public int SelectedAccountIndex
444
        {
445
            get { return _selectedAccountIndex; }
446
            set
447
            {
448
                //var accountCount=Settings.Accounts.Count;
449
                //if (accountCount == 0)
450
                //    return;
451
                //if (0 <= value && value < accountCount)
452
                //    _selectedAccountIndex = value;
453
                //else
454
                //    _selectedAccountIndex = 0;
455
                _selectedAccountIndex = value;
456
                NotifyOfPropertyChange(() => CurrentAccount);
457
                NotifyOfPropertyChange(() => CanRemoveAccount);
458
                NotifyOfPropertyChange(()=>SelectedAccountIndex);
459
            }
460
        }*/
461

    
462
        private AccountViewModel _currentAccount;
463
        private readonly IWindowManager _windowManager;
464
        private readonly string _shortcutPath;
465

    
466

    
467
        
468
        public AccountViewModel CurrentAccount
469
        {
470
            get { return _currentAccount; }
471
            set
472
            {
473
                _currentAccount = value;
474
                NotifyOfPropertyChange(()=>CurrentAccount);
475
                NotifyOfPropertyChange(() => CanRemoveAccount);
476
                NotifyOfPropertyChange(() => CanSelectiveSyncFolders);
477
                NotifyOfPropertyChange(() => CanMoveAccountFolder);
478
            }
479
        }
480

    
481
/*
482
        public AccountSettings CurrentAccount
483
        {
484
            get {
485
                if (0 <= SelectedAccountIndex && SelectedAccountIndex < Settings.Accounts.Count)                    
486
                    return Settings.Accounts[SelectedAccountIndex];
487
                return null;
488
            }
489

    
490
        }
491
*/
492

    
493

    
494
        public bool CanMoveAccountFolder
495
        {
496
            get { return CurrentAccount != null; }
497
        }
498

    
499
    public void MoveAccountFolder()
500
    {
501

    
502
        using (var dlg = new FolderBrowserDialog())
503
        {
504
            var currentFolder = CurrentAccount.RootPath;
505
            dlg.SelectedPath = currentFolder;
506
            //Ask the user to select a folder
507
            //Note: We need a parent window here, which we retrieve with GetView            
508
            var view = (Window)GetView();            
509
            if (DialogResult.OK != dlg.ShowDialog(new Wpf32Window(view)))
510
                return;            
511

    
512
            var newPath= dlg.SelectedPath;                
513
            //Find the account's monitor and stop it
514
            PithosMonitor monitor;
515
            if (Shell.Monitors.TryGetValue(CurrentAccount.AccountName, out monitor))
516
            {
517
                monitor.Stop();
518

    
519

    
520
                var oldPath = monitor.RootPath;
521
                //The old directory may not exist eg. if we create an account for the first time
522
                if (Directory.Exists(oldPath))
523
                {
524
                    //If it does, do the move
525

    
526
                    //Now Create all of the directories
527
                    foreach (string dirPath in Directory.EnumerateDirectories(oldPath, "*",
528
                                                           SearchOption.AllDirectories))
529
                        Directory.CreateDirectory(dirPath.Replace(oldPath, newPath));
530

    
531
                    //Copy all the files
532
                    foreach (string newFilePath in Directory.EnumerateFiles(oldPath, "*.*",
533
                                                                            SearchOption.AllDirectories))
534
                        File.Copy(newFilePath, newFilePath.Replace(oldPath, newPath));
535

    
536
                    Directory.Delete(oldPath, true);
537

    
538
                    //We also need to change the path of the existing file states
539
                    monitor.MoveFileStates(oldPath, newPath);
540
                }
541
            }
542
            //Replace the old rootpath with the new
543
            CurrentAccount.RootPath = newPath;
544
            //TODO: This will save all settings changes. Too coarse grained, need to fix at a later date
545
            Settings.Save();            
546
            //And start the monitor on the new RootPath            
547
            if (monitor != null)
548
            {
549
                monitor.RootPath = newPath;
550
                if (CurrentAccount.IsActive)
551
                    monitor.Start();
552
            }
553
            else
554
                Shell.MonitorAccount(CurrentAccount.Account);
555
            //Finally, notify that the Settings, CurrentAccount have changed
556
            NotifyOfPropertyChange(() => CurrentAccount);
557
            NotifyOfPropertyChange(() => Settings);
558

    
559
        }
560
    }
561
    }
562
}