Revision c53aa229

b/trunk/Pithos.Client.WPF/Pithos.Client.WPF.csproj
121 121
    </Reference>
122 122
    <Reference Include="System.Drawing" />
123 123
    <Reference Include="System.Runtime.Serialization" />
124
    <Reference Include="System.ServiceModel" />
124 125
    <Reference Include="System.Windows.Forms" />
125 126
    <Reference Include="System.Windows.Interactivity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
126 127
      <HintPath>..\packages\Caliburn.Micro.1.2.0\lib\Net40\System.Windows.Interactivity.dll</HintPath>
......
161 162
      <DependentUpon>PreferencesView.xaml</DependentUpon>
162 163
    </Compile>
163 164
    <Compile Include="PreferencesViewModel.cs" />
164
    <Compile Include="TaskbarViewModel.cs" />
165
    <Compile Include="Services\Events.cs" />
166
    <Compile Include="ShellViewModel.cs" />
167
    <Compile Include="Services\StatusService.cs" />
165 168
    <Compile Include="Wpf32Window.cs" />
166 169
    <Page Include="FilePropertiesView.xaml">
167 170
      <SubType>Designer</SubType>
......
189 192
      <SubType>Designer</SubType>
190 193
      <Generator>MSBuild:Compile</Generator>
191 194
    </Page>
195
    <Page Include="ShellView.xaml">
196
      <Generator>MSBuild:Compile</Generator>
197
      <SubType>Designer</SubType>
198
    </Page>
192 199
  </ItemGroup>
193 200
  <ItemGroup>
194 201
    <Compile Include="Properties\AssemblyInfo.cs">
b/trunk/Pithos.Client.WPF/PreferencesView.xaml
31 31
            <RowDefinition Height="Auto"/>
32 32
        </Grid.RowDefinitions>
33 33

  
34
        <tb:TaskbarIcon x:Name="TaskbarView"                           
35
                        IconSource="{Binding StatusIcon}" 
36
                        ToolTipText="{Binding StatusMessage}"
37
                        DataContext="{Binding Taskbar}"
38
                        MenuActivation="LeftOrRightClick" 
39
                        DoubleClickCommand="{Binding DataContext.Taskbar.OpenPithosFolderCommand,ElementName=TheView}"                         
40
                        cal:Bind.Model="{Binding DataContext.Taskbar,ElementName=TheView}">
41
            <tb:TaskbarIcon.ContextMenu>
42
                <ContextMenu x:Name="TaskbarMenu" >
43
                    <MenuItem Header="Open PITHOS Folder" x:Name="OpenPithosFolder" cal:Message.Attach="OpenPithosFolder" FontWeight="Bold" />
44
                    <MenuItem Header="Launch PITHOS Site"  x:Name="GoToSite" cal:Message.Attach="GoToSite" />
45
                    <MenuItem Header="Recently Changed Files" x:Name="Taskbar_RecentFiles" ItemsSource="{Binding RecentFiles}">
46
                            <MenuItem.ItemTemplate>
47
                                <DataTemplate>
48
                                    <TextBlock Text="{Binding FileName}"/>
49
                                </DataTemplate>
50
                            </MenuItem.ItemTemplate>
51
                    </MenuItem>
52
                    <Separator  />
53
                    <MenuItem Header="{Binding UsageMessage}" x:Name="UsageMessage" />
54
                    <Separator  />
55
                    <MenuItem Header="{Binding StatusMessage}" x:Name="StatusMessage" />
56
                    <Separator  />
57
                    <MenuItem  Header="{Binding PauseSyncCaption}" x:Name="ToggleSynching" cal:Message.Attach="ToggleSynching"  />
58
                    <Separator  />
59
                    <MenuItem  Header="Preferences ..." x:Name="ShowPreferences"  Click="ShowPreferences_Click" cal:Message.Attach="ShowPreferences" />
60
                    <Separator  />
61
                    <MenuItem  Header="Exit" Name="ExitPithos" cal:Message.Attach="ExitPithos" />
62
                </ContextMenu>
63
            </tb:TaskbarIcon.ContextMenu>
64
        </tb:TaskbarIcon>
65

  
66

  
67

  
34
       
68 35
        <TabControl Grid.Row="0">  
69 36
            <TabItem VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch">
70 37
                <TabItem.Header>
......
216 183
        </TabControl>
217 184

  
218 185
        <StackPanel Orientation="Horizontal" Grid.Row="1" HorizontalAlignment="Right">
219
            <Button Name="SaveChanges" Content="OK" Click="SaveChanges_Click" Margin="5,5,10,5" Style="{StaticResource ButtonStyle}"/>
220
            <Button Name="RejectChanges" Content="Cancel" Click="RejectChanges_Click" Margin="5,5,10,5" Style="{StaticResource ButtonStyle}"/>
186
            <Button Name="SaveChanges" Content="OK" Margin="5,5,10,5" Style="{StaticResource ButtonStyle}"/>
187
            <Button Name="RejectChanges" Content="Cancel" Margin="5,5,10,5" Style="{StaticResource ButtonStyle}"/>
221 188
            <Button Name="ApplyChanges" Content="Apply" Style="{StaticResource ButtonStyle}" />
222 189
        </StackPanel>
223 190
    </Grid>
b/trunk/Pithos.Client.WPF/PreferencesView.xaml.cs
25 25
        {
26 26
            InitializeComponent();
27 27
        }
28

  
29
        private void SaveChanges_Click(object sender, RoutedEventArgs e)
30
        {
31
            this.Hide();
32
        }
33

  
34
        private void RejectChanges_Click(object sender, RoutedEventArgs e)
35
        {
36
            this.Hide();
37
        }
38

  
39
        private void ShowPreferences_Click(object sender, RoutedEventArgs e)
40
        {
41
            this.Show();
42
            this.Topmost = true;
43

  
44
            
45
        }
46

  
47 28
       
48 29
    }
49 30
}
b/trunk/Pithos.Client.WPF/PreferencesViewModel.cs
36 36
    /// <summary>
37 37
    /// TODO: Update summary.
38 38
    /// </summary>
39
    [Export(typeof(IShell))]
40
    public class PreferencesViewModel : Screen, IShell, IHandle<Notification>
39
    [Export]
40
    public class PreferencesViewModel : Screen
41 41
    {
42 42
        private IEventAggregator _events;
43 43

  
......
49 49
            set
50 50
            {
51 51
                _settings = value;
52
                foreach (var account in _settings.Accounts.Where(account=>account.IsActive))
53
                {
54
                    if (String.IsNullOrWhiteSpace(account.RootPath))
55
                    {
56
                        account.RootPath = _settings.PithosPath;
57
                        _settings.Save();
58
                    }
59
                }
60 52
                NotifyOfPropertyChange(()=>Settings);
61 53
            }
62 54
        }
63

  
64

  
65
        private Dictionary<string,PithosMonitor> _monitors=new Dictionary<string, PithosMonitor>();
66
        public Dictionary<string, PithosMonitor> Monitors
67
        {
68
            get { return _monitors; }            
69
        }
70

  
71
        private TaskbarViewModel _taskbar;
72
        public TaskbarViewModel Taskbar
73
        {
74
            get { return _taskbar; }
75
            set
76
            {
77
                _taskbar = value;
78
            }
79
        }
80

  
55
        
56
        public ShellViewModel Shell { get;  set; }
81 57
        //ShellExtensionController _extensionController=new ShellExtensionController();
82

  
83
        [ImportingConstructor]
84
        public PreferencesViewModel(IEventAggregator events, TaskbarViewModel taskbar, PithosSettings settings)
58
        
59
        public PreferencesViewModel(IEventAggregator events,ShellViewModel shell, PithosSettings settings)
85 60
        {
86 61
            _events = events;
87 62
            _events.Subscribe(this);
88 63

  
89 64
            DisplayName = "Pithos Preferences";
65
            Shell = shell;
90 66

  
91
            Taskbar=taskbar;
92
            Taskbar.Parent = this;
93
            
94 67
            Settings=settings;            
95 68

  
96 69

  
97
            Taskbar.UsageMessage = "Using 15% of 50 GB";
98
            Taskbar.StatusMessage = "In Synch";            
99 70
        }
100 71

  
72
/*
101 73
        protected override void OnViewAttached(object view, object context)
102 74
        {
103 75
            var window = (Window)view;
......
110 82
        {
111 83
            var window = (Window)view;
112 84
            window.Hide();
113
            Taskbar.UpdateStatus();
114 85
            base.OnViewLoaded(view);
115 86
        }
116 87

  
88
*/
117 89
        #region Preferences Properties
118 90

  
119 91
        private bool _noProxy;
......
162 134
        public void SaveChanges()
163 135
        {
164 136
            DoSave();
165
           
137
            TryClose(true);
166 138
        }
167 139

  
168 140
        public void RejectChanges()
169 141
        {
170 142
            Settings.Reload();
171
           
143
            TryClose(false);
172 144
        }
173 145

  
174 146
        public void ApplyChanges()
......
179 151
        private void DoSave()
180 152
        {
181 153
            Settings.Save();
182
            NotifyOfPropertyChange(()=>Settings);
183

  
184
            var activeAccount = Settings.Accounts.FirstOrDefault(account => account.IsActive);
185
            if (activeAccount == null)
186
                return;
187
            if (String.IsNullOrWhiteSpace(activeAccount.AccountName))
188
                return;
189

  
190
            var monitor = Monitors[activeAccount.AccountName];
191
            monitor.ApiKey = activeAccount.ApiKey;
192
            monitor.UserName = activeAccount.AccountName;
193
            monitor.UsePithos = activeAccount.UsePithos;
154
            foreach (var account in Settings.Accounts)
155
            {                
156
                Shell.MonitorAccount(account);
157
            }
194 158

  
195
            monitor.Start();
159
            NotifyOfPropertyChange(()=>Settings);
196 160
        }
197 161

  
198 162
        public void ChangePithosFolder()
......
215 179
        {
216 180
            var newAccount = new AccountSettings();
217 181
            Settings.Accounts.Add(newAccount);
218
            SelectedAccountIndex= Settings.Accounts.Count-1;
182
            SelectedAccountIndex= Settings.Accounts.Count-1;           
219 183
            NotifyOfPropertyChange(()=>Settings);
220 184
        }
221 185

  
......
247 211

  
248 212
        public void RemoveAccount()
249 213
        {
250
            Settings.Accounts.RemoveAll(account => account.AccountName == CurrentAccount.AccountName);
251
            
214
            var accountName = CurrentAccount.AccountName;
215
            Settings.Accounts.RemoveAll(account => account.AccountName == accountName);
216

  
217
            Shell.RemoveMonitor(accountName);
218

  
252 219
            NotifyOfPropertyChange(()=>CurrentAccount);
253 220
            NotifyOfPropertyChange(()=>Settings);
254 221
            //NotifyOfPropertyChange("Settings.Accounts");
......
281 248
            }
282 249
        }
283 250

  
284
        public void RefreshOverlays()
285
        {
286
            string path=Settings.PithosPath;
287
            if (String.IsNullOrWhiteSpace(path))
288
                throw new ArgumentNullException("path", "The path parameter must not be emtpy");
289

  
290
            if (!Directory.Exists(path) && !File.Exists(path))
291
                throw new FileNotFoundException("The specified file or path does not exist", path);
292

  
293

  
294
            IntPtr pathPointer = Marshal.StringToCoTaskMemAuto(path);
295

  
296
            try
297
            {
298
                NativeMethods.SHChangeNotify(HChangeNotifyEventID.SHCNE_UPDATEITEM,
299
                                             HChangeNotifyFlags.SHCNF_PATHW | HChangeNotifyFlags.SHCNF_FLUSHNOWAIT,
300
                                             pathPointer, IntPtr.Zero);
301
            }
302
            finally
303
            {
304
                Marshal.FreeHGlobal(pathPointer);
305
            }
306

  
307
        }
251
       
308 252
        #endregion
309 253

  
310 254
        private int _selectedAccountIndex;
......
339 283
        }
340 284

  
341 285

  
342
        public void Handle(Notification notification)
343
        {
344
            if (!Settings.ShowDesktopNotifications)
345
                return;
346
            BalloonIcon icon = BalloonIcon.None;
347
            switch (notification.Level)
348
            {
349
                case TraceLevel.Error:
350
                    icon = BalloonIcon.Error;
351
                    break;
352
                case TraceLevel.Info:
353
                case TraceLevel.Verbose:
354
                    icon = BalloonIcon.Info;
355
                    break;
356
                case TraceLevel.Warning:
357
                    icon = BalloonIcon.Warning;
358
                    break;
359
                default:
360
                    icon = BalloonIcon.None;
361
                    break;
362
            }
363

  
364
            var tv = (PreferencesView)this.GetView();
365
            tv.TaskbarView.ShowBalloonTip(notification.Title, notification.Message, icon);
366
        }
367 286

  
368 287

  
369 288
    public void MoveAccountFolder()
......
381 300

  
382 301
            var newPath= dlg.SelectedPath;                
383 302
            //Find the account's monitor and stop it
384
            var monitor = Monitors[CurrentAccount.AccountName];            
303
            var monitor = Shell.Monitors[CurrentAccount.AccountName];            
385 304
            monitor.Stop();
386 305
                            
387 306
            var oldPath = monitor.RootPath;                
b/trunk/Pithos.Client.WPF/Services/Events.cs
1
namespace Pithos.Client.WPF.Services
2
{
3
    public class ShowFilePropertiesEvent
4
    {
5
        public string FileName { get; set; }
6

  
7
        public ShowFilePropertiesEvent(){}
8

  
9
        public ShowFilePropertiesEvent(string fileName)
10
        {
11
            FileName = fileName;
12
        }
13

  
14
    }
15
}
b/trunk/Pithos.Client.WPF/Services/StatusService.cs
1
// -----------------------------------------------------------------------
2
// <copyright file="StatusService.cs" company="Microsoft">
3
// TODO: Update copyright text.
4
// </copyright>
5
// -----------------------------------------------------------------------
6

  
7

  
8
using Caliburn.Micro;
9
using System.ServiceModel;
10
using System.ComponentModel.Composition;
11
using Pithos.Core;
12
using Pithos.Interfaces;
13

  
14
namespace Pithos.Client.WPF.Services
15
{
16
    /// <summary>
17
    /// TODO: Update summary.
18
    /// </summary>
19
    [ServiceBehavior(IncludeExceptionDetailInFaults = true)]
20
    [Export]
21
    public class StatusService : IStatusService,ISettingsService, ICommandsService
22
    {
23
        [Import]
24
        public IPithosSettings Settings { get; set; }
25

  
26
        [Import]
27
        public IStatusChecker Checker { get; set; }
28

  
29
        [Import]
30
        public PithosMonitor Monitor { get; set; }
31

  
32
        public StatusService()
33
        {
34
            IoC.BuildUp(this);
35
        }
36

  
37
        private IEventAggregator _events;
38

  
39
        [ImportingConstructor]
40
        public StatusService(IStatusChecker checker,IEventAggregator events)
41
        {
42
            Checker = checker;
43
            _events = events;
44
        }
45

  
46

  
47
        public FileOverlayStatus GetStatus(string filePath)
48
        {
49
            return Checker.GetFileOverlayStatus(filePath);
50
        }
51

  
52
        public void DisplayProperties(string filePath)
53
        {
54
            //Monitor.
55
        }
56

  
57
        public PithosSettingsData GetSettings()
58
        {
59
            var data = new PithosSettingsData(Settings);
60
            return data;
61
        }
62

  
63
        public void ShowProperties(string fileName)
64
        {
65
            _events.Publish(new ShowFilePropertiesEvent(fileName));
66
        }
67
    }
68
}
b/trunk/Pithos.Client.WPF/ShellView.xaml
5 5
        xmlns:cal="http://www.caliburnproject.org"
6 6
         >
7 7

  
8
    <Grid Background="White">
8
    <Window.Resources>
9
        <ResourceDictionary>
10
            <ResourceDictionary.MergedDictionaries>
11
                <ResourceDictionary Source="PithosStyles.xaml" />
12
            </ResourceDictionary.MergedDictionaries>
13
        </ResourceDictionary>
14
    </Window.Resources>
15
    <Grid>
9 16
        <Grid.RowDefinitions>
10 17
            <RowDefinition Height="*"/>
11 18
            <RowDefinition Height="Auto"/>
12 19
        </Grid.RowDefinitions>
13
            <tb:TaskbarIcon x:Name="PithosIcon"
14
                        IconSource="/Images/Tray.ico"
15
                        ToolTipText="All Files in Sync"
16
                        MenuActivation="LeftOrRightClick">
17
            <tb:TaskbarIcon.ContextMenu>
18 20

  
19
                <ContextMenu x:Name="TaskbarMenu"      >
20
                    <MenuItem Header="Open PITHOS Folder" x:Name="OpenPithosFolder" cal:Message.Attach="OpenPithosFolder"/>
21
                    <MenuItem Header="Launch PITHOS Site"  x:Name="GoToSite" cal:Message.Attach="GoToSite"/>
22
                    <MenuItem Header="Recently Changed Files" >
23
                        <MenuItem x:Name="RecentFiles"/>
21
        <tb:TaskbarIcon x:Name="TaskbarView"                           
22
                        IconSource="{Binding StatusIcon}" 
23
                        ToolTipText="{Binding StatusMessage}"                        
24
                        MenuActivation="LeftOrRightClick" 
25
                        DoubleClickCommand="{Binding OpenPithosFolderCommand}"                         
26
                        >
27
            <tb:TaskbarIcon.ContextMenu>
28
                <ContextMenu x:Name="TaskbarMenu" >
29
                    <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="Recently Changed Files" x:Name="RecentFiles" ItemsSource="{Binding RecentFiles}">
32
                        <MenuItem.ItemTemplate>
33
                            <DataTemplate>
34
                                <TextBlock Text="{Binding FileName}"/>
35
                            </DataTemplate>
36
                        </MenuItem.ItemTemplate>
24 37
                    </MenuItem>
25 38
                    <Separator  />
26
                    <MenuItem Header="15% of 50GB used" x:Name="UsageMessage" />
39
                    <MenuItem Header="{Binding UsageMessage}" x:Name="UsageMessage" />
27 40
                    <Separator  />
28
                    <MenuItem Header="All Files Up to Date" x:Name="StatusMessage" />
41
                    <MenuItem Header="{Binding StatusMessage}" x:Name="StatusMessage" />
29 42
                    <Separator  />
30
                    <MenuItem  Header="Pause Synching" x:Name="ToggleSynching" cal:Message.Attach="ToggleSynching"/>
43
                    <MenuItem  Header="{Binding PauseSyncCaption}" x:Name="ToggleSynching" cal:Message.Attach="ToggleSynching"  />
31 44
                    <Separator  />
32
                    <MenuItem  Header="Preferences ..." x:Name="ShowPreferences" cal:Message.Attach="ShowPreferences"/>
45
                    <MenuItem  Header="Preferences ..." x:Name="ShowPreferences" cal:Message.Attach="ShowPreferences"  />
33 46
                    <Separator  />
34
                    <MenuItem  Header="Exit" Name="ExitPithos" cal:Message.Attach="ExitPithos"/>
47
                    <MenuItem  Header="Exit" Name="ExitPithos" cal:Message.Attach="ExitPithos" />
35 48
                </ContextMenu>
36 49
            </tb:TaskbarIcon.ContextMenu>
37 50
        </tb:TaskbarIcon>
38

  
39

  
40
        <TabControl Grid.Row="0">
41
            <TabItem Header="General"/>
42
            <TabItem Header="Accounts"/>
43
            <TabItem Header="Network"/>
44
            <TabItem Header="Advanced"/>
45
        </TabControl>
46
        
47
        <StackPanel Orientation="Horizontal" Grid.Row="1">
48
            <Button Name="SaveChanges" Content="OK" />
49
            <Button Name="RejectChanges" Content="Cancel"/>
50
        </StackPanel>
51 51
    </Grid>
52

  
53 52
</Window>
b/trunk/Pithos.Client.WPF/ShellViewModel.cs
1
using System.Windows;
2
using System.Windows.Controls;
1
using System.Collections.Concurrent;
2
using System.ComponentModel.Composition;
3
using System.Diagnostics;
4
using System.IO;
5
using System.Runtime.InteropServices;
6
using System.ServiceModel;
7
using System.ServiceModel.Description;
8
using System.Threading.Tasks;
9
using System.Windows;
3 10
using Caliburn.Micro;
11
using Hardcodet.Wpf.TaskbarNotification;
12
using Pithos.Client.WPF.Configuration;
13
using Pithos.Client.WPF.Properties;
14
using Pithos.Core;
15
using Pithos.Interfaces;
16
using System;
17
using System.Collections.Generic;
18
using System.Linq;
19
using System.Text;
20
using StatusService = Pithos.Client.WPF.Services.StatusService;
4 21

  
5 22
namespace Pithos.Client.WPF {
6 23
    using System.ComponentModel.Composition;
7 24

  
8 25
    [Export(typeof(IShell))]
9
    public class ShellViewModel : ViewAware, IShell
26
    public class ShellViewModel : ViewAware, IStatusNotification, IShell, IHandle<Notification>
10 27
    {
11 28
       
12
       
13
        protected override void OnViewAttached(object view, object context)
29
        private IStatusChecker _statusChecker;
30
        private IEventAggregator _events;
31

  
32
        public PithosSettings Settings { get; private set; }
33

  
34
        public IScreen Parent { get; set; }
35

  
36

  
37
        private Dictionary<string, PithosMonitor> _monitors = new Dictionary<string, PithosMonitor>();
38
        public Dictionary<string, PithosMonitor> Monitors
14 39
        {
15
            var window = (Window)view;            
16
            window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
17
            window.ShowInTaskbar = false;
40
            get { return _monitors; }
41
        }
42

  
43
        private ServiceHost _statusService { get; set; }
44

  
45
        private static readonly log4net.ILog Log = log4net.LogManager.GetLogger("Pithos");
46

  
47
        [ImportingConstructor]
48
        public ShellViewModel(IWindowManager windowManager, IEventAggregator events, IStatusChecker statusChecker, PithosSettings settings)
49
        {
50
            _windowManager = windowManager;
51
            OpenPithosFolderCommand = new PithosCommand(OpenPithosFolder);
52
            _statusChecker = statusChecker;
53
            _events = events;            
54
            Settings = settings;
18 55
            
19
            base.OnViewAttached(view, context);
56
            
57

  
58
            UsageMessage = "Using 15% of 50 GB";
59
            StatusMessage = "In Synch";
60

  
61
            foreach (var account in settings.Accounts)
62
            {
63

  
64
                MonitorAccount(account);
65
            }
66

  
67
            StartStatusService();
68
        }
69
        
70

  
71
        
72
        public void MonitorAccount(AccountSettings account)
73
        {
74
            PithosMonitor monitor = null;
75
            var accountName = account.AccountName;
76

  
77
            if (_monitors.TryGetValue(accountName,out monitor))
78
            {
79
                //If the account is active
80
                if (account.IsActive)
81
                    //Start the monitor. It's OK to start an already started monitor,
82
                    //it will just ignore the call
83
                    monitor.Start();
84
                else
85
                {
86
                    //If the account is inactive
87
                    //Stop and remove the monitor
88
                    RemoveMonitor(accountName);
89
                }
90
                return;
91
            }
92

  
93
            //PithosMonitor uses MEF so we need to resolve it
94
            monitor = new PithosMonitor
95
                          {
96
                              UserName = accountName,
97
                              ApiKey = account.ApiKey,
98
                              UsePithos = account.UsePithos,
99
                              StatusNotification = this,
100
                              RootPath=account.RootPath                             
101
                          };          
102
            IoC.BuildUp(monitor);
103

  
104
            var appSettings = Properties.Settings.Default;
105
            monitor.AuthenticationUrl = account.UsePithos
106
                                            ? appSettings.PithosAuthenticationUrl
107
                                            : appSettings.CloudfilesAuthenticationUrl;
108

  
109
            _monitors[accountName] = monitor;
110

  
111
            if (account.IsActive)
112
            {                
113
                //Don't start a monitor if it doesn't have an account and ApiKey
114
                if (String.IsNullOrWhiteSpace(monitor.UserName) || String.IsNullOrWhiteSpace(monitor.ApiKey))
115
                    return;
116
                StartMonitor(monitor);
117
            }
20 118
        }
21 119

  
22
      
120

  
23 121
        protected override void OnViewLoaded(object view)
24 122
        {
25
            var window = (Window) view;
123
            var window = (Window)view;
26 124
            window.Hide();
27
            
125
            UpdateStatus();
28 126
            base.OnViewLoaded(view);
29 127
        }
30 128

  
31 129

  
32 130
        #region Status Properties
33
        
131

  
34 132
        private string _statusMessage;
35 133
        public string StatusMessage
36 134
        {
......
38 136
            set
39 137
            {
40 138
                _statusMessage = value;
41
                NotifyOfPropertyChange(()=>StatusMessage);
139
                NotifyOfPropertyChange(() => StatusMessage);
42 140
            }
43 141
        }
44 142

  
......
49 147
            set
50 148
            {
51 149
                _usageMessage = value;
52
                NotifyOfPropertyChange(()=>UsageMessage);
150
                NotifyOfPropertyChange(() => UsageMessage);
53 151
            }
54 152
        }
55 153

  
56
        private readonly IObservableCollection<FileEntry> _recentFiles=new BindableCollection<FileEntry>();
57
        public IObservableCollection<FileEntry> RecentFiles
154

  
155
        private string _pauseSyncCaption="Pause Syncing";
156
        public string PauseSyncCaption
157
        {
158
            get { return _pauseSyncCaption; }
159
            set
160
            {
161
                _pauseSyncCaption = value;
162
                NotifyOfPropertyChange(() => PauseSyncCaption);
163
            }
164
        }
165

  
166
        private readonly ObservableConcurrentCollection<FileEntry> _recentFiles = new ObservableConcurrentCollection<FileEntry>();
167
        public ObservableConcurrentCollection<FileEntry> RecentFiles
168
        {
169
            get { return _recentFiles; }
170
        }
171

  
172

  
173
        private string _statusIcon="Images/Tray.ico";
174
        public string StatusIcon
58 175
        {
59
            get { return _recentFiles; }            
176
            get { return _statusIcon; }
177
            set
178
            {
179
                _statusIcon = value;
180
                NotifyOfPropertyChange(() => StatusIcon);
181
            }
60 182
        }
61 183

  
62 184
        #endregion
63 185

  
186
        #region Commands
64 187

  
65 188
        public void ShowPreferences()
66 189
        {
67
            var window = (Window) this.GetView();
68
            window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
69
            window.Show();            
190
            Settings.Reload();
191
            var preferences = new PreferencesViewModel(_events, this,Settings);            
192
            _windowManager.ShowDialog(preferences);
70 193
        }
71 194

  
195

  
196
        public PithosCommand OpenPithosFolderCommand { get; private set; }
197

  
72 198
        public void OpenPithosFolder()
73 199
        {
74
            
200
            Process.Start(Settings.PithosPath);
75 201
        }
76 202

  
77 203
        public void GoToSite()
78 204
        {
205
            var activeAccount=Settings.Accounts.FirstOrDefault(account => account.IsActive);
206
            if (activeAccount == null)
207
                return;            
79 208

  
209
            var site = String.Format("http://{0}/ui/?token={1}&user={2}",
210
                Properties.Settings.Default.PithosSite,activeAccount.ApiKey,
211
                activeAccount.AccountName);
212
            Process.Start(site);
80 213
        }
81 214

  
215

  
82 216
        public void ToggleSynching()
83 217
        {
218
            bool isPaused=false;
219
            foreach (var pair in Monitors)
220
            {
221
                var monitor = pair.Value;
222
                monitor.Pause = !monitor.Pause;
223
                isPaused = monitor.Pause;
224
            }
225

  
226
            PauseSyncCaption = isPaused ? "Resume syncing" : "Pause syncing";
227
            var iconKey = isPaused? "TraySyncPaused" : "TrayInSynch";
228
            StatusIcon = String.Format(@"Images/{0}.ico", iconKey);
229
        }
230

  
231
        public void ExitPithos()
232
        {
233
            foreach (var pair in Monitors)
234
            {
235
                var monitor = pair.Value;
236
                monitor.Stop();
237
            }
238

  
239
            ((Window)GetView()).Close();
240
        }
241
        #endregion
242

  
243

  
244
        private Dictionary<PithosStatus, StatusInfo> iconNames = new List<StatusInfo>
245
            {
246
                new StatusInfo(PithosStatus.InSynch, "All files up to date", "TrayInSynch"),
247
                new StatusInfo(PithosStatus.Syncing, "Syncing Files", "TraySynching"),
248
                new StatusInfo(PithosStatus.SyncPaused, "Sync Paused", "TraySyncPaused")
249
            }.ToDictionary(s => s.Status);
250

  
251
        readonly IWindowManager _windowManager;
252

  
253

  
254
        public void UpdateStatus()
255
        {
256
            var pithosStatus = _statusChecker.GetPithosStatus();
257

  
258
            if (iconNames.ContainsKey(pithosStatus))
259
            {
260
                var info = iconNames[pithosStatus];
261
                StatusIcon = String.Format(@"Images/{0}.ico", info.IconName);
262
                StatusMessage = String.Format("Pithos 1.0\r\n{0}", info.StatusText);
263
            }
264

  
265
            var tv=this.GetView();
266
            _events.Publish(new Notification { Title = "Start", Message = "Start Monitoring", Level = TraceLevel.Info});
267
        }
268

  
269

  
270
       
271
        private Task StartMonitor(PithosMonitor monitor)
272
        {
273
            return Task.Factory.StartNew(() =>
274
            {
275
                using (log4net.ThreadContext.Stacks["Monitor"].Push("Start"))
276
                {
277
                    try
278
                    {
279
                        Log.InfoFormat("Start Monitoring {0}", monitor.UserName);
280
                        monitor.Start();
281
                    }
282
                    catch (Exception exc)
283
                    {
284
                        var message =
285
                            String.Format("An exception occured. Can't start monitoring\nWill retry in 10 seconds");
286
                        Task.Factory.StartNewDelayed(10000, () => StartMonitor(monitor));
287
                        _events.Publish(new Notification {Title = "Error", Message = message, Level = TraceLevel.Error});
288
                        Log.Error(message, exc);
289
                    }
290
                }
291
            });
292
        }
293

  
294

  
295
        public void NotifyChange(string status, TraceLevel level=TraceLevel.Info)
296
        {
297
            this.StatusMessage = status;
84 298
            
299
            _events.Publish(new Notification { Title = "Pithos", Message = status, Level = level });
85 300
        }
86 301

  
87
        public void SaveChanges()
302
        public void NotifyChangedFile(string filePath)
88 303
        {
89
            var window = (Window)GetView();
90
            window.Hide();            
304
            var entry = new FileEntry {FullPath=filePath};
305
            IProducerConsumerCollection<FileEntry> files=this.RecentFiles;
306
            FileEntry popped;
307
            while (files.Count > 5)
308
                files.TryTake(out popped);
309
            files.TryAdd(entry);
91 310
        }
92 311

  
93
        public void RejectChanges()
312

  
313
        public void Handle(Notification notification)
94 314
        {
95
            var window=(Window)GetView();            
96
            window.Hide();
315
            if (!Settings.ShowDesktopNotifications)
316
                return;
317
            BalloonIcon icon = BalloonIcon.None;
318
            switch (notification.Level)
319
            {
320
                case TraceLevel.Error:
321
                    icon = BalloonIcon.Error;
322
                    break;
323
                case TraceLevel.Info:
324
                case TraceLevel.Verbose:
325
                    icon = BalloonIcon.Info;
326
                    break;
327
                case TraceLevel.Warning:
328
                    icon = BalloonIcon.Warning;
329
                    break;
330
                default:
331
                    icon = BalloonIcon.None;
332
                    break;
333
            }
334

  
335
            var tv = (ShellView)this.GetView();
336
            tv.TaskbarView.ShowBalloonTip(notification.Title, notification.Message, icon);
97 337
        }
98 338

  
339
        public void RemoveMonitor(string accountName)
340
        {
341
            if (String.IsNullOrWhiteSpace(accountName))
342
                return;
343

  
344
            PithosMonitor monitor;
345
            if (Monitors.TryGetValue(accountName, out monitor))
346
            {
347
                Monitors.Remove(accountName);
348
                monitor.Stop();
349
            }
350
        }
351

  
352
        public void RefreshOverlays()
353
        {
354
            foreach (var pair in Monitors)
355
            {
356
                var monitor = pair.Value;
357

  
358
                var path = monitor.RootPath;
359

  
360
                if (String.IsNullOrWhiteSpace(path))
361
                    continue;
362

  
363
                if (!Directory.Exists(path) && !File.Exists(path))
364
                    continue;
365

  
366
                IntPtr pathPointer = Marshal.StringToCoTaskMemAuto(path);
367

  
368
                try
369
                {
370
                    NativeMethods.SHChangeNotify(HChangeNotifyEventID.SHCNE_UPDATEITEM,
371
                                                 HChangeNotifyFlags.SHCNF_PATHW | HChangeNotifyFlags.SHCNF_FLUSHNOWAIT,
372
                                                 pathPointer, IntPtr.Zero);
373
                }
374
                finally
375
                {
376
                    Marshal.FreeHGlobal(pathPointer);
377
                }
378
            }
379
        }
380

  
381
        private void StartStatusService()
382
        {
383
            // Create a ServiceHost for the CalculatorService type and provide the base address.
384
            var baseAddress = new Uri("net.pipe://localhost/pithos");
385
            _statusService = new ServiceHost(typeof(StatusService), baseAddress);
386

  
387
            var binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
388

  
389
            _statusService.AddServiceEndpoint(typeof(IStatusService), binding, "net.pipe://localhost/pithos/statuscache");
390
            _statusService.AddServiceEndpoint(typeof(ISettingsService), binding, "net.pipe://localhost/pithos/settings");
391

  
392

  
393
            //// Add a mex endpoint
394
            var smb = new ServiceMetadataBehavior
395
            {
396
                HttpGetEnabled = true,
397
                HttpGetUrl = new Uri("http://localhost:30000/pithos/mex")
398
            };
399
            _statusService.Description.Behaviors.Add(smb);
400

  
401

  
402
            _statusService.Open();
403
        }
404

  
405
        private void StopStatusService()
406
        {
407
            if (_statusService == null)
408
                return;
409

  
410
            if (_statusService.State == CommunicationState.Faulted)
411
                _statusService.Abort();
412
            else if (_statusService.State != CommunicationState.Closed)
413
                _statusService.Close();
414
            _statusService = null;
415

  
416
        }
99 417
    }
100 418
}
b/trunk/Pithos.Core.Test/NetworkAgentTest.cs
3 3
using System.IO;
4 4
using System.Linq;
5 5
using System.Text;
6
using System.Threading.Tasks;
6 7
using NUnit.Framework;
7 8
using Pithos.Core.Agents;
8 9
using Pithos.Network;
......
15 16
        [Test]
16 17
        public void TestUpload()
17 18
        {
18
            var agent = new NetworkAgent
19
            {
20
                PithosContainer = "pithos",
21
                BlockSize = 4194304,
22
                BlockHash="sha256",
23
                CloudClient = new CloudFilesClient
24
                                {
25
                                    AuthenticationUrl = @"https://pithos.dev.grnet.gr",
26
                                    UsePithos = true
27
                                }
28
            };
19
            var agent = new NetworkAgent();
29 20

  
30 21
            var account = "890329@vho.grnet.gr";
31
            agent.CloudClient.Authenticate(account, "24989dce4e0fcb072f8cb60c8922be19");
22
            var apiKey = "24989dce4e0fcb072f8cb60c8922be19";
23

  
24
            var client = new CloudFilesClient(account,apiKey)
25
                             {
26
                                 AuthenticationUrl = @"https://pithos.dev.grnet.gr",
27
                                 UsePithos = true
28
                             };
29

  
30
            
31
            var accountInfo=client.Authenticate();
32 32

  
33 33
            var fileName = "012345.dump";
34 34
            var filePath = Path.Combine(@"e:\pithos\", fileName);
......
47 47
            }
48 48

  
49 49

  
50
            agent.CloudClient.DeleteObject(null, agent.PithosContainer, fileName,agent.TrashContainer);
50
            client.DeleteObject(null, FolderConstants.PithosContainer, fileName);
51 51

  
52
            var task = Signature.CalculateTreeHashAsync(filePath, agent.BlockSize, agent.BlockHash);
53
            agent.UploadWithHashMap(account,"pithos",new FileInfo(filePath),fileName,task);
52
            var task = Signature.CalculateTreeHashAsync(filePath, accountInfo.BlockSize, accountInfo.BlockHash);
53
            var tasks=agent.UploadWithHashMap(accountInfo,account,"pithos",new FileInfo(filePath),fileName,task);
54
            Task.Factory.Iterate(tasks).Wait();
54 55

  
55
            var newHash = agent.CloudClient.GetHashMap(null, agent.PithosContainer, fileName).Result;
56
            var newHash = client.GetHashMap(null, FolderConstants.PithosContainer, fileName).Result;
56 57

  
57 58

  
58 59
            var treeHash = task.Result;
......
67 68
        {
68 69
            var fileAgent = new FileAgent {FragmentsPath = @"e:\pithos\Fragments"};
69 70

  
70
            var agent = new NetworkAgent
71
            var agent = new NetworkAgent();
72

  
73
            var account = "890329@vho.grnet.gr";
74
            var apiKey = "24989dce4e0fcb072f8cb60c8922be19";
75
            var client = new CloudFilesClient(account,apiKey)
71 76
            {
72
                PithosContainer = "pithos",
73
                BlockSize = 4194304,
74
                BlockHash="sha256",
75
                CloudClient = new CloudFilesClient
76
                                {
77
                                    AuthenticationUrl = @"https://pithos.dev.grnet.gr",
78
                                    UsePithos = true
79
                                },
80
                FileAgent=fileAgent
77
                AuthenticationUrl = @"https://pithos.dev.grnet.gr",
78
                UsePithos = true
81 79
            };
82 80

  
83
            var account = "890329@vho.grnet.gr";
84
            agent.CloudClient.Authenticate(account, "24989dce4e0fcb072f8cb60c8922be19");
81
            var accountInfo=client.Authenticate();
85 82

  
86 83
            var fileName = @"AccessDatabaseEngine_x64.exe";
87 84

  
88 85
            var filePath = Path.Combine(@"e:\pithos\", fileName);
89 86
            if (File.Exists(filePath))
90
                File.Delete(filePath);            
91
            
92
            var newHash = agent.CloudClient.GetHashMap(null, agent.PithosContainer, fileName).Result;
93
            agent.DownloadWithBlocks(account,agent.PithosContainer,new Uri(fileName,UriKind.Relative),filePath,newHash)
87
                File.Delete(filePath);
88

  
89
            var newHash = client.GetHashMap(null, FolderConstants.PithosContainer, fileName).Result;
90
            agent.DownloadWithBlocks(client, account, FolderConstants.PithosContainer, new Uri(fileName, UriKind.Relative), filePath, newHash)
94 91
                .Wait();
95 92

  
96 93
            Assert.IsTrue(File.Exists(filePath));
97
            var treeHash = Signature.CalculateTreeHashAsync(filePath, agent.BlockSize, agent.BlockHash).Result;
94
            var treeHash = Signature.CalculateTreeHashAsync(filePath, accountInfo.BlockSize, accountInfo.BlockHash).Result;
98 95

  
99 96
            Assert.AreEqual(treeHash.TopHash, newHash.TopHash);
100 97

  
b/trunk/Pithos.Core.Test/Pithos.Core.Test.csproj
122 122
      <Project>{A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}</Project>
123 123
      <Name>Newtonsoft.Json</Name>
124 124
    </ProjectReference>
125
    <ProjectReference Include="..\Libraries\ParallelExtensionsExtras\ParallelExtensionsExtras.csproj">
126
      <Project>{C45218F8-09E7-4F57-85BC-5D8D2AC736A3}</Project>
127
      <Name>ParallelExtensionsExtras</Name>
128
    </ProjectReference>
125 129
    <ProjectReference Include="..\Pithos.Core\Pithos.Core.csproj">
126 130
      <Project>{142AF135-DF30-4563-B0AC-B604235AE874}</Project>
127 131
      <Name>Pithos.Core</Name>
b/trunk/Pithos.Core/Agents/CloudTransferAction.cs
1 1
using System;
2
using System.Diagnostics.Contracts;
2 3
using System.IO;
3 4
using System.Threading;
4 5
using Pithos.Interfaces;
......
18 19

  
19 20
    public class CloudAction
20 21
    {
22
        public AccountInfo AccountInfo { get; set; }
21 23
        public CloudActionType Action { get; set; }
22 24
        public FileInfo LocalFile { get; set; }
23 25
        public ObjectInfo CloudFile { get; set; }
......
34 36
        }
35 37

  
36 38

  
37
        protected CloudAction(CloudActionType action)
39
        [ContractInvariantMethod]
40
        private void Invariants()
38 41
        {
42
            Contract.Invariant(AccountInfo!=null);
43
        }
44

  
45
        protected CloudAction(AccountInfo accountInfo,CloudActionType action)
46
        {
47
            if (accountInfo==null)
48
                throw new ArgumentNullException("accountInfo");
49
            Contract.EndContractBlock();
50

  
39 51
            Action = action;
52
            AccountInfo = accountInfo;
40 53
        }
41 54

  
42
        public CloudAction(CloudActionType action, FileInfo localFile, ObjectInfo cloudFile,FileState state,int blockSize, string algorithm) : this(action)
55
        public CloudAction(AccountInfo accountInfo, CloudActionType action, FileInfo localFile, ObjectInfo cloudFile, FileState state, int blockSize, string algorithm)
56
            : this(accountInfo,action)
43 57
        {
44 58
            LocalFile = localFile;
45 59
            CloudFile = cloudFile;
......
57 71

  
58 72
    public class CloudDownloadAction:CloudAction
59 73
    {
60
        public CloudDownloadAction(ObjectInfo cloudFile)
61
            :base(CloudActionType.DownloadUnconditional)
74
        public CloudDownloadAction(AccountInfo accountInfo, ObjectInfo cloudFile)
75
            :base(accountInfo,CloudActionType.DownloadUnconditional)
62 76
        {            
63 77
            CloudFile = cloudFile;
64 78
        }
......
66 80
    }
67 81
    public class CloudDeleteAction:CloudAction
68 82
    {
69
        public CloudDeleteAction(string fileName, FileState fileState)
70
            : this(new ObjectInfo { Name = fileName },fileState)
83
        public CloudDeleteAction(AccountInfo accountInfo, string fileName, FileState fileState)
84
            : this(accountInfo,new ObjectInfo { Name = fileName },fileState)
71 85
        {
72 86
        }
73 87

  
74
        public CloudDeleteAction(ObjectInfo cloudFile, FileState fileState) 
75
            : base(CloudActionType.DeleteCloud)
88
        public CloudDeleteAction(AccountInfo accountInfo, ObjectInfo cloudFile, FileState fileState) 
89
            : base(accountInfo,CloudActionType.DeleteCloud)
76 90
        {
77 91
            CloudFile = cloudFile;
78 92
            FileState = fileState;
......
81 95

  
82 96
    public class CloudUploadAction:CloudAction
83 97
    {
84
        public CloudUploadAction(FileInfo fileInfo, FileState state, int blockSize, string algorithm) 
85
            : base(CloudActionType.UploadUnconditional,fileInfo,ObjectInfo.Empty,state,blockSize,algorithm)
98
        public CloudUploadAction(AccountInfo accountInfo, FileInfo fileInfo, FileState state, int blockSize, string algorithm) 
99
            : base(accountInfo,CloudActionType.UploadUnconditional,fileInfo,ObjectInfo.Empty,state,blockSize,algorithm)
86 100
        {
87 101
        }
88 102
    }
......
94 108
        public string NewFileName { get; set; }
95 109
        public string NewPath { get; set; }
96 110

  
97
        public CloudMoveAction(CloudActionType action, string oldPath, string oldFileName, string newFileName, string newPath)
98
            :base(action)
111
        public CloudMoveAction(AccountInfo accountInfo, CloudActionType action, string oldPath, string oldFileName, string newFileName, string newPath)
112
            :base(accountInfo,action)
99 113
        {
100 114
            OldFileName = oldFileName;
101 115
            OldPath = oldPath;
b/trunk/Pithos.Core/Agents/FileAgent.cs
14 14

  
15 15
namespace Pithos.Core.Agents
16 16
{
17
    [Export]
17
    [Export,PartCreationPolicy(CreationPolicy.NonShared)]
18 18
    public class FileAgent
19 19
    {
20 20
        Agent<WorkflowState> _agent;
......
27 27
        [Import]
28 28
        public WorkflowAgent WorkflowAgent { get; set; }
29 29

  
30
        public string RootPath { get; private set; }
30
        private AccountInfo AccountInfo { get; set; }
31

  
32
        private string RootPath { get;  set; }
31 33

  
32 34
        private static readonly ILog Log = LogManager.GetLogger("FileAgent");
33 35

  
34
        public void Start(string rootPath)
36
        public void Start(AccountInfo accountInfo,string rootPath)
35 37
        {
38
            if (accountInfo==null)
39
                throw new ArgumentNullException("accountInfo");
36 40
            if (String.IsNullOrWhiteSpace(rootPath))
37 41
                throw new ArgumentNullException("rootPath");
38 42
            if (!Path.IsPathRooted(rootPath))
39 43
                throw new ArgumentException("rootPath must be an absolute path","rootPath");
40 44
            Contract.EndContractBlock();
41 45

  
46
            AccountInfo = accountInfo;
42 47
            RootPath = rootPath;
43 48
            _watcher = new FileSystemWatcher(rootPath);
44 49
            _watcher.IncludeSubdirectories = true;            
......
201 206
            var filePath = e.FullPath;
202 207
            if (Ignore(filePath)) 
203 208
                return;
204
            _agent.Post(new WorkflowState { Path = filePath, FileName = e.Name, TriggeringChange = e.ChangeType });
209
            _agent.Post(new WorkflowState(AccountInfo) { Path = filePath, FileName = e.Name, TriggeringChange = e.ChangeType });
205 210
        }
206 211

  
207 212

  
......
213 218
            if (Ignore(oldFullPath) || Ignore(fullPath))
214 219
                return;
215 220

  
216
            _agent.Post(new WorkflowState
221
            _agent.Post(new WorkflowState(AccountInfo)
217 222
            {
218 223
                OldPath = oldFullPath,
219 224
                OldFileName = e.OldName,
b/trunk/Pithos.Core/Agents/NetworkAgent.cs
25 25
        
26 26
        public IStatusNotification StatusNotification { get; set; }
27 27
        [Import]
28
        public ICloudClient CloudClient { get; set; }
29

  
30
        [Import]
31 28
        public FileAgent FileAgent {get;set;}
32 29

  
33
        /*
34
        [Import]
35
        public IPithosWorkflow Workflow { get; set; }
36
*/
37

  
38

  
39
        public string PithosContainer { get; set; }
40
        public string TrashContainer { get; private set; }
41
        public IList<string> Containers { get; private set; }
42

  
43
        public int BlockSize { get; set; }
44
        public string BlockHash { get; set; }
30
       /* public int BlockSize { get; set; }
31
        public string BlockHash { get; set; }*/
45 32

  
46 33
        private static readonly ILog Log = LogManager.GetLogger("NetworkAgent");
47 34

  
35
        private List<AccountInfo> _accounts=new List<AccountInfo>();
48 36

  
49
        public void Start(string pithosContainer, string trashContainer, int blockSize, string blockHash)
37
        public void Start(/*int blockSize, string blockHash*/)
50 38
        {
51
            if (String.IsNullOrWhiteSpace(pithosContainer))
52
                throw new ArgumentNullException("pithosContainer");
53
            if (String.IsNullOrWhiteSpace(trashContainer))
54
                throw new ArgumentNullException("trashContainer");
39
/*
40
            if (blockSize<0)
41
                throw new ArgumentOutOfRangeException("blockSize");
42
            if (String.IsNullOrWhiteSpace(blockHash))
43
                throw new ArgumentOutOfRangeException("blockHash");
55 44
            Contract.EndContractBlock();
45
*/
56 46

  
57
            PithosContainer = pithosContainer;
58
            TrashContainer = trashContainer;
47
/*
59 48
            BlockSize = blockSize;
60 49
            BlockHash = blockHash;
50
*/
61 51

  
62 52

  
63 53
            _agent = Agent<CloudAction>.Start(inbox =>
......
77 67
        {
78 68
            if (action == null)
79 69
                throw new ArgumentNullException("action");
70
            if (action.AccountInfo==null)
71
                throw new ArgumentException("The action.AccountInfo is empty","action");
80 72
            Contract.EndContractBlock();
81 73

  
74
            var accountInfo = action.AccountInfo;
75

  
82 76
            using (log4net.ThreadContext.Stacks["NETWORK"].Push("PROCESS"))
83 77
            {                
84 78
                Log.InfoFormat("[ACTION] Start Processing {0}:{1}->{2}", action.Action, action.LocalFile,
85 79
                               action.CloudFile.Name);
86 80

  
87 81
                var localFile = action.LocalFile;
88
                var cloudFile = action.CloudFile;
82
                var cloudFile = action.CloudFile;                
89 83
                var downloadPath = (cloudFile == null)
90 84
                                       ? String.Empty
91
                                       : Path.Combine(FileAgent.RootPath, cloudFile.RelativeUrlToFilePath(CloudClient.UserName));
85
                                       : Path.Combine(accountInfo.AccountPath, cloudFile.RelativeUrlToFilePath(accountInfo.UserName));
92 86

  
93 87
                try
94 88
                {
95
                    var account = action.CloudFile.Account ?? CloudClient.UserName;
96
                    var container = action.CloudFile.Container ?? PithosContainer;
89
                    var account = action.CloudFile.Account ?? accountInfo.UserName;
90
                    var container = action.CloudFile.Container ?? FolderConstants.PithosContainer;
97 91

  
98 92
                    switch (action.Action)
99 93
                    {
100 94
                        case CloudActionType.UploadUnconditional:
101
                            UploadCloudFile(account, container, localFile, action.LocalHash.Value, action.TopHash.Value);
95
                            UploadCloudFile(accountInfo,account, container, localFile, action.LocalHash.Value, action.TopHash.Value);
102 96
                            break;
103 97
                        case CloudActionType.DownloadUnconditional:
104 98

  
105
                            DownloadCloudFile(account, container, new Uri(cloudFile.Name, UriKind.Relative),
99
                            DownloadCloudFile(accountInfo, account, container, new Uri(cloudFile.Name, UriKind.Relative),
106 100
                                              downloadPath);
107 101
                            break;
108 102
                        case CloudActionType.DeleteCloud:
109
                            DeleteCloudFile(account, container, cloudFile.Name);
103
                            DeleteCloudFile(accountInfo, account, container, cloudFile.Name);
110 104
                            break;
111 105
                        case CloudActionType.RenameCloud:
112 106
                            var moveAction = (CloudMoveAction)action;
113
                            RenameCloudFile(account, container, moveAction.OldFileName, moveAction.NewPath,
107
                            RenameCloudFile(accountInfo, account, container, moveAction.OldFileName, moveAction.NewPath,
114 108
                                            moveAction.NewFileName);
115 109
                            break;
116 110
                        case CloudActionType.MustSynch:
......
118 112
                            if (!File.Exists(downloadPath))
119 113
                            {
120 114
                                var cloudUri = new Uri(action.CloudFile.Name, UriKind.Relative);
121
                                DownloadCloudFile(account, container, cloudUri, downloadPath);
115
                                DownloadCloudFile(accountInfo, account, container, cloudUri, downloadPath);
122 116
                            }
123 117
                            else
124 118
                            {
125
                                SyncFiles(action);
119
                                SyncFiles(accountInfo, action);
126 120
                            }
127 121
                            break;
128 122
                    }
......
137 131
                {
138 132
                    Log.ErrorFormat("{0} : {1} -> {2}  failed because the file was not found.\n Rescheduling a delete",
139 133
                        action.Action, action.LocalFile, action.CloudFile, exc);
140
                    Post(new CloudDeleteAction(action.CloudFile,action.FileState));
134
                    Post(new CloudDeleteAction(accountInfo,action.CloudFile,action.FileState));
141 135
                }
142 136
                catch (Exception exc)
143 137
                {
......
150 144
            }
151 145
        }
152 146

  
153
        private void SyncFiles(CloudAction action)
147
        private void SyncFiles(AccountInfo accountInfo,CloudAction action)
154 148
        {
149
            if (accountInfo == null)
150
                throw new ArgumentNullException("accountInfo");
155 151
            if (action==null)
156 152
                throw new ArgumentNullException("action");
157 153
            if (action.LocalFile==null)
......
168 164

  
169 165
            var account = cloudFile.Account;
170 166
            //Use "pithos" by default if no container is specified
171
            var container = cloudFile.Container ?? PithosContainer;
167
            var container = cloudFile.Container ?? FolderConstants.PithosContainer;
172 168

  
173 169
            var cloudUri = new Uri(cloudFile.Name, UriKind.Relative);
174 170
            var cloudHash = cloudFile.Hash.ToLower();
......
192 188
            if (lastUpTime <= lastLocalTime)
193 189
            {
194 190
                //It probably means it was changed while the app was down                        
195
                UploadCloudFile(account, container, localFile, action.LocalHash.Value,
191
                UploadCloudFile(accountInfo,account, container, localFile, action.LocalHash.Value,
196 192
                                action.TopHash.Value);
197 193
            }
198 194
            else
......
204 200
                {
205 201
                    case FileStatus.Unchanged:                        
206 202
                        //If the local file's status is Unchanged, we can go on and download the newer cloud file
207
                        DownloadCloudFile(account, container,cloudUri,downloadPath);
203
                        DownloadCloudFile(accountInfo,account, container,cloudUri,downloadPath);
208 204
                        break;
209 205
                    case FileStatus.Modified:
210 206
                        //If the local file is Modified, we may have a conflict. In this case we should mark the file as Conflict
......
273 269
        {
274 270
            if (cloudAction == null)
275 271
                throw new ArgumentNullException("cloudAction");
272
            if (cloudAction.AccountInfo==null)
273
                throw new ArgumentException("The CloudAction.AccountInfo is empty","cloudAction");
276 274
            Contract.EndContractBlock();
277 275
            
278 276
            //If the action targets a local file, add a treehash calculation
279 277
            if (cloudAction.LocalFile != null)
280 278
            {
281

  
282
                if (cloudAction.LocalFile.Length>BlockSize)
283
                    cloudAction.TopHash = new Lazy<string>(() => Signature.CalculateTreeHashAsync(cloudAction.LocalFile, 
284
                                    BlockSize, BlockHash).Result
279
                var accountInfo = cloudAction.AccountInfo;
280
                if (cloudAction.LocalFile.Length>accountInfo.BlockSize)
281
                    cloudAction.TopHash = new Lazy<string>(() => Signature.CalculateTreeHashAsync(cloudAction.LocalFile,
282
                                    accountInfo.BlockSize, accountInfo.BlockHash).Result
285 283
                                     .TopHash.ToHashString());
286 284
                else
287 285
                {
......
305 303
            }
306 304
        }
307 305

  
306
        
307

  
308 308
        //Remote files are polled periodically. Any changes are processed
309
        public Task ProcessRemoteFiles(string accountPath,DateTime? since=null)
309
        public Task ProcessRemoteFiles(DateTime? since=null)
310
        {
311
            return Task<Task>.Factory.StartNewDelayed(10000, () =>
312
            {
313
                using (log4net.ThreadContext.Stacks["Retrieve Remote"].Push("All accounts"))
314
                {
315
                    //Next time we will check for all changes since the current check minus 1 second
316
                    //This is done to ensure there are no discrepancies due to clock differences
317
                    DateTime nextSince = DateTime.Now.AddSeconds(-1);
318
                    
319
                    var tasks=from accountInfo in _accounts
320
                              select ProcessAccountFiles(accountInfo, since);
321
                    var process=Task.Factory.Iterate(tasks);
322

  
323
                    return process.ContinueWith(t=>
324
                        ProcessRemoteFiles(nextSince));
325
                }
326
            });            
327
        }
328

  
329
        public Task ProcessAccountFiles(AccountInfo accountInfo,DateTime? since=null)
310 330
        {   
311
            if (String.IsNullOrWhiteSpace(accountPath))
312
                throw new ArgumentNullException(accountPath);
331
            if (accountInfo==null)
332
                throw new ArgumentNullException("accountInfo");
333
            if (String.IsNullOrWhiteSpace(accountInfo.AccountPath))
334
                throw new ArgumentException("The AccountInfo.AccountPath is empty","accountInfo");
313 335
            Contract.EndContractBlock();
314 336

  
315
            using (log4net.ThreadContext.Stacks["SCHEDULE"].Push("Retrieve Remote"))
337
            using (log4net.ThreadContext.Stacks["Retrieve Remote"].Push(accountInfo.UserName))
316 338
            {
317
                Log.Info("[LISTENER] Scheduled");
339
                Log.Info("Scheduled");
340
                var client=new CloudFilesClient(accountInfo);
318 341

  
319 342
                //Get the list of server objects changed since the last check
320
                var listObjects = Task<IList<ObjectInfo>>.Factory.StartNewDelayed(10000, () =>
321
                                CloudClient.ListObjects(CloudClient.UserName, PithosContainer, since));
343
                var listObjects = Task<IList<ObjectInfo>>.Factory.StartNew(() =>
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff