Revision aba9e6d9

b/trunk/Pithos.Client.WPF/AppBootstrapper.cs
3 3
using System.Windows.Navigation;
4 4
using Caliburn.Micro;
5 5
using Caliburn.Micro.Logging;
6
using Pithos.Client.WPF.Properties;
6 7
using Pithos.Core;
7 8
using Pithos.Network;
8 9
using log4net.Appender;
......
27 28
	    public AppBootstrapper()
28 29
	    {
29 30
            LogManager.GetLog = type => new log4netLogger(type);
31
	        UpgradeSettings();
30 32
	    }
31 33

  
32
		/// <summary>
34
	    private void UpgradeSettings()
35
	    {
36
            if (Settings.Default.MustUpgrade)
37
            {
38
                Settings.Default.Upgrade();
39
                Settings.Default.MustUpgrade = false;
40
                Settings.Default.Save();
41
            }
42
	    }
43

  
44
	    /// <summary>
33 45
		/// By default, we are configured to use MEF
34 46
		/// </summary>
35 47
		protected override void Configure() {
b/trunk/Pithos.Client.WPF/Converters/EmptyToVisibilityConverter.cs
9 9
namespace Pithos.Client.WPF.Converters
10 10
{
11 11
    /// <summary>
12
    /// Returns Visible if a string value contains data, Hidden otherwise
12
    /// Returns Visible if a string value contains data, Collapsed otherwise
13 13
    /// </summary>
14 14
    public class EmptyToVisibilityConverter:IValueConverter
15 15
    {
......
18 18
            var stringValue = value as string;
19 19

  
20 20
            if (String.IsNullOrWhiteSpace(stringValue))
21
                return Visibility.Hidden;
21
                return Visibility.Collapsed;
22 22
            return Visibility.Visible;
23 23
        }
24 24

  
b/trunk/Pithos.Client.WPF/FileProperties/FilePropertiesView.xaml
4 4
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
5 5
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:cal="http://www.caliburnproject.org"
6 6
        mc:Ignorable="d" 
7
             d:DesignHeight="300" d:DesignWidth="300" MaxWidth="500"
8
         Height="481" Icon="/Pithos.Client.WPF;component/Images/PithosTaskbar.png"
9
        Background="{StaticResource {x:Static SystemColors.ControlBrushKey}}">
7
             d:DesignHeight="300" d:DesignWidth="300" MaxWidth="900"
8
         Height="681" Icon="/Pithos.Client.WPF;component/Images/PithosTaskbar.png"
9
        Background="{StaticResource {x:Static SystemColors.ControlBrushKey}}" 
10
        >
10 11
    <Window.Resources>
11 12
        <ResourceDictionary>
12 13
            <ResourceDictionary.MergedDictionaries>
13 14
                <ResourceDictionary Source="..\PithosStyles.xaml" />
14 15
            </ResourceDictionary.MergedDictionaries>
16
            <BooleanToVisibilityConverter x:Key="BoolToVisible" />
15 17
        </ResourceDictionary>       
16 18
    </Window.Resources>
17 19
    <Grid>
......
20 22
            <RowDefinition Height="Auto"/>
21 23
            <RowDefinition Height="*"/>
22 24
            <RowDefinition Height="*"/>
25
            <RowDefinition Height="*"/>
23 26
            <RowDefinition Height="Auto"/>
24 27
        </Grid.RowDefinitions>
25 28

  
......
78 81
            </Grid>
79 82
        </GroupBox>
80 83
        <GroupBox Header="Metadata" Grid.Row="2" >
81
                <DataGrid ItemsSource="{Binding Tags}" 
84
                <DataGrid ItemsSource="{Binding Tags}" x:Name="Tags"
82 85
                    AutoGenerateColumns="False" CanUserAddRows="True" >
83 86
                    <DataGrid.Columns>                        
84 87
                        <DataGridTemplateColumn >
85 88
                            <DataGridTemplateColumn.CellTemplate>
86 89
                                <DataTemplate>
87
                                    <Button Content=" - " 
88
                                cal:Message.Attach="RemoveTag($dataContext)" />
90
                                    <Button Content=" - " Command="DataGrid.DeleteCommand"/>
89 91
                                </DataTemplate>
90 92
                            </DataGridTemplateColumn.CellTemplate>
91 93
                        </DataGridTemplateColumn>
92
                        <DataGridTextColumn Binding="{Binding Name}" Header="Name" />
94
                        <DataGridTextColumn Binding="{Binding Name}" Header="Key" />
93 95
                        <DataGridTextColumn Binding="{Binding Value}" Header="Value"  />                    
94 96
                    </DataGrid.Columns>
95 97
                </DataGrid>
96 98
        </GroupBox>
97
        <GroupBox Header="Permissions" Grid.Row="3" >
99
        <GroupBox Header="Public &amp; Permissions" Grid.Row="3" >
100
            <StackPanel>
101
                <TextBlock Margin="5" Visibility="{Binding Path=IsPublic,FallbackValue=false, Converter={StaticResource BoolToVisible}}">
102
                    <Run Text="Public URL:" />
103
                    <Run Text="{Binding PublicUrl,FallbackValue='http://someurl'}" />
104
                </TextBlock>
105

  
106
                <CheckBox x:Name="IsPublic" Content="Public" Margin="65,5,5,5"/>
98 107
                <DataGrid ItemsSource="{Binding Permissions}" 
99 108
                    AutoGenerateColumns="False" CanUserAddRows="True">
100 109
                    <DataGrid.Columns>
......
111 120
                        <DataGridCheckBoxColumn Binding="{Binding Write}" Header="Write"/>                                           
112 121
                    </DataGrid.Columns>
113 122
                </DataGrid>
123
            </StackPanel>
124
        </GroupBox>
125
        <GroupBox Header="General" Grid.Row="4">
126
            <Grid>
127
                <Grid.Resources>
128
                    <Style x:Key="NameColumnStyle" TargetType="TextBlock">
129
                        <Setter Property="HorizontalAlignment" Value="Right"/>
130
                        <Setter Property="Margin" Value="5,2"/>
131
                    </Style>
132
                    <Style x:Key="ValueColumnStyle" TargetType="TextBox">
133
                        <Setter Property="HorizontalAlignment" Value="Stretch"/>
134
                        <Setter Property="VerticalAlignment" Value="Top"/>
135
                        <Setter Property="Margin" Value="5,2"/>
136
                    </Style>
137
                </Grid.Resources>
138
                <Grid.ColumnDefinitions>
139
                    <ColumnDefinition Width="Auto" />
140
                    <ColumnDefinition Width="*"/>
141
                </Grid.ColumnDefinitions>
142
                <Grid.RowDefinitions>
143
                    <RowDefinition Height="Auto"/>
144
                    <RowDefinition Height="Auto"/>
145
                    <RowDefinition Height="Auto"/>
146
                </Grid.RowDefinitions>
147
                <TextBlock Text="Content Encoding :" Grid.Row="0" Grid.Column="0" Style="{StaticResource ResourceKey=NameColumnStyle}"/>
148
                <TextBlock Text="Content Disposition :" Grid.Row="1" Grid.Column="0" Style="{StaticResource ResourceKey=NameColumnStyle}"/>
149
                <TextBlock Text="Manifest :" Grid.Row="2" Grid.Column="0" Style="{StaticResource ResourceKey=NameColumnStyle}"/>
150
                <TextBox x:Name="ContentEncoding" Text="text/utf8" Grid.Row="0" Grid.Column="1" Style="{StaticResource ResourceKey=ValueColumnStyle}"/>
151
                <TextBox x:Name="ContentDisposition" Grid.Row="1" Grid.Column="1" Style="{StaticResource ResourceKey=ValueColumnStyle}"/>
152
                <TextBox x:Name="Manifest" Grid.Row="2" Grid.Column="1" Style="{StaticResource ResourceKey=ValueColumnStyle}"/>
153
            </Grid>
114 154
        </GroupBox>
115
        <StackPanel Orientation="Horizontal" Grid.Row="4" HorizontalAlignment="Right">
116
            <Button Name="SaveChanges" Content="OK" Margin="5,5,10,5" Style="{StaticResource ButtonStyle}"/>
117
            <Button Name="RejectChanges" Content="Cancel" Margin="5,5,10,5" Style="{StaticResource ButtonStyle}"/>
155
        <StackPanel Orientation="Horizontal" Grid.Row="5" HorizontalAlignment="Right">
156
            <Button Name="SaveChanges" Content="OK" Margin="5,5,10,5" Style="{StaticResource ButtonStyle}" IsDefault="False" />
157
            <Button Name="RejectChanges" Content="Cancel" Margin="5,5,10,5" Style="{StaticResource ButtonStyle}" IsCancel="True" />
118 158
            <Button Name="ApplyChanges" Content="Apply" Style="{StaticResource ButtonStyle}" />
119 159
        </StackPanel>
120 160

  
b/trunk/Pithos.Client.WPF/FileProperties/FilePropertiesViewModel.cs
7 7
using System.Collections;
8 8
using System.Collections.Concurrent;
9 9
using System.Collections.ObjectModel;
10
using System.Collections.Specialized;
10 11
using System.ComponentModel.Composition;
11 12
using System.Diagnostics;
12 13
using System.Diagnostics.Contracts;
......
16 17
using System.Windows.Media.Imaging;
17 18
using Caliburn.Micro;
18 19
using Pithos.Client.WPF.FileProperties;
20
using Pithos.Client.WPF.Properties;
19 21
using Pithos.Interfaces;
22
using Pithos.Network;
20 23

  
21 24
namespace Pithos.Client.WPF
22 25
{
......
42 45
            }
43 46
        }
44 47

  
48

  
49
        private bool _isPublic;
50
        public bool IsPublic
51
        {
52
            get { return _isPublic; }
53
            set
54
            {
55
                _isPublic = value;
56
                NotifyOfPropertyChange(()=>IsPublic);
57
            }
58
        }
59

  
60
        private string _contentDisposition;
61
        public string ContentDisposition
62
        {
63
            get { return _contentDisposition; }
64
            set
65
            {
66
                _contentDisposition = value;
67
                NotifyOfPropertyChange(() => ContentDisposition);
68
            }
69
        }
70

  
71
        private string _contentEncoding;
72
        public string ContentEncoding
73
        {
74
            get { return _contentEncoding; }
75
            set
76
            {
77
                _contentEncoding = value;
78
                NotifyOfPropertyChange(() => ContentEncoding);
79
            }
80
        }
81

  
82

  
83
        private string _manifest;
84
        public string Manifest
85
        {
86
            get { return _manifest; }
87
            set
88
            {
89
                _manifest = value;
90
                NotifyOfPropertyChange(() => Manifest);
91
            }
92
        }
93

  
45 94
        public string Kind { get; set; }
46 95
        public string Size { get; set; }
47 96
        public string ShortSize { get; set; }
......
51 100
        public long Version { get; set; }
52 101
        protected string LocalFileName { get; set; }
53 102
        public BitmapSource FileIcon { get; set; }
103
        public string PublicUrl { get; set; }
54 104

  
55 105
        public string FileName { get; set; }
56 106
        public string Container { get; set; }
57 107

  
108
        public bool TagsChanged { get; private set; }
109
        public bool PermissionsChanged { get; private set; }
110

  
58 111
        public FilePropertiesViewModel(ShellViewModel shell,ObjectInfo pithosFile,string localFileName)
59 112
        {
60 113
            if (shell==null)
......
65 118
                throw new ArgumentNullException("localFileName");
66 119
            Contract.EndContractBlock();
67 120

  
121

  
122
            _tags = new ObservableCollection<Tag>();
123
            _tags.CollectionChanged += (sender, evt) => { TagsChanged = true; };
124
            _permissions = new ObservableCollection<Permission>();
125
            _permissions.CollectionChanged += (sender, evt) => { PermissionsChanged = true; };
126
            
68 127
            Shell = shell;
69 128
            LocalFileName = localFileName;
70 129
            PithosFile = pithosFile;
71 130
            Title = String.Format("{0} Properties", pithosFile.Name);
72 131
        }
73 132

  
133
        
74 134

  
75 135
        protected ShellViewModel Shell { get; set; }
76 136

  
......
103 163
                ModifiedBy = value.ModifiedBy;
104 164
                Version = value.Version??0;
105 165

  
166
                ContentDisposition = value.ContendDisposition;
167
                ContentEncoding = value.ContentEncoding;
168
                Manifest = value.Manifest;
169
                IsPublic = value.IsPublic;
170
                
171
                PublicUrl = String.Format("{0}/v1{1}", Settings.Default.PithosSite ,value.PublicUrl);
172

  
106 173
                using (var icon = Icon.ExtractAssociatedIcon(LocalFileName))
107 174
                {
108 175
                    FileIcon = Imaging.CreateBitmapSourceFromHIcon(icon.Handle, Int32Rect.Empty,
......
113 180
        }
114 181

  
115 182

  
116

  
117
        private readonly ObservableCollection<Tag> _tags = new ObservableCollection<Tag>();
183
        private readonly ObservableCollection<Tag> _tags ;
118 184
        public ObservableCollection<Tag> Tags
119 185
        {
120
            get { return _tags; }
186
            get { return _tags;}
121 187
        }
122 188

  
123
        private readonly ObservableCollection<Permission> _permissions = new ObservableCollection<Permission>();
189
        private readonly ObservableCollection<Permission> _permissions ;
190
        
191

  
124 192
        public ObservableCollection<Permission> Permissions
125 193
        {
126 194
            get { return _permissions; }
......
154 222

  
155 223
        private void DoSave()
156 224
        {
225
            if (TagsChanged)
226
            {
227
                PithosFile.Tags = this.Tags.ToDictionary(tag => tag.Name, tag => tag.Value);
228
            }
157 229
            
230
            if (PermissionsChanged)
231
            {
232
                PithosFile.Permissions = this.Permissions.ToDictionary(perm => perm.UserName, perm => perm.Value);
233
            }
234

  
235
            PithosFile.ContendDisposition = ContentDisposition;
236
            PithosFile.ContentEncoding = ContentEncoding;
237
            PithosFile.Manifest = Manifest;
238
            PithosFile.IsPublic = IsPublic;
239

  
240
            var monitor = Shell.Monitors[PithosFile.Account];
241
            monitor.CloudClient.UpdateMetadata(PithosFile);
242

  
243

  
244
            TagsChanged = false;
245
            PermissionsChanged = false;
158 246
        }
159 247

  
160 248

  
b/trunk/Pithos.Client.WPF/FileProperties/Permission.cs
47 47
            UserName = userName;
48 48
            Read = (string.Compare(permission, "read", true) == 0);
49 49
            Write= (string.Compare(permission, "write", true) == 0);
50
            Value = permission;
50 51
        }
51 52

  
53
        public string Value { get; private set; }
54

  
52 55
        public Permission()
53 56
        {
54 57
            
b/trunk/Pithos.Client.WPF/Preferences/PreferencesViewModel.cs
138 138
       
139 139
        #region Commands
140 140
        
141
        public bool CanSelectiveSyncFolders
142
        {
143
            get { return CurrentAccount != null; }
144
        }
145

  
141 146
        public void SelectiveSyncFolders()
142 147
        {
143 148
            var monitor = Shell.Monitors[CurrentAccount.AccountName];
......
285 290

  
286 291
                Settings.ExtensionsActivated = value;
287 292

  
288
                //if (value)
289
                //    _extensionController.RegisterExtensions();
290
                //else
291
                //{
292
                //    _extensionController.UnregisterExtensions();
293
                //}
293
/*
294
                if (value)
295
                    _extensionController.RegisterExtensions();
296
                else
297
                {
298
                    _extensionController.UnregisterExtensions();
299
                }
300
*/
294 301
                NotifyOfPropertyChange(() => ExtensionsActivated);
295 302
            }
296 303
        }
......
332 339
                _currentAccount = value;
333 340
                NotifyOfPropertyChange(()=>CurrentAccount);
334 341
                NotifyOfPropertyChange(() => CanRemoveAccount);
342
                NotifyOfPropertyChange(() => CanSelectiveSyncFolders);
343
                NotifyOfPropertyChange(() => CanMoveAccountFolder);
335 344
            }
336 345
        }
337 346

  
......
348 357
*/
349 358

  
350 359

  
351

  
360
        public bool CanMoveAccountFolder
361
        {
362
            get { return CurrentAccount != null; }
363
        }
352 364

  
353 365
    public void MoveAccountFolder()
354 366
    {
b/trunk/Pithos.Client.WPF/Properties/Settings.Designer.cs
261 261
                this["Accounts"] = value;
262 262
            }
263 263
        }
264
        
265
        [global::System.Configuration.UserScopedSettingAttribute()]
266
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
267
        [global::System.Configuration.DefaultSettingValueAttribute("True")]
268
        public bool MustUpgrade {
269
            get {
270
                return ((bool)(this["MustUpgrade"]));
271
            }
272
            set {
273
                this["MustUpgrade"] = value;
274
            }
275
        }
264 276
    }
265 277
}
b/trunk/Pithos.Client.WPF/Properties/Settings.settings
66 66
      <Value Profile="(Default)">&lt;?xml version="1.0" encoding="utf-16"?&gt;
67 67
&lt;ArrayOfAccountSettings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" /&gt;</Value>
68 68
    </Setting>
69
    <Setting Name="MustUpgrade" Type="System.Boolean" Scope="User">
70
      <Value Profile="(Default)">True</Value>
71
    </Setting>
69 72
  </Settings>
70 73
</SettingsFile>
b/trunk/Pithos.Client.WPF/Services/StatusService.cs
18 18
    /// <summary>
19 19
    /// TODO: Update summary.
20 20
    /// </summary>
21
    [ServiceBehavior(UseSynchronizationContext=false, IncludeExceptionDetailInFaults = true)]
21
    [ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Reentrant, UseSynchronizationContext=false, IncludeExceptionDetailInFaults = true)]
22 22
    [Export]
23 23
    public class StatusService : IStatusService,ISettingsService, ICommandsService
24 24
    {
b/trunk/Pithos.Client.WPF/Shell/FeedbackViewModel.cs
4 4
// </copyright>
5 5
// -----------------------------------------------------------------------
6 6

  
7
using System.Collections.Specialized;
7 8
using System.ComponentModel.Composition;
8 9
using System.Diagnostics;
9 10
using System.Diagnostics.Contracts;
......
95 96
            TryClose();
96 97
        }
97 98

  
98
        private async void PostForm(string formUrl, string token,Dictionary<string, string> fields)
99
        private void PostForm(string formUrl, string token,Dictionary<string, string> fields)
99 100
        {
100 101

  
101 102
            if (String.IsNullOrWhiteSpace(formUrl))
......
106 107
                throw new ArgumentNullException("fields");
107 108
            Contract.EndContractBlock();
108 109

  
109
            var request = WebRequest.Create(formUrl);
110
            request.Method = "POST";
111
            request.Headers.Add("X-Auth-Token",token);
112 110

  
111
            var client = new WebClient();
112
            client.Headers.Add("X-Auth-Token", token);
113 113

  
114
            var builder = new StringBuilder();
115
            foreach (var field in fields)
116
            {
117
                builder.AppendFormat("{0}={1}&", field.Key, Uri.EscapeDataString(field.Value));
118
            }
119

  
120
            var postData = builder.ToString().TrimEnd('&');
121
            var byteArray = Encoding.UTF8.GetBytes(postData);
122

  
123
            request.ContentType = "application/x-www-form-urlencoded";
124
            request.ContentLength = byteArray.Length;
125
            using (var stream = request.GetRequestStream())
114
            var values=new NameValueCollection();
115
            fields.Apply(field => values.Add(field.Key, Uri.EscapeDataString(field.Value)));
116
            try
126 117
            {
127
                stream.Write(byteArray, 0, byteArray.Length);
118
                client.UploadValues(formUrl, values);
128 119
            }
129
            
130

  
131
            // Get the response.
132
            var response = await request.GetResponseAsync();
133

  
134

  
135
            var webResponse = ((HttpWebResponse)response);
136
            var result=webResponse.StatusCode;
137
            if (result != HttpStatusCode.OK)
120
            catch (WebException exc)
138 121
            {
139
                // Get the stream containing content returned by the server.
140
                using (var responseStream = webResponse.GetResponseStream())
141
                using (var reader = new StreamReader(responseStream))
142
                {
143
                    var responseFromServer = reader.ReadToEnd();
144
                    Log.WarnFormat("Unexpected status returned from feedback form: {0} - {1}\r\n{2}",result,webResponse.StatusDescription, responseFromServer);                    
145
                }
122
                Log.WarnFormat("Unexpected status returned from feedback form: {0} - {1}", exc.Status,exc.Message);                    
146 123
            }
147
            response.Close();
148 124
        }
149 125
    }
150 126
}
b/trunk/Pithos.Client.WPF/Shell/ShellViewModel.cs
335 335
            var account = pair.Key;
336 336
            var accountMonitor = pair.Value;
337 337

  
338
            if (accountMonitor == null)
339
                return;
340

  
338 341
            ObjectInfo info = accountMonitor.GetObjectInfo(filePath);
339 342

  
340 343
            
b/trunk/Pithos.Client.WPF/app.config
78 78
            xmlns:xsd="http://www.w3.org/2001/XMLSchema" />
79 79
        </value>
80 80
      </setting>
81
      <setting name="MustUpgrade" serializeAs="String">
82
        <value>True</value>
83
      </setting>
81 84
    </Pithos.Client.WPF.Properties.Settings>
82 85
  </userSettings>
83 86
  <connectionStrings>
b/trunk/Pithos.Core/Agents/Agent.cs
29 29
            _messages.Add(message);
30 30
        }
31 31

  
32
        /// <summary>
33
        /// Receives a message asynchronously, optionally with a timeout. Receive throws a TimeoutException if the timeout expires
34
        /// </summary>
35
        /// <param name="timeout">Optional timeout in milliseconds. If provided, Receive fails with a TimeoutException if no message is available in the specified time</param>
36
        /// <returns>A Task that will return the message asynchronously</returns>
32 37
        public Task<TMessage> Receive(int timeout = -1)
33 38
        {
34 39
            return Task<TMessage>.Factory.StartNew(() =>
......
40 45
            });
41 46
        }
42 47

  
48

  
49
        /// <summary>
50
        /// Receives a message asynchronously, optionally with a timeout. TryReceive returns an empty task if the timeout expires
51
        /// </summary>
52
        /// <param name="timeout">Optional timeout in milliseconds. If provided, Receive returns an empty task</param>
53
        /// <returns>A Task that will return the message asynchronously</returns>
43 54
        public Task<TMessage> TryReceive(int timeout = -1)
44 55
        {
45 56
            return Task<TMessage>.Factory.StartNew(() =>
......
52 63

  
53 64

  
54 65

  
55

  
66
        /// <summary>
67
        /// Start the agent
68
        /// </summary>
56 69
        public void Start()
57 70
        {
58 71
            Task.Factory.StartNew(() => _process(this), CancellationToken);            
59 72
        }
60 73

  
61 74

  
62

  
75
        /// <summary>
76
        /// Create and start a new agent for the specified type of message
77
        /// </summary>
78
        /// <param name="action">The message processing action</param>
79
        /// <returns>A started Agent</returns>
63 80
        public static Agent<TMessage> Start(Action<Agent<TMessage>> action)
64 81
        {
65 82
            var agent = new Agent<TMessage>(action);
......
67 84
            return agent;
68 85
        }
69 86

  
87
        /// <summary>
88
        /// Stops the agent 
89
        /// </summary>
70 90
        public void Stop()
71 91
        {
92
            //Stop the message queue
72 93
            _messages.CompleteAdding();
94
            //And signal the cancellation
73 95
            _cancelSource.Cancel();
74 96
        }
75 97

  
98
        /// <summary>
99
        /// Execute an action asynchronously, using the agent's cancellation source
100
        /// </summary>
101
        /// <param name="action">The action to execute</param>
76 102
        public void DoAsync(Action action)
77 103
        {
78 104
            Contract.Requires(action!=null);
......
106 132
            return _messages;
107 133
        }
108 134

  
135

  
109 136
        public Task LoopAsync(Task process, Action loop,Action<Exception> onError=null)
110 137
        {
111 138
            Contract.Requires(process!=null);
b/trunk/Pithos.Interfaces/ObjectInfo.cs
4 4
using System.Dynamic;
5 5
using System.Globalization;
6 6
using System.IO;
7
using System.Linq;
8
using System.Text;
7 9
using System.Text.RegularExpressions;
8 10
using Newtonsoft.Json;
9 11

  
......
79 81

  
80 82
        public string Container { get; set; }
81 83

  
84
        public string ContendDisposition { get; set; }
85

  
86
        public string ContentEncoding { get; set; }
87

  
88
        public string Manifest { get; set; }
89

  
90
        private bool _isPublic;
91
        public bool IsPublic
92
        {
93
            get { return !String.IsNullOrWhiteSpace(PublicUrl); }
94
            set
95
            {
96
                if (!value)
97
                    PublicUrl = "false";
98
                else if (String.IsNullOrWhiteSpace(PublicUrl))
99
                    PublicUrl="true";                
100
            }
101
        }
102

  
103
        public string PublicUrl { get; set; }
104

  
82 105
        public ObjectInfo()
83 106
        {}
84 107

  
......
180 203
            return false;
181 204
        }
182 205

  
206
        public string GetPermissionString()
207
        {
208
            var permissionBuilder = new StringBuilder();
209
            var groupings = Permissions.GroupBy(pair => pair.Value);
210
            foreach (var grouping in groupings)
211
            {
212
                permissionBuilder.AppendFormat("{0}={1}", grouping.Key, String.Join(",", grouping));
213
            }
214
            var permissions = permissionBuilder.ToString();
215
            return permissions;
216
        }
183 217
    }
184 218
}
b/trunk/Pithos.Network/CloudFilesClient.cs
5 5

  
6 6
using System;
7 7
using System.Collections.Generic;
8
using System.Collections.Specialized;
8 9
using System.ComponentModel.Composition;
9 10
using System.Diagnostics.Contracts;
10 11
using System.IO;
......
399 400
            }
400 401
        }
401 402

  
403
        public void UpdateMetadata(ObjectInfo objectInfo)
404
        {
405
            if (objectInfo == null)
406
                throw new ArgumentNullException("objectInfo");
407
            Contract.EndContractBlock();
408

  
409
            using (log4net.ThreadContext.Stacks["Objects"].Push("UpdateMetadata"))
410
            {
411
                if (Log.IsDebugEnabled) Log.DebugFormat("START");
412

  
413

  
414
                using(var client=new RestClient(_baseClient))
415
                {
416

  
417
                    client.BaseAddress = GetAccountUrl(objectInfo.Account);
418
                    
419
                    client.Parameters.Clear();
420
                    
421

  
422
                    //Set Tags
423
                    foreach (var tag in objectInfo.Tags)
424
                    {
425
                        var headerTag = String.Format("X-Object-Meta-{0}", tag.Key);
426
                        client.Headers.Add(headerTag, tag.Value);
427
                    }
428

  
429
                    //Set Permissions
430

  
431
                    var permissions=objectInfo.GetPermissionString();
432
                    client.SetNonEmptyHeaderValue("X-Object-Sharing",permissions);
433

  
434
                    client.SetNonEmptyHeaderValue("Content-Disposition",objectInfo.ContendDisposition);
435
                    client.SetNonEmptyHeaderValue("Content-Encoding",objectInfo.ContentEncoding);
436
                    client.SetNonEmptyHeaderValue("X-Object-Manifest",objectInfo.Manifest);
437
                    client.SetNonEmptyHeaderValue("X-Object-Public", objectInfo.PublicUrl);
438

  
439

  
440
                    var uriBuilder = client.GetAddressBuilder(objectInfo.Container, objectInfo.Name);
441
                    var uri = uriBuilder.Uri;
442

  
443
                    var content = client.UploadValues(uri,new NameValueCollection());
444

  
445

  
446
                    client.AssertStatusOK("UpdateMetadata failed");
447
                    //If the status is NOT ACCEPTED or OK we have a problem
448
                    if (!(client.StatusCode == HttpStatusCode.Accepted || client.StatusCode == HttpStatusCode.OK))
449
                    {
450
                        Log.Error("Failed to update metadata");
451
                        throw new Exception("Failed to update metadata");
452
                    }
453

  
454
                    if (Log.IsDebugEnabled) Log.DebugFormat("END");
455
                }
456
            }
457

  
458
        }
459

  
402 460

  
403 461
        public IList<ObjectInfo> ListObjects(string account, string container, DateTime? since = null)
404 462
        {
......
575 633
                                var tags = (from key in keys
576 634
                                            where key.StartsWith("X-Object-Meta-")
577 635
                                            let name = key.Substring(14)
578
                                            select new {Name = name, Value = client.ResponseHeaders[name]})
636
                                            select new {Name = name, Value = client.ResponseHeaders[key]})
579 637
                                    .ToDictionary(t => t.Name, t => t.Value);
580 638
                                var extensions = (from key in keys
581 639
                                                  where key.StartsWith("X-Object-") && !key.StartsWith("X-Object-Meta-")
582 640
                                                  select new {Name = key, Value = client.ResponseHeaders[key]})
583 641
                                    .ToDictionary(t => t.Name, t => t.Value);
642
                                
643
                                
584 644
                                var info = new ObjectInfo
585 645
                                               {
586 646
                                                   Account = account,
......
591 651
                                                   Bytes = Convert.ToInt64(client.GetHeaderValue("Content-Length")),
592 652
                                                   Tags = tags,
593 653
                                                   Last_Modified = client.LastModified,
594
                                                   Extensions = extensions
654
                                                   Extensions = extensions,
655
                                                   ContentEncoding=client.GetHeaderValue("Content-Encoding",true),
656
                                                   ContendDisposition = client.GetHeaderValue("Content-Disposition",true),
657
                                                   Manifest=client.GetHeaderValue("X-Object-Manifest",true),
658
                                                   PublicUrl=client.GetHeaderValue("X-Object-Public",true)
595 659
                                               };
596 660
                                return info;
597 661
                            case HttpStatusCode.NotFound:
b/trunk/Pithos.Network/ICloudClient.cs
59 59
        #endregion
60 60

  
61 61
        AccountInfo GetAccountPolicies(AccountInfo accountInfo);
62

  
63
        void UpdateMetadata(ObjectInfo objectInfo);
62 64
    }
63 65

  
64 66

  
......
124 126
            return default(AccountInfo);
125 127
        }
126 128

  
129
        public void UpdateMetadata(ObjectInfo objectInfo)
130
        {
131
            Contract.Requires(objectInfo!=null);
132

  
133
            return ;
134
        }
135

  
127 136

  
128 137
        public IList<ObjectInfo> ListObjects(string account, string container, DateTime? since = null)
129 138
        {
b/trunk/Pithos.Network/RestClient.cs
220 220
            RetryWithoutContent(address, retries, "DELETE");
221 221
        }
222 222

  
223
        public string GetHeaderValue(string headerName)
223
        public string GetHeaderValue(string headerName,bool optional=false)
224 224
        {
225 225
            if (this.ResponseHeaders==null)
226 226
                throw new InvalidOperationException("ResponseHeaders are null");
227 227
            Contract.EndContractBlock();
228 228

  
229 229
            var values=this.ResponseHeaders.GetValues(headerName);
230
            if (values == null)
231
                throw new WebException(String.Format("The {0}  header is missing", headerName));
232
            else
230
            if (values != null)
233 231
                return values[0];
232

  
233
            if (optional)            
234
                return null;            
235
            //A required header was not found
236
            throw new WebException(String.Format("The {0}  header is missing", headerName));
237
        }
238

  
239
        public void SetNonEmptyHeaderValue(string headerName, string value)
240
        {
241
            if (String.IsNullOrWhiteSpace(value))
242
                return;
243
            Headers.Add(headerName,value);
234 244
        }
235 245

  
236 246
        private void RetryWithoutContent(string address, int retries, string method)
b/trunk/Pithos.ShellExtensions/FileContext.cs
34 34
            {
35 35
                
36 36
                var accountPath=(from account in Settings.Accounts
37
                                where CurrentFile.StartsWith(account.RootPath, StringComparison.InvariantCultureIgnoreCase)
37
                                where !String.IsNullOrWhiteSpace(account.RootPath) && CurrentFile.StartsWith(account.RootPath, StringComparison.InvariantCultureIgnoreCase)
38 38
                                select account.RootPath).FirstOrDefault();
39 39
                Debug.WriteLine(String.Format("Account path is {0}\r\n Current Path is {1}", accountPath, CurrentFile), LogCategories.Shell);
40 40
                return !String.IsNullOrWhiteSpace(accountPath);
b/trunk/Pithos.ShellExtensions/Menus/DisplayFlags.cs
15 15
        /// <summary>
16 16
        /// Don't display the item at all
17 17
        /// </summary>
18
        None,
18
        None=0,
19 19

  
20 20
        /// <summary>
21 21
        /// Display the item only on folders
22 22
        /// </summary>
23
        Folder,
23
        Folder=1,
24 24

  
25 25
        /// <summary>
26 26
        /// Display the item only on files
27 27
        /// </summary>
28
        File,
28
        File=2,
29 29

  
30 30
        /// <summary>
31 31
        /// Display the item both on container folders
32 32
        /// </summary>
33
        Container,
33
        Container=4,
34 34
        /// <summary>
35 35
        /// Display the item both on folders and files
36 36
        /// </summary>
37
        All
37
        All=7
38 38
    }
39 39
}
b/trunk/Pithos.ShellExtensions/ShellExtLib.cs
163 163
            // HKCR\<File Type> key which contains the ProgID to which the file type 
164 164
            // is linked.
165 165
            if (fileType.StartsWith("."))
166
            {
166
            {                                
167 167
                using (RegistryKey key = Registry.ClassesRoot.OpenSubKey(fileType))
168 168
                {
169 169
                    if (key != null)

Also available in: Unified diff