Revision c636df1f
b/trunk/Pithos.Client.WPF/Preferences/AccountViewModel.cs | ||
---|---|---|
45 | 45 |
namespace Pithos.Client.WPF.Preferences |
46 | 46 |
{ |
47 | 47 |
/// <summary> |
48 |
/// ViewModel wrapper for an account
|
|
48 |
/// ViewModel wrapper for a settings Account object with change notifications
|
|
49 | 49 |
/// </summary> |
50 | 50 |
public class AccountViewModel:PropertyChangedBase |
51 | 51 |
{ |
... | ... | |
120 | 120 |
set |
121 | 121 |
{ |
122 | 122 |
_account.ApiKey = value; |
123 |
IsExpired = false; |
|
123 | 124 |
NotifyOfPropertyChange(() => ApiKey); |
124 | 125 |
} |
125 | 126 |
} |
b/trunk/Pithos.Client.WPF/Preferences/AddAccountViewModel.cs | ||
---|---|---|
41 | 41 |
#endregion |
42 | 42 |
using System; |
43 | 43 |
using System.Collections.Generic; |
44 |
using System.ComponentModel; |
|
45 | 44 |
using System.ComponentModel.Composition; |
46 |
using System.IO; |
|
47 |
using System.Linq; |
|
48 |
using System.Net; |
|
49 |
using System.Text; |
|
50 | 45 |
using System.Threading.Tasks; |
51 | 46 |
using System.Windows; |
52 |
using System.Windows.Controls; |
|
53 | 47 |
using System.Windows.Forms; |
54 |
using Caliburn.Micro; |
|
55 | 48 |
using Pithos.Client.WPF.Properties; |
56 |
using Pithos.Core; |
|
57 | 49 |
using Pithos.Network; |
58 | 50 |
using MessageBox = System.Windows.MessageBox; |
59 | 51 |
using Screen = Caliburn.Micro.Screen; |
... | ... | |
178 | 170 |
|
179 | 171 |
public void SelectAccount() |
180 | 172 |
{ |
181 |
using (var dlg = new FolderBrowserDialog{Description="Please select a folder to store local files. Pithos will create a new folder called Okeanos under the folder you specify."})
|
|
173 |
using (var dlg = new FolderBrowserDialog{Description=Resources.AddAccountViewModel_SelectAccount_Please_select_a_folder})
|
|
182 | 174 |
{ |
183 | 175 |
//Ask the user to select a folder |
184 | 176 |
//Note: We need a parent window here, which we retrieve with GetView |
... | ... | |
219 | 211 |
{ |
220 | 212 |
ClearBusy(); |
221 | 213 |
|
222 |
(this.GetView() as Window).Activate();
|
|
214 |
((Window) GetView()).Activate();
|
|
223 | 215 |
} |
224 | 216 |
|
225 | 217 |
} |
... | ... | |
310 | 302 |
{ |
311 | 303 |
SetBusy("Validating Credentials", ""); |
312 | 304 |
var client = new CloudFilesClient(AccountName, Token) { AuthenticationUrl = CurrentServer,/*Proxy=Proxy */}; |
313 |
var containers = await TaskEx.Run(() =>
|
|
314 |
{
|
|
315 |
client.Authenticate();
|
|
316 |
return client.ListContainers(AccountName);
|
|
317 |
});
|
|
305 |
await TaskEx.Run(() => |
|
306 |
{ |
|
307 |
client.Authenticate(); |
|
308 |
return client.ListContainers(AccountName); |
|
309 |
}); |
|
318 | 310 |
HasValidCredentials = true; |
319 | 311 |
ValidationMessage = "Credentials Validated"; |
320 | 312 |
} |
321 |
/* |
|
322 |
catch (AggregateException exc) |
|
323 |
{ |
|
324 |
exc.Handle(ex=> |
|
325 |
{ |
|
326 |
HasValidCredentials = false; |
|
327 |
MessageBox.Show("The account is not valid", "Account Error", MessageBoxButton.OK, MessageBoxImage.Stop); |
|
328 |
}); |
|
329 |
} |
|
330 |
*/ |
|
331 | 313 |
catch |
332 | 314 |
{ |
333 | 315 |
HasValidCredentials = false; |
b/trunk/Pithos.Client.WPF/Preferences/PreferencesViewModel.cs | ||
---|---|---|
40 | 40 |
*/ |
41 | 41 |
#endregion |
42 | 42 |
|
43 |
// </copyright> |
|
44 |
// ----------------------------------------------------------------------- |
|
45 | 43 |
|
46 | 44 |
using System.Collections.Concurrent; |
47 | 45 |
using System.ComponentModel.Composition; |
48 | 46 |
using System.Diagnostics; |
49 | 47 |
using System.IO; |
50 |
using System.Net; |
|
51 | 48 |
using System.Threading.Tasks; |
52 | 49 |
using System.Windows; |
53 | 50 |
using System.Windows.Forms; |
54 | 51 |
using Caliburn.Micro; |
55 | 52 |
using Pithos.Client.WPF.Configuration; |
53 |
using Pithos.Client.WPF.Properties; |
|
56 | 54 |
using Pithos.Client.WPF.SelectiveSynch; |
57 | 55 |
using Pithos.Core; |
58 | 56 |
using Pithos.Interfaces; |
... | ... | |
63 | 61 |
namespace Pithos.Client.WPF.Preferences |
64 | 62 |
{ |
65 | 63 |
/// <summary> |
66 |
/// TODO: Update summary. |
|
64 |
/// The preferences screen displays user and account settings and updates the PithosMonitor |
|
65 |
/// classes when account settings change. |
|
67 | 66 |
/// </summary> |
67 |
/// <remarks> |
|
68 |
/// The class is a single ViewModel for all Preferences tabs. It can be broken in separate |
|
69 |
/// ViewModels, one for each tab. |
|
70 |
/// </remarks> |
|
68 | 71 |
[Export] |
69 | 72 |
public class PreferencesViewModel : Screen |
70 | 73 |
{ |
71 |
private IEventAggregator _events; |
|
74 |
private readonly IEventAggregator _events;
|
|
72 | 75 |
|
73 | 76 |
//Logging in the Pithos client is provided by log4net |
74 | 77 |
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger("Pithos"); |
... | ... | |
102 | 105 |
|
103 | 106 |
public PreferencesViewModel(IWindowManager windowManager, IEventAggregator events, ShellViewModel shell, PithosSettings settings, string currentTab) |
104 | 107 |
{ |
108 |
// ReSharper disable DoNotCallOverridableMethodsInConstructor |
|
109 |
//Caliburn.Micro uses DisplayName for the view's title |
|
110 |
DisplayName = "Pithos Preferences"; |
|
111 |
// ReSharper restore DoNotCallOverridableMethodsInConstructor |
|
112 |
|
|
105 | 113 |
_windowManager = windowManager; |
106 | 114 |
_events = events; |
107 | 115 |
|
108 |
DisplayName = "Pithos Preferences"; |
|
109 | 116 |
Shell = shell; |
110 | 117 |
|
111 | 118 |
Settings=settings; |
... | ... | |
193 | 200 |
set |
194 | 201 |
{ |
195 | 202 |
if (value<0) |
196 |
throw new ArgumentOutOfRangeException("value","The Startup Delay must be greater or equal to 0");
|
|
203 |
throw new ArgumentOutOfRangeException("value",Resources.PreferencesViewModel_StartupDelay_Greater_or_equal_to_0);
|
|
197 | 204 |
Settings.StartupDelay = TimeSpan.FromMinutes(value); |
198 | 205 |
NotifyOfPropertyChange(()=>StartupDelay); |
199 | 206 |
} |
... | ... | |
222 | 229 |
{ |
223 | 230 |
_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 }); |
224 | 231 |
|
225 |
//var userName = CurrentAccount.AccountName; |
|
226 | 232 |
try |
227 | 233 |
{ |
228 | 234 |
|
229 | 235 |
var credentials = await PithosAccount.RetrieveCredentials(Settings.PithosLoginUrl); |
230 |
|
|
236 |
//The server will return credentials for a different account, not just the current account |
|
237 |
//We need to find the correct account first |
|
231 | 238 |
var account = Accounts.First(act => act.AccountName == credentials.UserName); |
232 |
//The server may return credentials for a different account |
|
233 |
var monitor = Shell.Monitors[account.AccountName]; |
|
234 |
account.ApiKey = credentials.Password; |
|
235 |
monitor.ApiKey = credentials.Password; |
|
239 |
account.ApiKey = credentials.Password; |
|
236 | 240 |
account.IsExpired = false; |
237 | 241 |
Settings.Save(); |
238 | 242 |
TaskEx.Delay(10000).ContinueWith(_ =>Shell.MonitorAccount(account.Account)); |
... | ... | |
334 | 338 |
IsActive=wizard.IsAccountActive |
335 | 339 |
}; |
336 | 340 |
Settings.Accounts.Add(newAccount); |
337 |
var accountVM = new AccountViewModel(newAccount);
|
|
338 |
(Accounts as IProducerConsumerCollection<AccountViewModel>).TryAdd(accountVM);
|
|
339 |
CurrentAccount = accountVM;
|
|
341 |
var accountVm = new AccountViewModel(newAccount);
|
|
342 |
(Accounts as IProducerConsumerCollection<AccountViewModel>).TryAdd(accountVm);
|
|
343 |
CurrentAccount = accountVm;
|
|
340 | 344 |
NotifyOfPropertyChange(() => Accounts); |
341 | 345 |
NotifyOfPropertyChange(() => Settings); |
342 | 346 |
} |
... | ... | |
441 | 445 |
}*/ |
442 | 446 |
|
443 | 447 |
private AccountViewModel _currentAccount; |
444 |
private IWindowManager _windowManager; |
|
445 |
private string _shortcutPath; |
|
448 |
private readonly IWindowManager _windowManager;
|
|
449 |
private readonly string _shortcutPath;
|
|
446 | 450 |
|
447 | 451 |
|
448 | 452 |
|
... | ... | |
517 | 521 |
Directory.Delete(oldPath, true); |
518 | 522 |
|
519 | 523 |
//We also need to change the path of the existing file states |
520 |
if (monitor != null) |
|
521 |
monitor.MoveFileStates(oldPath, newPath); |
|
524 |
monitor.MoveFileStates(oldPath, newPath); |
|
522 | 525 |
} |
523 | 526 |
} |
524 | 527 |
//Replace the old rootpath with the new |
b/trunk/Pithos.Client.WPF/Properties/Resources.Designer.cs | ||
---|---|---|
1 | 1 |
//------------------------------------------------------------------------------ |
2 | 2 |
// <auto-generated> |
3 | 3 |
// This code was generated by a tool. |
4 |
// Runtime Version:4.0.30319.235
|
|
4 |
// Runtime Version:4.0.30319.488
|
|
5 | 5 |
// |
6 | 6 |
// Changes to this file may cause incorrect behavior and will be lost if |
7 | 7 |
// the code is regenerated. |
... | ... | |
59 | 59 |
resourceCulture = value; |
60 | 60 |
} |
61 | 61 |
} |
62 |
|
|
63 |
/// <summary> |
|
64 |
/// Looks up a localized string similar to Please select a folder to store local files. Pithos will create a new folder called Okeanos under the folder you specify.. |
|
65 |
/// </summary> |
|
66 |
internal static string AddAccountViewModel_SelectAccount_Please_select_a_folder { |
|
67 |
get { |
|
68 |
return ResourceManager.GetString("AddAccountViewModel_SelectAccount_Please_select_a_folder", resourceCulture); |
|
69 |
} |
|
70 |
} |
|
71 |
|
|
72 |
/// <summary> |
|
73 |
/// Looks up a localized string similar to The Startup Delay must be greater or equal to 0. |
|
74 |
/// </summary> |
|
75 |
internal static string PreferencesViewModel_StartupDelay_Greater_or_equal_to_0 { |
|
76 |
get { |
|
77 |
return ResourceManager.GetString("PreferencesViewModel_StartupDelay_Greater_or_equal_to_0", resourceCulture); |
|
78 |
} |
|
79 |
} |
|
62 | 80 |
} |
63 | 81 |
} |
b/trunk/Pithos.Client.WPF/Properties/Resources.resx | ||
---|---|---|
114 | 114 |
<resheader name="writer"> |
115 | 115 |
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> |
116 | 116 |
</resheader> |
117 |
<data name="PreferencesViewModel_StartupDelay_Greater_or_equal_to_0"> |
|
118 |
<value xml:space="preserve">The Startup Delay must be greater or equal to 0</value> |
|
119 |
</data> |
|
120 |
<data name="AddAccountViewModel_SelectAccount_Please_select_a_folder"> |
|
121 |
<value xml:space="preserve">Please select a folder to store local files. Pithos will create a new folder called Okeanos under the folder you specify.</value> |
|
122 |
</data> |
|
117 | 123 |
</root> |
b/trunk/Pithos.Client.WPF/Shell/ShellViewModel.cs | ||
---|---|---|
232 | 232 |
if (_monitors.TryGetValue(accountName, out monitor)) |
233 | 233 |
{ |
234 | 234 |
//If the account is active |
235 |
if (account.IsActive) |
|
236 |
//Start the monitor. It's OK to start an already started monitor, |
|
237 |
//it will just ignore the call |
|
238 |
StartMonitor(monitor).Wait(); |
|
239 |
else |
|
240 |
{ |
|
241 |
//If the account is inactive |
|
242 |
//Stop and remove the monitor |
|
243 |
RemoveMonitor(accountName); |
|
244 |
} |
|
235 |
if (account.IsActive) |
|
236 |
{ |
|
237 |
//The Api Key may have changed throuth the Preferences dialog |
|
238 |
monitor.ApiKey = account.ApiKey; |
|
239 |
Debug.Assert(monitor.StatusNotification == this,"An existing monitor should already have a StatusNotification service object"); |
|
240 |
monitor.RootPath = account.RootPath; |
|
241 |
//Start the monitor. It's OK to start an already started monitor, |
|
242 |
//it will just ignore the call |
|
243 |
StartMonitor(monitor).Wait(); |
|
244 |
} |
|
245 |
else |
|
246 |
{ |
|
247 |
//If the account is inactive |
|
248 |
//Stop and remove the monitor |
|
249 |
RemoveMonitor(accountName); |
|
250 |
} |
|
245 | 251 |
return; |
246 | 252 |
} |
247 | 253 |
|
b/trunk/Pithos.Core/PithosMonitor.cs | ||
---|---|---|
40 | 40 |
*/ |
41 | 41 |
#endregion |
42 | 42 |
using System; |
43 |
using System.Collections.Concurrent; |
|
44 | 43 |
using System.Collections.Generic; |
45 | 44 |
using System.ComponentModel.Composition; |
46 |
using System.Diagnostics; |
|
47 | 45 |
using System.Diagnostics.Contracts; |
48 | 46 |
using System.IO; |
49 | 47 |
using System.Linq; |
50 |
using System.Net; |
|
51 |
using System.Net.NetworkInformation; |
|
52 |
using System.Security.Cryptography; |
|
53 |
using System.ServiceModel.Description; |
|
54 |
using System.Text; |
|
55 | 48 |
using System.Threading; |
56 | 49 |
using System.Threading.Tasks; |
57 |
using Castle.ActiveRecord.Queries; |
|
58 |
using Microsoft.WindowsAPICodePack.Net; |
|
59 | 50 |
using Pithos.Core.Agents; |
60 | 51 |
using Pithos.Interfaces; |
61 |
using System.ServiceModel; |
|
62 | 52 |
using Pithos.Network; |
63 | 53 |
using log4net; |
64 | 54 |
|
... | ... | |
211 | 201 |
return; |
212 | 202 |
} |
213 | 203 |
_cancellationSource = new CancellationTokenSource(); |
214 |
|
|
215 | 204 |
|
216 |
CloudClient=new CloudFilesClient(UserName,ApiKey); |
|
217 |
CloudClient.UsePithos = true; |
|
218 |
CloudClient.AuthenticationUrl = this.AuthenticationUrl; |
|
205 |
|
|
206 |
CloudClient = new CloudFilesClient(UserName, ApiKey) |
|
207 |
{UsePithos = true, AuthenticationUrl = AuthenticationUrl}; |
|
208 |
|
|
219 | 209 |
|
220 | 210 |
_accountInfo = CloudClient.Authenticate(); |
221 | 211 |
_accountInfo.SiteUri = AuthenticationUrl; |
... | ... | |
259 | 249 |
var pithosContainers = new List<string>{ FolderConstants.TrashContainer,FolderConstants.PithosContainer}; |
260 | 250 |
foreach (var container in pithosContainers) |
261 | 251 |
{ |
262 |
var info=CloudClient.GetContainerInfo(this.UserName, container);
|
|
252 |
var info=CloudClient.GetContainerInfo(UserName, container); |
|
263 | 253 |
if (info == ContainerInfo.Empty) |
264 | 254 |
{ |
265 |
CloudClient.CreateContainer(this.UserName, container);
|
|
266 |
info = CloudClient.GetContainerInfo(this.UserName, container);
|
|
255 |
CloudClient.CreateContainer(UserName, container); |
|
256 |
info = CloudClient.GetContainerInfo(UserName, container); |
|
267 | 257 |
} |
268 | 258 |
_blockSize = info.BlockSize; |
269 | 259 |
_blockHash = info.BlockHash; |
... | ... | |
276 | 266 |
|
277 | 267 |
private void IndexLocalFiles() |
278 | 268 |
{ |
279 |
StatusNotification.NotifyChange("Indexing Local Files",TraceLevel.Info);
|
|
280 |
using (log4net.ThreadContext.Stacks["Monitor"].Push("Indexing local files"))
|
|
269 |
StatusNotification.NotifyChange("Indexing Local Files"); |
|
270 |
using (ThreadContext.Stacks["Monitor"].Push("Indexing local files")) |
|
281 | 271 |
{ |
282 | 272 |
Log.Info("START"); |
283 | 273 |
try |
... | ... | |
359 | 349 |
|
360 | 350 |
public override int GetHashCode(CloudAction obj) |
361 | 351 |
{ |
352 |
if (obj == null) |
|
353 |
return 0; |
|
362 | 354 |
var hash1 = (obj.LocalFile == null) ? int.MaxValue : obj.LocalFile.FullName.GetHashCode(); |
363 | 355 |
var hash2 = (obj.CloudFile == null) ? int.MaxValue : (obj.CloudFile.Hash ?? obj.CloudFile.Name??"").GetHashCode(); |
364 | 356 |
var hash3 = obj.Action.GetHashCode(); |
... | ... | |
366 | 358 |
} |
367 | 359 |
} |
368 | 360 |
|
369 |
private Timer timer; |
|
370 |
|
|
371 | 361 |
private void StartNetworkAgent() |
372 | 362 |
{ |
373 | 363 |
|
... | ... | |
385 | 375 |
} |
386 | 376 |
|
387 | 377 |
//Make sure a hidden cache folder exists to store partial downloads |
388 |
private static string CreateHiddenFolder(string rootPath, string folderName)
|
|
378 |
private static void CreateHiddenFolder(string rootPath, string folderName)
|
|
389 | 379 |
{ |
390 | 380 |
if (String.IsNullOrWhiteSpace(rootPath)) |
391 | 381 |
throw new ArgumentNullException("rootPath"); |
... | ... | |
412 | 402 |
Log.InfoFormat("Reset cache folder to hidden: {0}", folder); |
413 | 403 |
} |
414 | 404 |
} |
415 |
return folder; |
|
416 | 405 |
} |
417 | 406 |
|
418 | 407 |
|
... | ... | |
435 | 424 |
if (FileAgent!=null) |
436 | 425 |
FileAgent.Stop(); |
437 | 426 |
FileAgent = null; |
438 |
if (timer != null) |
|
439 |
timer.Dispose(); |
|
440 |
timer = null; |
|
441 | 427 |
} |
442 | 428 |
|
443 | 429 |
|
... | ... | |
545 | 531 |
//Create the root URL for the target account |
546 | 532 |
var oldName = UserName; |
547 | 533 |
var absoluteUri = _accountInfo.StorageUri.AbsoluteUri; |
548 |
var nameIndex=absoluteUri.IndexOf(oldName); |
|
534 |
var nameIndex=absoluteUri.IndexOf(oldName, StringComparison.Ordinal);
|
|
549 | 535 |
var root=absoluteUri.Substring(0, nameIndex); |
550 | 536 |
|
551 | 537 |
accountInfo = new AccountInfo |
... | ... | |
560 | 546 |
} |
561 | 547 |
else |
562 | 548 |
{ |
563 |
accountName = this.UserName;
|
|
549 |
accountName = UserName; |
|
564 | 550 |
container = parts[0]; |
565 | 551 |
relativeUrl = String.Join("/", parts.Splice(1)); |
566 | 552 |
} |
... | ... | |
591 | 577 |
//Create the root URL for the target account |
592 | 578 |
var oldName = UserName; |
593 | 579 |
var absoluteUri = _accountInfo.StorageUri.AbsoluteUri; |
594 |
var nameIndex=absoluteUri.IndexOf(oldName); |
|
580 |
var nameIndex=absoluteUri.IndexOf(oldName, StringComparison.Ordinal);
|
|
595 | 581 |
var root=absoluteUri.Substring(0, nameIndex); |
596 | 582 |
|
597 | 583 |
accountInfo = new AccountInfo |
Also available in: Unified diff