First version of File Properties window. Works with random file
authorPanagiotis Kanavos <pkanavos@gmail.com>
Mon, 31 Oct 2011 21:11:40 +0000 (23:11 +0200)
committerPanagiotis Kanavos <pkanavos@gmail.com>
Mon, 31 Oct 2011 21:11:40 +0000 (23:11 +0200)
12 files changed:
trunk/Pithos.Client.WPF/FileProperties/FilePropertiesView.xaml
trunk/Pithos.Client.WPF/FileProperties/FilePropertiesView.xaml.cs
trunk/Pithos.Client.WPF/FileProperties/FilePropertiesViewModel.cs
trunk/Pithos.Client.WPF/ShellView.xaml
trunk/Pithos.Client.WPF/ShellViewModel.cs
trunk/Pithos.Core.Test/EnumerableExtensionsTest.cs [new file with mode: 0644]
trunk/Pithos.Core.Test/Pithos.Core.Test.csproj
trunk/Pithos.Core/Agents/NetworkAgent.cs
trunk/Pithos.Core/EnumerableExtensions.cs [new file with mode: 0644]
trunk/Pithos.Core/Pithos.Core.csproj
trunk/Pithos.Core/PithosMonitor.cs
trunk/Pithos.Network/CloudFilesClient.cs

index fc3aa8c..50f088d 100644 (file)
-<UserControl x:Class="Pithos.Client.WPF.FilePropertiesView"
+<Window x:Class="Pithos.Client.WPF.FilePropertiesView"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
              mc:Ignorable="d" 
-             d:DesignHeight="300" d:DesignWidth="300">
+             d:DesignHeight="300" d:DesignWidth="300"
+        Background="#FFD4D0C8" Height="481">
+    <Window.Resources>
+        <ResourceDictionary>
+            <ResourceDictionary.MergedDictionaries>
+                <ResourceDictionary Source="..\PithosStyles.xaml" />
+            </ResourceDictionary.MergedDictionaries>
+        </ResourceDictionary>       
+    </Window.Resources>
     <Grid>
-            
+        <Grid.RowDefinitions>
+            <RowDefinition Height="Auto"/>
+            <RowDefinition Height="Auto"/>
+            <RowDefinition Height="*"/>
+            <RowDefinition Height="Auto"/>
+        </Grid.RowDefinitions>
+
+        <Grid Grid.Row="0">
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition Width="Auto"/>
+                <ColumnDefinition Width="*"/>
+                <ColumnDefinition Width="Auto"/>
+            </Grid.ColumnDefinitions>
+            <Image x:Name="FileIcon" Width="48" Height="48" Margin="5" Grid.Column="0" Stretch="None"/>
+            <StackPanel Margin="5" Grid.Column="1">
+                <TextBlock x:Name="FileName" Grid.Row="0" Text="File Name" FontSize="16" FontWeight="Bold"/>
+                <StackPanel Orientation="Horizontal" Grid.Row="1">
+                    <TextBlock Text="Container: " />
+                    <TextBlock x:Name="Container" Text="Container" />
+                </StackPanel>
+            </StackPanel>
+            <TextBlock x:Name="ShortSize" Text="345 KB" FontWeight="Bold" FontSize="14" Grid.Column="2" />
+        </Grid>
+        <GroupBox Header="General" Grid.Row="1">
+            <Grid>
+                <Grid.Resources>
+                    <Style x:Key="NameColumnStyle" TargetType="TextBlock">
+                        <Setter Property="HorizontalAlignment" Value="Right"/>
+                        <Setter Property="Margin" Value="5,2"/>
+                    </Style>
+                    <Style x:Key="ValueColumnStyle" TargetType="TextBlock">
+                        <Setter Property="HorizontalAlignment" Value="Left"/>
+                        <Setter Property="Margin" Value="5,2"/>
+                    </Style>
+                </Grid.Resources>
+                <Grid.ColumnDefinitions>
+                    <ColumnDefinition Width="Auto" />
+                    <ColumnDefinition Width="*"/>
+                </Grid.ColumnDefinitions>
+                <Grid.RowDefinitions>
+                    <RowDefinition/>
+                    <RowDefinition/>
+                    <RowDefinition/>
+                    <RowDefinition/>
+                    <RowDefinition/>
+                    <RowDefinition/>
+                </Grid.RowDefinitions>
+                <TextBlock Text="Kind :" Grid.Row="0" Grid.Column="0" Style="{StaticResource ResourceKey=NameColumnStyle}"/>
+                <TextBlock Text="Size :" Grid.Row="1" Grid.Column="0" Style="{StaticResource ResourceKey=NameColumnStyle}"/>
+                <TextBlock Text="Where :" Grid.Row="2" Grid.Column="0" Style="{StaticResource ResourceKey=NameColumnStyle}"/>
+                <TextBlock Text="Modified :" Grid.Row="3" Grid.Column="0" Style="{StaticResource ResourceKey=NameColumnStyle}"/>
+                <TextBlock Text="Modified By :" Grid.Row="4" Grid.Column="0" Style="{StaticResource ResourceKey=NameColumnStyle}"/>
+                <TextBlock Text="Version :" Grid.Row="5" Grid.Column="0" Style="{StaticResource ResourceKey=NameColumnStyle}"/>
+                <TextBlock x:Name="Kind" Text="application/pdf" Grid.Row="0" Grid.Column="1" Style="{StaticResource ResourceKey=ValueColumnStyle}"/>
+                <TextBlock x:Name="Size" Text="345 KB (345,332 bytes)" Grid.Row="1" Grid.Column="1" Style="{StaticResource ResourceKey=ValueColumnStyle}"/>
+                <TextBlock x:Name="Where" Text="pithos/somefile.pdf" Grid.Row="2" Grid.Column="1" Style="{StaticResource ResourceKey=ValueColumnStyle}"/>
+                <TextBlock x:Name="Modified" Text="28/10/2011 11:34 AM" Grid.Row="3" Grid.Column="1" Style="{StaticResource ResourceKey=ValueColumnStyle}"/>
+                <TextBlock x:Name="ModifiedBy" Text="SomeUser" Grid.Row="4" Grid.Column="1" Style="{StaticResource ResourceKey=ValueColumnStyle}"/>
+                <TextBlock x:Name="Version" Text="2345456" Grid.Row="5" Grid.Column="1" Style="{StaticResource ResourceKey=ValueColumnStyle}"/>
+            </Grid>
+        </GroupBox>
+        <GroupBox Header="Metadata" Grid.Row="2" >
+            <ListView x:Name="GeneralProperties">
+                <ListView.View>
+                    <GridView>
+                        <GridViewColumn  >
+                            <GridViewColumn.CellTemplate>
+                                <DataTemplate>
+                                    <Button Content="-" Width="20"/>
+                                </DataTemplate>
+                            </GridViewColumn.CellTemplate>
+                        </GridViewColumn>
+                        <GridViewColumn Header="Name"/>
+                        <GridViewColumn Header="Value" />                
+                </GridView>
+            </ListView.View>
+                <ListViewItem Content="Moo">                                        
+                </ListViewItem>
+            </ListView>
+        </GroupBox>
+        <StackPanel Orientation="Horizontal" Grid.Row="3" HorizontalAlignment="Right">
+            <Button Name="SaveChanges" Content="OK" Margin="5,5,10,5" Style="{StaticResource ButtonStyle}"/>
+            <Button Name="RejectChanges" Content="Cancel" Margin="5,5,10,5" Style="{StaticResource ButtonStyle}"/>
+            <Button Name="ApplyChanges" Content="Apply" Style="{StaticResource ButtonStyle}" />
+        </StackPanel>
+
     </Grid>
-</UserControl>
+</Window>
index 2690f9d..cb193a5 100644 (file)
@@ -17,7 +17,7 @@ namespace Pithos.Client.WPF
     /// <summary>
     /// Interaction logic for FilePropertiesView.xaml
     /// </summary>
-    public partial class FilePropertiesView : UserControl
+    public partial class FilePropertiesView : Window
     {
         public FilePropertiesView()
         {
index e417282..7a87938 100644 (file)
@@ -4,9 +4,16 @@
 // </copyright>
 // -----------------------------------------------------------------------
 
+using System.Collections;
 using System.Collections.Concurrent;
 using System.Collections.ObjectModel;
 using System.ComponentModel.Composition;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+using System.Drawing;
+using System.Windows;
+using System.Windows.Interop;
+using System.Windows.Media.Imaging;
 using Caliburn.Micro;
 using Pithos.Interfaces;
 
@@ -23,13 +30,47 @@ namespace Pithos.Client.WPF
     [Export(typeof(FilePropertiesViewModel))]
     public class FilePropertiesViewModel : Screen, IShell
     {
+        private string _title;
+        public string Title
+        {
+            get { return _title; }
+            set
+            {
+                _title = value;
+                NotifyOfPropertyChange(()=>Title);
+            }
+        }
+
+        public string Kind { get; set; }
+        public string Size { get; set; }
+        public string ShortSize { get; set; }
+        public string Where { get; set; }
+        public DateTime Modified { get; set; }
+        public string ModifiedBy { get; set; }
+        public long Version { get; set; }
+        protected string LocalFileName { get; set; }
+        public BitmapSource FileIcon { get; set; }
 
-        public FilePropertiesViewModel(ShellViewModel shell,ObjectInfo pithosFile)
+        public string FileName { get; set; }
+        public string Container { get; set; }
+
+        public FilePropertiesViewModel(ShellViewModel shell,ObjectInfo pithosFile,string localFileName)
         {
+            if (shell==null)
+                throw new ArgumentNullException("shell");
+            if (pithosFile==null)
+                throw new ArgumentNullException("pithosFile");
+            if (String.IsNullOrWhiteSpace(localFileName))
+                throw new ArgumentNullException("localFileName");
+            Contract.EndContractBlock();
+
             Shell = shell;
+            LocalFileName = localFileName;
             PithosFile = pithosFile;
+            Title = String.Format("{0} Properties", pithosFile.Name);
         }
 
+
         protected ShellViewModel Shell { get; set; }
 
         private ObjectInfo _pithosFile;
@@ -40,9 +81,29 @@ namespace Pithos.Client.WPF
             {
                 _pithosFile = value;
                 
+                if (Permissions!=null)
+                    ((IDictionary<string,string>)Permissions).Clear();                
+                value.Permissions.Apply(perm=>Permissions.Add(perm.Key,perm.Value));
+                Kind=value.Content_Type;
+                ShortSize = ByteSize(value.Bytes);
+                Size = String.Format("{0} ({1:N0} bytes)", ShortSize, value.Bytes);
+                Where = Uri.UnescapeDataString(value.Name);
+                FileName = Uri.UnescapeDataString(value.Name.Split('/').Last());
+                Container = value.Container;
+                Modified = value.Last_Modified;
+                ModifiedBy = value.ModifiedBy;
+                Version = value.Version??0;
+
+                using (var icon = Icon.ExtractAssociatedIcon(LocalFileName))
+                {
+                    FileIcon = Imaging.CreateBitmapSourceFromHIcon(icon.Handle, Int32Rect.Empty,
+                                                                   BitmapSizeOptions.FromEmptyOptions());
+                }
                 NotifyOfPropertyChange(()=>PithosFile);
             }
         }
+        
+
 
         private ObservableConcurrentDictionary<string, string> _permissions;
         public ObservableConcurrentDictionary<string, string> Permissions
@@ -57,7 +118,7 @@ namespace Pithos.Client.WPF
 
         public void Refresh()
         {
-            PithosFile=Shell.RetrieveObjectInfo(PithosFile);
+            PithosFile=Shell.RefreshObjectInfo(PithosFile);
         }
 
         public override void CanClose(Action<bool> callback)
@@ -68,12 +129,12 @@ namespace Pithos.Client.WPF
         public void SaveChanges()
         {
             DoSave();
-            TryClose(true);
+            TryClose();
         }
 
         public void RejectChanges()
         {
-            TryClose(false);
+            TryClose();
         }
 
         public void ApplyChanges()
@@ -86,5 +147,32 @@ namespace Pithos.Client.WPF
             
         }
 
+        static string[] sizeSuffixes = { 
+        "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
+
+        public string ByteSize(long size)
+        {
+            
+            const string formatTemplate = "{0}{1:0.#} {2}";
+
+            if (size == 0)
+            {
+                return string.Format(formatTemplate, null, 0, sizeSuffixes[0]);
+            }
+
+            var absSize = Math.Abs((double)size);
+            var fpPower = Math.Log(absSize, 1000);
+            var intPower = (int)fpPower;
+            var iUnit = intPower >= sizeSuffixes.Length
+                ? sizeSuffixes.Length - 1
+                : intPower;
+            var normSize = absSize / Math.Pow(1000, iUnit);
+
+            return string.Format(
+                formatTemplate,
+                size < 0 ? "-" : null, normSize, sizeSuffixes[iUnit]);
+        } 
+
+
     }
 }
index 5550f69..a464355 100644 (file)
@@ -70,6 +70,8 @@
                     <Separator  />
                     <MenuItem  Header="Preferences ..." x:Name="ShowPreferences" cal:Message.Attach="ShowPreferences"  />
                     <Separator  />
+                    <MenuItem  Header="Properties ..." x:Name="ShowFileProperties" cal:Message.Attach="ShowFileProperties"  />
+                    <Separator  />
                     <MenuItem  Header="Exit" Name="ExitPithos" cal:Message.Attach="ExitPithos" />
                 </ContextMenu>
             </tb:TaskbarIcon.ContextMenu>
index 8254ada..02ca514 100644 (file)
@@ -27,7 +27,7 @@ namespace Pithos.Client.WPF {
     using System.ComponentModel.Composition;
 
     [Export(typeof(IShell))]
-    public class ShellViewModel : ViewAware, IStatusNotification, IShell, 
+    public class ShellViewModel : Screen, IStatusNotification, IShell, 
         IHandle<Notification>, IHandle<SelectiveSynchChanges>
     {
        
@@ -65,13 +65,6 @@ namespace Pithos.Client.WPF {
 
                 StatusMessage = "In Synch";
 
-                foreach (var account in settings.Accounts)
-                {
-
-                    MonitorAccount(account);
-                }
-
-                StartStatusService();
             }
             catch (Exception exc)
             {
@@ -79,9 +72,19 @@ namespace Pithos.Client.WPF {
                 throw;
             }
         }
-        
 
-        
+        protected override void OnActivate()
+        {
+            base.OnActivate();
+            foreach (var account in Settings.Accounts)
+            {
+
+                MonitorAccount(account);
+            }
+
+            StartStatusService();
+        }
+
         public void MonitorAccount(AccountSettings account)
         {
             Task.Factory.StartNew(() =>
@@ -105,15 +108,16 @@ namespace Pithos.Client.WPF {
                     return;
                 }
 
-                //PithosMonitor uses MEF so we need to resolve it
+                //Create a new monitor/ Can't use MEF here, it would return a single instance for all monitors
                 monitor = new PithosMonitor
-                            {
-                                UserName = accountName,
-                                ApiKey = account.ApiKey,
-                                UsePithos = account.UsePithos,
-                                StatusNotification = this,
-                                RootPath = account.RootPath
-                            };
+                              {
+                                  UserName = accountName,
+                                  ApiKey = account.ApiKey,
+                                  UsePithos = account.UsePithos,
+                                  StatusNotification = this,
+                                  RootPath = account.RootPath
+                              };
+                //PithosMonitor uses MEF so we need to resolve it
                 IoC.BuildUp(monitor);
 
                 var appSettings = Properties.Settings.Default;
@@ -221,11 +225,7 @@ namespace Pithos.Client.WPF {
             Process.Start(account.AccountPath);
         }
 
-        public void GoToSite()
-        {
-            
-        }
-
+        
         public void GoToSite(AccountInfo account)
         {
             var site = String.Format("{0}/ui/?token={1}&user={2}",
@@ -234,6 +234,48 @@ namespace Pithos.Client.WPF {
             Process.Start(site);
         }
 
+        public void ShowFileProperties()
+        {
+            var account = Settings.Accounts.First(acc => acc.IsActive);            
+            var dir = new DirectoryInfo(account.RootPath + @"\pithos");
+            var files=dir.GetFiles();
+            var r=new Random();
+            var idx=r.Next(0, files.Length);
+            ShowFileProperties(files[idx].FullName);            
+        }
+
+        public void ShowFileProperties(string filePath)
+        {
+            if (String.IsNullOrWhiteSpace(filePath))
+                throw new ArgumentNullException("filePath");
+            if (!File.Exists(filePath))
+                throw new ArgumentException(String.Format("Non existent file {0}",filePath),"filePath");
+            Contract.EndContractBlock();
+
+            var pair=(from monitor in  Monitors
+                               where filePath.StartsWith(monitor.Value.RootPath, StringComparison.InvariantCultureIgnoreCase)
+                                   select monitor).FirstOrDefault();
+            var account = pair.Key;
+            var accountMonitor = pair.Value;
+
+            ObjectInfo info = accountMonitor.GetObjectInfo(filePath);
+
+            
+
+            var fileProperties = new FilePropertiesViewModel(this, info,filePath);
+            _windowManager.ShowWindow(fileProperties);
+        }
+
+        public ObjectInfo RefreshObjectInfo(ObjectInfo currentInfo)
+        {
+            if (currentInfo==null)
+                throw new ArgumentNullException("currentInfo");
+            Contract.EndContractBlock();
+
+            var monitor = Monitors[currentInfo.Account];
+            var newInfo=monitor.CloudClient.GetObjectInfo(currentInfo.Account, currentInfo.Container, currentInfo.Name);
+            return newInfo;
+        }
 
         public void ToggleSynching()
         {
diff --git a/trunk/Pithos.Core.Test/EnumerableExtensionsTest.cs b/trunk/Pithos.Core.Test/EnumerableExtensionsTest.cs
new file mode 100644 (file)
index 0000000..07bd91b
--- /dev/null
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using NUnit.Framework;
+
+namespace Pithos.Core.Test
+{
+    [TestFixture]
+    public class EnumerableExtensionsTest
+    {
+        [Test]
+        public void TestSplice()
+        {
+            string[] twoElems= {"one","two"};
+            var result=twoElems.Splice(1);
+            Assert.AreEqual(1,result.Count());
+            
+            result=twoElems.Splice(0);
+            Assert.AreEqual(2,result.Count());
+        }
+
+        [Test]
+        public void TestSpliceEmpty()
+        {
+            string[] empty= {};
+            var result=empty.Splice(1);
+            Assert.AreEqual(0,result.Count());
+            
+            result=empty.Splice(0);
+            Assert.AreEqual(0,result.Count());
+        }
+    }
+}
index 33e8c3e..428c2a1 100644 (file)
     <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="EnumerableExtensionsTest.cs" />
     <Compile Include="NetworkAgentTest.cs" />
     <Compile Include="PithosWorkflowTest.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
index f3c34f7..180118b 100644 (file)
@@ -832,7 +832,7 @@ namespace Pithos.Core.Agents
                 var parts=relativePath.Split('\\');
                 var accountName = parts[1];
                 var oldName = accountInfo.UserName;
-                   var absoluteUri = accountInfo.StorageUri.AbsoluteUri;
+                var absoluteUri = accountInfo.StorageUri.AbsoluteUri;
                 var nameIndex=absoluteUri.IndexOf(oldName);
                 var root=absoluteUri.Substring(0, nameIndex);
 
diff --git a/trunk/Pithos.Core/EnumerableExtensions.cs b/trunk/Pithos.Core/EnumerableExtensions.cs
new file mode 100644 (file)
index 0000000..27a82d1
--- /dev/null
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.Contracts;
+using System.Linq;
+using System.Text;
+
+namespace Pithos.Core
+{
+    public static class EnumerableExtensions
+    {
+        public static IEnumerable<T> Splice<T>(this IList<T> array,int startIndex,int endIndex=-1)
+        {
+            if (array==null)
+                throw new ArgumentNullException("array");
+            if (startIndex<0)
+                throw new ArgumentOutOfRangeException("startIndex");            
+            Contract.EndContractBlock();
+
+            if (startIndex>=array.Count)
+                yield break;
+
+            if (endIndex == -1)
+                endIndex = array.Count;
+            for (var i = startIndex; i < endIndex; i++)
+                yield return array[i];
+        }
+    }
+}
index 0f453a5..cf9602c 100644 (file)
     <Compile Include="Agents\NetworkAgent.cs" />
     <Compile Include="Agents\WorkflowAgent.cs" />
     <Compile Include="DynamicDictionary.cs" />
+    <Compile Include="EnumerableExtensions.cs" />
     <Compile Include="FileHashMap.cs" />
     <Compile Include="FileState.cs" />
     <Compile Include="IStatusService.cs" />
index a254654..f6216d3 100644 (file)
@@ -412,6 +412,68 @@ namespace Pithos.Core
                        select dir.Name;
             return dirs;
         }
+
+        public ObjectInfo GetObjectInfo(string filePath)
+        {
+            if (String.IsNullOrWhiteSpace(filePath))
+                throw new ArgumentNullException("filePath");
+            Contract.EndContractBlock();
+
+            var file=new FileInfo(filePath);
+            string relativeUrl;//=file.AsRelativeUrlTo(this.RootPath);
+            var relativePath = file.AsRelativeTo(RootPath);
+            
+            string accountName,container;
+            
+            var parts=relativePath.Split('\\');
+
+            var accountInfo = _accountInfo;
+            if (relativePath.StartsWith(FolderConstants.OthersFolder))
+            {                
+                accountName = parts[1];
+                container = parts[2];
+                relativeUrl = String.Join("/", parts.Splice(3));
+                //Create the root URL for the target account
+                var oldName = UserName;
+                var absoluteUri =  _accountInfo.StorageUri.AbsoluteUri;
+                var nameIndex=absoluteUri.IndexOf(oldName);
+                var root=absoluteUri.Substring(0, nameIndex);
+
+                accountInfo = new AccountInfo
+                {
+                    UserName = accountName,
+                    AccountPath = Path.Combine(accountInfo.AccountPath, parts[0], parts[1]),
+                    StorageUri = new Uri(root + accountName),
+                    BlockHash=accountInfo.BlockHash,
+                    BlockSize=accountInfo.BlockSize,
+                    Token=accountInfo.Token
+                };
+            }
+            else
+            {
+                accountName = this.UserName;
+                container = parts[0];
+                relativeUrl = String.Join("/", parts.Splice(1));
+            }
+
+            /*return new ObjectInfo
+                       {
+                           Account = accountName,
+                           Container = container,
+                           Name = relativeUrl,
+                           Content_Type="application/pdf",
+                           Bytes = 123456,
+                           Hash = "8902372934sjhshjfsdjkf223894sdgh",
+                           Last_Modified = DateTime.Today.AddDays(-1),
+                           ModifiedBy = accountName,
+                           Version = 12312455,
+                           VersionTimestamp = DateTime.Today.AddDays(-1)
+                       };*/
+
+            var client = new CloudFilesClient(accountInfo);
+            var objectInfo=client.GetObjectInfo(accountName, container, relativeUrl);
+            return objectInfo;
+        }
     }
 
 
index a33aa0c..1a0ad50 100644 (file)
@@ -537,9 +537,12 @@ namespace Pithos.Network
                                     .ToDictionary(t => t.Name, t => t.Value);
                                 var info = new ObjectInfo
                                                {
+                                                   Account = account,
+                                                   Container = container,
                                                    Name = objectName,
                                                    Hash = client.GetHeaderValue("ETag"),
                                                    Content_Type = client.GetHeaderValue("Content-Type"),
+                                                   Bytes = Convert.ToInt64(client.GetHeaderValue("Content-Length")),
                                                    Tags = tags,
                                                    Last_Modified = client.LastModified,
                                                    Extensions = extensions