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