-<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>
/// <summary>
/// Interaction logic for FilePropertiesView.xaml
/// </summary>
- public partial class FilePropertiesView : UserControl
+ public partial class FilePropertiesView : Window
{
public FilePropertiesView()
{
// </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;
[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;
{
_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
public void Refresh()
{
- PithosFile=Shell.RetrieveObjectInfo(PithosFile);
+ PithosFile=Shell.RefreshObjectInfo(PithosFile);
}
public override void CanClose(Action<bool> callback)
public void SaveChanges()
{
DoSave();
- TryClose(true);
+ TryClose();
}
public void RejectChanges()
{
- TryClose(false);
+ TryClose();
}
public void ApplyChanges()
}
+ 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]);
+ }
+
+
}
}
<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>
using System.ComponentModel.Composition;
[Export(typeof(IShell))]
- public class ShellViewModel : ViewAware, IStatusNotification, IShell,
+ public class ShellViewModel : Screen, IStatusNotification, IShell,
IHandle<Notification>, IHandle<SelectiveSynchChanges>
{
StatusMessage = "In Synch";
- foreach (var account in settings.Accounts)
- {
-
- MonitorAccount(account);
- }
-
- StartStatusService();
}
catch (Exception exc)
{
throw;
}
}
-
-
+ protected override void OnActivate()
+ {
+ base.OnActivate();
+ foreach (var account in Settings.Accounts)
+ {
+
+ MonitorAccount(account);
+ }
+
+ StartStatusService();
+ }
+
public void MonitorAccount(AccountSettings account)
{
Task.Factory.StartNew(() =>
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;
Process.Start(account.AccountPath);
}
- public void GoToSite()
- {
-
- }
-
+
public void GoToSite(AccountInfo account)
{
var site = String.Format("{0}/ui/?token={1}&user={2}",
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()
{
--- /dev/null
+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());
+ }
+ }
+}
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
+ <Compile Include="EnumerableExtensionsTest.cs" />
<Compile Include="NetworkAgentTest.cs" />
<Compile Include="PithosWorkflowTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
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);
--- /dev/null
+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];
+ }
+ }
+}
<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" />
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;
+ }
}
.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