Statistics
| Branch: | Revision:

root / trunk / Pithos.Client.WPF / Preferences / PreferencesViewModel.cs @ 7f5882da

History | View | Annotate | Download (18.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.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
            Settings.Save();
282
            //SetStartupMode();            
283

    
284
            foreach (var account in _accountsToRemove)
285
            {
286
                Settings.Accounts.Remove(account);
287
                Shell.RemoveMonitor(account.AccountName);
288
            }
289

    
290
            foreach (var account in Settings.Accounts)
291
            {                                
292
                Shell.MonitorAccount(account);
293
            }
294

    
295
            NotifyOfPropertyChange(()=>Settings);
296
        }
297

    
298
     /*   public void ChangePithosFolder()
299
        {
300
            var browser = new FolderBrowserDialog();
301
            browser.SelectedPath = Settings.PithosPath;
302
            var result = browser.ShowDialog((IWin32Window)GetView());
303
            if (result == DialogResult.OK)
304
            {
305
                var newPath = browser.SelectedPath;
306
                var accountName = CurrentAccount.AccountName;
307
                var monitor = Shell.Monitors[accountName];
308
                monitor.Stop();
309
                
310
                Shell.Monitors.Remove(accountName);
311

    
312
                Directory.Move(Settings.PithosPath, newPath);
313
                Settings.PithosPath = newPath;
314
                Settings.Save();
315

    
316
                Shell.MonitorAccount(CurrentAccount);
317

    
318
                NotifyOfPropertyChange(() => Settings);                
319
            }
320
        }
321
*/
322

    
323
        
324

    
325
       public void AddAccount()
326
       {
327
           var wizard = new AddAccountViewModel();
328
           if (_windowManager.ShowDialog(wizard) == true)
329
           {
330
               string selectedPath = wizard.AccountPath;
331
               var initialRootPath = Path.Combine(selectedPath, "Okeanos");
332
               var actualRootPath= initialRootPath;
333
               int attempt = 1;
334
               while (Directory.Exists(actualRootPath) || File.Exists(actualRootPath))
335
               {
336
                   actualRootPath = String.Format("{0} {1}", initialRootPath,attempt++);
337
               }
338

    
339
               var newAccount = new AccountSettings
340
                                    {
341
                                        AccountName = wizard.AccountName,
342
                                        ServerUrl=wizard.CurrentServer,
343
                                        ApiKey=wizard.Token,
344
                                        RootPath = actualRootPath,
345
                                        IsActive=wizard.IsAccountActive
346
                                    };
347
               Settings.Accounts.Add(newAccount);
348
               var accountVm = new AccountViewModel(newAccount);
349
               (Accounts as IProducerConsumerCollection<AccountViewModel>).TryAdd(accountVm);
350
               CurrentAccount = accountVm;
351
               NotifyOfPropertyChange(() => Accounts);
352
               NotifyOfPropertyChange(() => Settings);   
353
           }
354

    
355

    
356
            
357
       }
358

    
359
        public async void AddPithosAccount()
360
       {
361
            var credentials=await PithosAccount.RetrieveCredentials(Settings.PithosLoginUrl);
362
            var account = Settings.Accounts.FirstOrDefault(act => act.AccountName == credentials.UserName);
363
            var accountVM = new AccountViewModel(account);
364
            if (account == null)
365
            {
366
                account=new AccountSettings{
367
                    AccountName=credentials.UserName,
368
                    ApiKey=credentials.Password
369
                };
370
                Settings.Accounts.Add(account);
371
                accountVM = new AccountViewModel(account);
372
                (Accounts as IProducerConsumerCollection<AccountViewModel>).TryAdd(accountVM);
373
            }
374
            else
375
            {
376
                account.ApiKey=credentials.Password;
377
            }
378
            //SelectedAccountIndex= Settings.Accounts.IndexOf(account);
379
            CurrentAccount = accountVM;
380
            NotifyOfPropertyChange(() => Accounts);
381
            NotifyOfPropertyChange(()=>Settings);                       
382
       }
383

    
384

    
385
        readonly List<AccountSettings> _accountsToRemove = new List<AccountSettings>();
386
        public void RemoveAccount()
387
        {
388
            var accountName = CurrentAccount.AccountName;
389

    
390
            Accounts.TryRemove(CurrentAccount);
391
            _accountsToRemove.Add(CurrentAccount.Account);
392

    
393
            CurrentAccount = null;
394
            NotifyOfPropertyChange(() => Accounts);
395

    
396
            
397
            //NotifyOfPropertyChange("Settings.Accounts");
398
        }
399

    
400
        public bool CanRemoveAccount
401
        {
402
            get { return (CurrentAccount != null); }
403
        }
404

    
405

    
406

    
407
        public bool ExtensionsActivated
408
        {
409
            get { return Settings.ExtensionsActivated; }
410
            set
411
            {
412
                if (Settings.ExtensionsActivated == value)
413
                    return;
414

    
415
                Settings.ExtensionsActivated = value;
416

    
417
/*
418
                if (value)
419
                    _extensionController.RegisterExtensions();
420
                else
421
                {
422
                    _extensionController.UnregisterExtensions();
423
                }
424
*/
425
                NotifyOfPropertyChange(() => ExtensionsActivated);
426
            }
427
        }
428

    
429
       
430
        #endregion
431

    
432
       /* private int _selectedAccountIndex;
433
        public int SelectedAccountIndex
434
        {
435
            get { return _selectedAccountIndex; }
436
            set
437
            {
438
                //var accountCount=Settings.Accounts.Count;
439
                //if (accountCount == 0)
440
                //    return;
441
                //if (0 <= value && value < accountCount)
442
                //    _selectedAccountIndex = value;
443
                //else
444
                //    _selectedAccountIndex = 0;
445
                _selectedAccountIndex = value;
446
                NotifyOfPropertyChange(() => CurrentAccount);
447
                NotifyOfPropertyChange(() => CanRemoveAccount);
448
                NotifyOfPropertyChange(()=>SelectedAccountIndex);
449
            }
450
        }*/
451

    
452
        private AccountViewModel _currentAccount;
453
        private readonly IWindowManager _windowManager;
454
        private readonly string _shortcutPath;
455

    
456

    
457
        
458
        public AccountViewModel CurrentAccount
459
        {
460
            get { return _currentAccount; }
461
            set
462
            {
463
                _currentAccount = value;
464
                NotifyOfPropertyChange(()=>CurrentAccount);
465
                NotifyOfPropertyChange(() => CanRemoveAccount);
466
                NotifyOfPropertyChange(() => CanSelectiveSyncFolders);
467
                NotifyOfPropertyChange(() => CanMoveAccountFolder);
468
            }
469
        }
470

    
471
/*
472
        public AccountSettings CurrentAccount
473
        {
474
            get {
475
                if (0 <= SelectedAccountIndex && SelectedAccountIndex < Settings.Accounts.Count)                    
476
                    return Settings.Accounts[SelectedAccountIndex];
477
                return null;
478
            }
479

    
480
        }
481
*/
482

    
483

    
484
        public bool CanMoveAccountFolder
485
        {
486
            get { return CurrentAccount != null; }
487
        }
488

    
489
    public void MoveAccountFolder()
490
    {
491

    
492
        using (var dlg = new FolderBrowserDialog())
493
        {
494
            var currentFolder = CurrentAccount.RootPath;
495
            dlg.SelectedPath = currentFolder;
496
            //Ask the user to select a folder
497
            //Note: We need a parent window here, which we retrieve with GetView            
498
            var view = (Window)GetView();            
499
            if (DialogResult.OK != dlg.ShowDialog(new Wpf32Window(view)))
500
                return;            
501

    
502
            var newPath= dlg.SelectedPath;                
503
            //Find the account's monitor and stop it
504
            PithosMonitor monitor;
505
            if (Shell.Monitors.TryGetValue(CurrentAccount.AccountName, out monitor))
506
            {
507
                monitor.Stop();
508

    
509

    
510
                var oldPath = monitor.RootPath;
511
                //The old directory may not exist eg. if we create an account for the first time
512
                if (Directory.Exists(oldPath))
513
                {
514
                    //If it does, do the move
515

    
516
                    //Now Create all of the directories
517
                    foreach (string dirPath in Directory.EnumerateDirectories(oldPath, "*",
518
                                                           SearchOption.AllDirectories))
519
                        Directory.CreateDirectory(dirPath.Replace(oldPath, newPath));
520

    
521
                    //Copy all the files
522
                    foreach (string newFilePath in Directory.EnumerateFiles(oldPath, "*.*",
523
                                                                            SearchOption.AllDirectories))
524
                        File.Copy(newFilePath, newFilePath.Replace(oldPath, newPath));
525

    
526
                    Directory.Delete(oldPath, true);
527

    
528
                    //We also need to change the path of the existing file states
529
                    monitor.MoveFileStates(oldPath, newPath);
530
                }
531
            }
532
            //Replace the old rootpath with the new
533
            CurrentAccount.RootPath = newPath;
534
            //TODO: This will save all settings changes. Too coarse grained, need to fix at a later date
535
            Settings.Save();            
536
            //And start the monitor on the new RootPath            
537
            if (monitor != null)
538
            {
539
                monitor.RootPath = newPath;
540
                if (CurrentAccount.IsActive)
541
                    monitor.Start();
542
            }
543
            else
544
                Shell.MonitorAccount(CurrentAccount.Account);
545
            //Finally, notify that the Settings, CurrentAccount have changed
546
            NotifyOfPropertyChange(() => CurrentAccount);
547
            NotifyOfPropertyChange(() => Settings);
548

    
549
        }
550
    }
551
    }
552
}