Revision 0bd56b7c
b/trunk/Pithos.Client.WPF/AppBootstrapper.cs | ||
---|---|---|
1 | 1 |
using System.Windows; |
2 |
using System.Windows.Controls; |
|
2 | 3 |
using System.Windows.Navigation; |
3 | 4 |
using Caliburn.Micro; |
4 | 5 |
using Caliburn.Micro.Logging; |
... | ... | |
56 | 57 |
|
57 | 58 |
|
58 | 59 |
container.Compose(batch); |
60 |
|
|
61 |
ConventionManager.AddElementConvention<MenuItem>(ItemsControl.ItemsSourceProperty, "DataContext", "Click"); |
|
59 | 62 |
} |
60 | 63 |
|
61 | 64 |
protected override object GetInstance(Type serviceType, string key) |
b/trunk/Pithos.Client.WPF/ShellView.xaml | ||
---|---|---|
3 | 3 |
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
4 | 4 |
xmlns:tb="clr-namespace:Hardcodet.Wpf.TaskbarNotification;assembly=Hardcodet.Wpf.TaskbarNotification" |
5 | 5 |
xmlns:cal="http://www.caliburnproject.org" |
6 |
x:Name="TheView" |
|
6 | 7 |
> |
7 | 8 |
|
8 | 9 |
<Window.Resources> |
... | ... | |
27 | 28 |
<tb:TaskbarIcon.ContextMenu> |
28 | 29 |
<ContextMenu x:Name="TaskbarMenu" > |
29 | 30 |
<MenuItem Header="Open PITHOS Folder" x:Name="OpenPithosFolder" cal:Message.Attach="OpenPithosFolder" FontWeight="Bold" /> |
30 |
<MenuItem Header="Launch PITHOS Site" x:Name="GoToSite" cal:Message.Attach="GoToSite" /> |
|
31 |
<MenuItem Header="Go to Account Site" x:Name="GoToSiteMenu" ItemsSource="{Binding Accounts}" > |
|
32 |
<MenuItem.ItemTemplate> |
|
33 |
<DataTemplate> |
|
34 |
<TextBlock x:Name="AccountLink" Text="{Binding Path=UserName}" cal:Message.Attach="[Event MouseLeftButtonUp]=[Action GoToSite($dataContext)]" |
|
35 |
cal:Action.TargetWithoutContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=MenuItem, AncestorLevel=2}, Path=DataContext}" /> |
|
36 |
</DataTemplate> |
|
37 |
</MenuItem.ItemTemplate> |
|
38 |
</MenuItem> |
|
39 |
|
|
31 | 40 |
<MenuItem Header="Recently Changed Files" x:Name="RecentFiles" ItemsSource="{Binding RecentFiles}"> |
32 | 41 |
<MenuItem.ItemTemplate> |
33 | 42 |
<DataTemplate> |
... | ... | |
35 | 44 |
</DataTemplate> |
36 | 45 |
</MenuItem.ItemTemplate> |
37 | 46 |
</MenuItem> |
38 |
<Separator /> |
|
39 |
<MenuItem Header="{Binding UsageMessage}" x:Name="UsageMessage" /> |
|
47 |
<Separator /> |
|
48 |
<MenuItem Header="Usage" x:Name="Usages" ItemsSource="{Binding Accounts}"> |
|
49 |
<MenuItem.ItemTemplate> |
|
50 |
<DataTemplate> |
|
51 |
<StackPanel Orientation="Horizontal"> |
|
52 |
<TextBlock Text="{Binding UserName}" /> |
|
53 |
<TextBlock Text=":" Margin="5,0"/> |
|
54 |
<TextBlock Text="{Binding Usage}" /> |
|
55 |
</StackPanel> |
|
56 |
</DataTemplate> |
|
57 |
</MenuItem.ItemTemplate> |
|
58 |
</MenuItem> |
|
40 | 59 |
<Separator /> |
41 | 60 |
<MenuItem Header="{Binding StatusMessage}" x:Name="StatusMessage" /> |
42 | 61 |
<Separator /> |
b/trunk/Pithos.Client.WPF/ShellViewModel.cs | ||
---|---|---|
20 | 20 |
using System.Collections.Generic; |
21 | 21 |
using System.Linq; |
22 | 22 |
using System.Text; |
23 |
using Pithos.Network; |
|
23 | 24 |
using StatusService = Pithos.Client.WPF.Services.StatusService; |
24 | 25 |
|
25 | 26 |
namespace Pithos.Client.WPF { |
... | ... | |
59 | 60 |
|
60 | 61 |
Settings = settings; |
61 | 62 |
|
62 |
UsageMessage = "Using 15% of 50 GB"; |
|
63 | 63 |
StatusMessage = "In Synch"; |
64 | 64 |
|
65 | 65 |
foreach (var account in settings.Accounts) |
... | ... | |
144 | 144 |
} |
145 | 145 |
} |
146 | 146 |
|
147 |
private string _usageMessage;
|
|
148 |
public string UsageMessage
|
|
147 |
private ObservableConcurrentCollection<AccountInfo> _accounts = new ObservableConcurrentCollection<AccountInfo>();
|
|
148 |
public ObservableConcurrentCollection<AccountInfo> Accounts
|
|
149 | 149 |
{ |
150 |
get { return _usageMessage; } |
|
151 |
set |
|
152 |
{ |
|
153 |
_usageMessage = value; |
|
154 |
NotifyOfPropertyChange(() => UsageMessage); |
|
155 |
} |
|
150 |
get { return _accounts; } |
|
156 | 151 |
} |
157 | 152 |
|
158 | 153 |
|
... | ... | |
207 | 202 |
|
208 | 203 |
public void GoToSite() |
209 | 204 |
{ |
210 |
var activeAccount=Settings.Accounts.FirstOrDefault(account => account.IsActive); |
|
211 |
if (activeAccount == null) |
|
212 |
return; |
|
205 |
|
|
206 |
} |
|
213 | 207 |
|
208 |
public void GoToSite(AccountInfo account) |
|
209 |
{ |
|
214 | 210 |
var site = String.Format("{0}/ui/?token={1}&user={2}", |
215 |
Properties.Settings.Default.PithosSite,activeAccount.ApiKey,
|
|
216 |
activeAccount.AccountName);
|
|
211 |
Properties.Settings.Default.PithosSite,account.Token,
|
|
212 |
account.UserName);
|
|
217 | 213 |
Process.Start(site); |
218 | 214 |
} |
219 | 215 |
|
... | ... | |
389 | 385 |
files.TryAdd(entry); |
390 | 386 |
} |
391 | 387 |
|
388 |
public void NotifyAccount(AccountInfo account) |
|
389 |
{ |
|
390 |
if (account== null) |
|
391 |
return; |
|
392 |
|
|
393 |
account.SiteUri= String.Format("{0}/ui/?token={1}&user={2}", |
|
394 |
Properties.Settings.Default.PithosSite, account.Token, |
|
395 |
account.UserName); |
|
396 |
|
|
397 |
IProducerConsumerCollection<AccountInfo> accounts = Accounts; |
|
398 |
for (var i = 0; i < _accounts.Count; i++) |
|
399 |
{ |
|
400 |
AccountInfo item; |
|
401 |
if (accounts.TryTake(out item)) |
|
402 |
{ |
|
403 |
if (item.UserName!=account.UserName) |
|
404 |
{ |
|
405 |
accounts.TryAdd(item); |
|
406 |
} |
|
407 |
} |
|
408 |
} |
|
409 |
|
|
410 |
accounts.TryAdd(account); |
|
411 |
} |
|
412 |
|
|
392 | 413 |
|
393 | 414 |
public void RemoveMonitor(string accountName) |
394 | 415 |
{ |
b/trunk/Pithos.Core/Agents/BlockUpdater.cs | ||
---|---|---|
22 | 22 |
|
23 | 23 |
public string TempPath { get; private set; } |
24 | 24 |
|
25 |
public bool HasBlocks |
|
26 |
{ |
|
27 |
get { return _blocks.Count>0; } |
|
28 |
} |
|
29 |
|
|
25 | 30 |
readonly ConcurrentDictionary<int, string> _blocks = new ConcurrentDictionary<int, string>(); |
26 | 31 |
readonly ConcurrentDictionary<string, string> _orphanBlocks = new ConcurrentDictionary<string, string>(); |
27 | 32 |
|
b/trunk/Pithos.Core/Agents/NetworkAgent.cs | ||
---|---|---|
318 | 318 |
client.ListObjects(accountInfo.UserName, FolderConstants.TrashContainer, since)); |
319 | 319 |
|
320 | 320 |
var listShared = Task<IList<ObjectInfo>>.Factory.StartNew(() => |
321 |
client.ListSharedObjects()); |
|
321 |
client.ListSharedObjects(since));
|
|
322 | 322 |
|
323 | 323 |
var listAll = Task.Factory.TrackedSequence( |
324 | 324 |
() => listObjects, |
... | ... | |
584 | 584 |
//Now we can store the object's metadata without worrying about ghost status entries |
585 | 585 |
StatusKeeper.StoreInfo(localPath, cloudFile); |
586 | 586 |
|
587 |
//Notify listeners that a local file has changed |
|
588 |
StatusNotification.NotifyChangedFile(localPath); |
|
589 |
|
|
590 | 587 |
} |
591 | 588 |
} |
592 | 589 |
|
... | ... | |
643 | 640 |
File.Replace(tempPath,localPath,null,true); |
644 | 641 |
else |
645 | 642 |
File.Move(tempPath,localPath); |
643 |
//Notify listeners that a local file has changed |
|
644 |
StatusNotification.NotifyChangedFile(localPath); |
|
645 |
|
|
646 | 646 |
}); |
647 | 647 |
return getObject; |
648 | 648 |
} |
... | ... | |
730 | 730 |
} |
731 | 731 |
} |
732 | 732 |
|
733 |
//Want to avoid notifications if no changes were made |
|
734 |
var hasChanges = blockUpdater.HasBlocks; |
|
733 | 735 |
blockUpdater.Commit(); |
736 |
|
|
737 |
if (hasChanges) |
|
738 |
//Notify listeners that a local file has changed |
|
739 |
StatusNotification.NotifyChangedFile(localPath); |
|
740 |
|
|
734 | 741 |
Log.InfoFormat("[BLOCK GET] COMPLETE {0}", localPath); |
735 | 742 |
} |
736 | 743 |
|
b/trunk/Pithos.Core/PithosMonitor.cs | ||
---|---|---|
53 | 53 |
public string UserName { get; set; } |
54 | 54 |
public string ApiKey { get; set; } |
55 | 55 |
|
56 |
private Network.AccountInfo _accountInfo;
|
|
56 |
private AccountInfo _accountInfo; |
|
57 | 57 |
|
58 | 58 |
|
59 | 59 |
private static readonly ILog Log = LogManager.GetLogger(typeof(PithosMonitor)); |
... | ... | |
121 | 121 |
_accountInfo = CloudClient.Authenticate(); |
122 | 122 |
_accountInfo.AccountPath = RootPath; |
123 | 123 |
|
124 |
var policy=CloudClient.GetAccountPolicies(_accountInfo); |
|
124 | 125 |
|
126 |
StatusNotification.NotifyAccount(policy); |
|
125 | 127 |
EnsurePithosContainers(); |
126 | 128 |
|
127 | 129 |
StatusKeeper.BlockHash = _blockHash; |
... | ... | |
386 | 388 |
} |
387 | 389 |
} |
388 | 390 |
|
391 |
|
|
389 | 392 |
public interface IStatusNotification |
390 | 393 |
{ |
391 | 394 |
void NotifyChange(string status,TraceLevel level=TraceLevel.Info); |
392 | 395 |
void NotifyChangedFile(string filePath); |
396 |
void NotifyAccount(AccountInfo policy); |
|
393 | 397 |
} |
394 | 398 |
} |
b/trunk/Pithos.Network/AccountInfo.cs | ||
---|---|---|
18 | 18 |
public int BlockSize { get; set; } |
19 | 19 |
|
20 | 20 |
public string BlockHash { get; set; } |
21 |
|
|
22 |
public long Quota { get; set; } |
|
23 |
|
|
24 |
public long BytesUsed { get; set; } |
|
25 |
|
|
26 |
|
|
27 |
public string Usage |
|
28 |
{ |
|
29 |
get |
|
30 |
{ |
|
31 |
var gigabytes = Quota / 1073741824; |
|
32 |
var percentage = BytesUsed / (double)Quota; |
|
33 |
return String.Format("{0:P0} of {1} GB", percentage, gigabytes); |
|
34 |
} |
|
35 |
|
|
36 |
} |
|
37 |
|
|
38 |
public string SiteUri { get; set; } |
|
21 | 39 |
} |
22 | 40 |
} |
b/trunk/Pithos.Network/CloudFilesClient.cs | ||
---|---|---|
6 | 6 |
using System; |
7 | 7 |
using System.Collections.Generic; |
8 | 8 |
using System.ComponentModel.Composition; |
9 |
using System.Diagnostics; |
|
10 | 9 |
using System.Diagnostics.Contracts; |
11 |
using System.Globalization; |
|
12 | 10 |
using System.IO; |
13 | 11 |
using System.Linq; |
14 | 12 |
using System.Net; |
15 | 13 |
using System.Security.Cryptography; |
16 | 14 |
using System.Text; |
17 |
using System.Threading.Algorithms; |
|
18 | 15 |
using System.Threading.Tasks; |
19 | 16 |
using Newtonsoft.Json; |
20 | 17 |
using Pithos.Interfaces; |
21 | 18 |
using log4net; |
22 |
using WebHeaderCollection = System.Net.WebHeaderCollection; |
|
23 | 19 |
|
24 | 20 |
namespace Pithos.Network |
25 | 21 |
{ |
... | ... | |
176 | 172 |
} |
177 | 173 |
|
178 | 174 |
Log.InfoFormat("[AUTHENTICATE] End for {0}", UserName); |
175 |
|
|
179 | 176 |
|
180 | 177 |
return new AccountInfo {StorageUri = StorageUrl, Token = Token, UserName = UserName}; |
181 | 178 |
|
... | ... | |
185 | 182 |
|
186 | 183 |
public IList<ContainerInfo> ListContainers(string account) |
187 | 184 |
{ |
188 |
|
|
189 | 185 |
using (var client = new RestClient(_baseClient)) |
190 | 186 |
{ |
191 | 187 |
if (!String.IsNullOrWhiteSpace(account)) |
... | ... | |
253 | 249 |
var accounts = ListSharingAccounts(since); |
254 | 250 |
foreach (var account in accounts) |
255 | 251 |
{ |
256 |
//Skip the account if it hasn't been modified |
|
257 |
if (account.last_modified < since) |
|
258 |
continue; |
|
259 |
|
|
260 | 252 |
var containers = ListContainers(account.name); |
261 | 253 |
foreach (var container in containers) |
262 | 254 |
{ |
263 |
var containerObjects = ListObjects(account.name, container.Name, since);
|
|
255 |
var containerObjects = ListObjects(account.name, container.Name, null);
|
|
264 | 256 |
objects.AddRange(containerObjects); |
265 | 257 |
} |
266 | 258 |
} |
... | ... | |
321 | 313 |
|
322 | 314 |
} |
323 | 315 |
|
316 |
public AccountInfo GetAccountPolicies(AccountInfo accountInfo) |
|
317 |
{ |
|
318 |
if (accountInfo==null) |
|
319 |
throw new ArgumentNullException("accountInfo"); |
|
320 |
Contract.EndContractBlock(); |
|
321 |
|
|
322 |
using (log4net.ThreadContext.Stacks["Account"].Push("GetPolicies")) |
|
323 |
{ |
|
324 |
if (Log.IsDebugEnabled) Log.DebugFormat("START"); |
|
325 |
|
|
326 |
using (var client = new RestClient(_baseClient)) |
|
327 |
{ |
|
328 |
if (!String.IsNullOrWhiteSpace(accountInfo.UserName)) |
|
329 |
client.BaseAddress = GetAccountUrl(accountInfo.UserName); |
|
330 |
|
|
331 |
client.Parameters.Clear(); |
|
332 |
client.Parameters.Add("format", "json"); |
|
333 |
client.Head(String.Empty, 3); |
|
334 |
|
|
335 |
var quotaValue=client.ResponseHeaders["X-Account-Policy-Quota"]; |
|
336 |
var bytesValue= client.ResponseHeaders["X-Account-Bytes-Used"]; |
|
337 |
|
|
338 |
long quota, bytes; |
|
339 |
if (long.TryParse(quotaValue, out quota)) |
|
340 |
accountInfo.Quota = quota; |
|
341 |
if (long.TryParse(bytesValue, out bytes)) |
|
342 |
accountInfo.BytesUsed = bytes; |
|
343 |
|
|
344 |
return accountInfo; |
|
345 |
|
|
346 |
} |
|
347 |
|
|
348 |
} |
|
349 |
} |
|
350 |
|
|
324 | 351 |
|
325 | 352 |
public IList<ObjectInfo> ListObjects(string account, string container, DateTime? since = null) |
326 | 353 |
{ |
... | ... | |
1028 | 1055 |
|
1029 | 1056 |
private static string CalculateHash(string fileName) |
1030 | 1057 |
{ |
1058 |
Contract.Requires(!String.IsNullOrWhiteSpace(fileName)); |
|
1059 |
Contract.EndContractBlock(); |
|
1060 |
|
|
1031 | 1061 |
string hash; |
1032 | 1062 |
using (var hasher = MD5.Create()) |
1033 | 1063 |
using(var stream=File.OpenRead(fileName)) |
... | ... | |
1039 | 1069 |
} |
1040 | 1070 |
return hash; |
1041 | 1071 |
} |
1042 |
|
|
1043 |
/* public void DeleteObject(string container, string objectName,string account) |
|
1044 |
{ |
|
1045 |
if (String.IsNullOrWhiteSpace(container)) |
|
1046 |
throw new ArgumentNullException("container", "The container property can't be empty"); |
|
1047 |
if (String.IsNullOrWhiteSpace(objectName)) |
|
1048 |
throw new ArgumentNullException("objectName", "The objectName property can't be empty"); |
|
1049 |
Contract.EndContractBlock(); |
|
1050 |
|
|
1051 |
using (var client = new RestClient(_baseClient)) |
|
1052 |
{ |
|
1053 |
if (!String.IsNullOrWhiteSpace(account)) |
|
1054 |
client.BaseAddress = GetAccountUrl(account); |
|
1055 |
); |
|
1056 |
client.DeleteWithRetry(container + "/" + objectName, 3); |
|
1057 |
|
|
1058 |
var expectedCodes = new[] {HttpStatusCode.NotFound, HttpStatusCode.NoContent}; |
|
1059 |
if (!expectedCodes.Contains(client.StatusCode)) |
|
1060 |
throw CreateWebException("DeleteObject", client.StatusCode); |
|
1061 |
} |
|
1062 |
|
|
1063 |
}*/ |
|
1064 |
|
|
1072 |
|
|
1065 | 1073 |
public void MoveObject(string account, string sourceContainer, string oldObjectName, string targetContainer, string newObjectName) |
1066 | 1074 |
{ |
1067 | 1075 |
if (String.IsNullOrWhiteSpace(sourceContainer)) |
b/trunk/Pithos.Network/ICloudClient.cs | ||
---|---|---|
57 | 57 |
void ShareObject(string account, string container, string objectName, string shareTo, bool read, bool write); |
58 | 58 |
|
59 | 59 |
#endregion |
60 |
|
|
61 |
AccountInfo GetAccountPolicies(AccountInfo accountInfo); |
|
60 | 62 |
} |
61 | 63 |
|
62 | 64 |
|
... | ... | |
115 | 117 |
|
116 | 118 |
} |
117 | 119 |
|
120 |
public AccountInfo GetAccountPolicies(AccountInfo accountInfo) |
|
121 |
{ |
|
122 |
Contract.Requires(accountInfo!=null); |
|
123 |
|
|
124 |
return default(AccountInfo); |
|
125 |
} |
|
126 |
|
|
118 | 127 |
|
119 | 128 |
public IList<ObjectInfo> ListObjects(string account, string container, DateTime? since = null) |
120 | 129 |
{ |
Also available in: Unified diff