{
try
{
+ var stack = new StackTrace();
+ var type = stack.GetFrame(0).GetMethod().DeclaringType;
+
var log = LogManager.GetLogger(source);
var level = GetLevel(eventType);
TaskEx.Run(()=>log.Logger.Log(GetType(), level, message, null));
public ConflictsViewModel()
{
this.DisplayName="Conflicts";
- var conflicts = from state in FileState.Queryable
- where state.FileStatus == FileStatus.Conflict ||
- state.OverlayStatus == FileOverlayStatus.Conflict
+ var fileStates = from state in FileState.Queryable
+ where state.FileStatus == FileStatus.Conflict ||
+ state.OverlayStatus == FileOverlayStatus.Conflict
+ select state;
+ var conflicts = from state in fileStates
let info=FileInfoExtensions.FromPath(state.FilePath)
select new ConflictFile {FilePath = state.FilePath,Reason=state.ConflictReason,LocalModified = info.LastWriteTime};
_conflicts = new ObservableCollection<ConflictFile>(conflicts.ToList());
</TabItem>
<TabItem Header="Permissions">
<StackPanel>
+ <StackPanel Orientation="Horizontal">
+ <TextBox x:Name="PermissionName" Width="200"/>
+ <CheckBox x:Name="PermissionIsReadOnly" Content="Is Read Only" />
+ <Button x:Name="AddPermission" Content="Add"/>
+ </StackPanel>
<TextBlock Margin="5" Visibility="{Binding Path=IsPublic,FallbackValue=Collapsed, Converter={StaticResource BoolToVisible}}">
<Run Text="Public URL:" />
<Run Text="{Binding PublicUrl,FallbackValue='http://someurl'}" />
}
}
+ private string _permissionName;
+ public string PermissionName
+ {
+ get { return _permissionName; }
+ set
+ {
+ _permissionName = value;
+ NotifyOfPropertyChange(()=>PermissionName);
+ }
+ }
+
+ private bool _permissionIsReadOnly;
+ public bool PermissionIsReadOnly
+ {
+ get { return _permissionIsReadOnly; }
+ set
+ {
+ _permissionIsReadOnly = value;
+ NotifyOfPropertyChange(()=>PermissionIsReadOnly);
+ }
+ }
+
+ public bool CanAddPermission
+ {
+ get { return !String.IsNullOrWhiteSpace(PermissionName); }
+ }
+
+ public void AddPermission()
+ {
+ Permissions.Add(new Permission{Read=PermissionIsReadOnly,UserName=PermissionName,Write=!PermissionIsReadOnly});
+ }
+
+
public bool TagsChanged { get; private set; }
public bool PermissionsChanged { get; private set; }
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Configuration.Install" />
<Reference Include="System.Data" />
- <Reference Include="System.Data.SQLite, Version=1.0.79.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=x86">
- <HintPath>..\packages\System.Data.SQLite.1.0.79.0\lib\net40\System.Data.SQLite.dll</HintPath>
+ <Reference Include="System.Data.SQLite, Version=1.0.80.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=x86">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\packages\System.Data.SQLite.1.0.80.0\lib\net40\System.Data.SQLite.dll</HintPath>
</Reference>
- <Reference Include="System.Data.SQLite.Linq">
- <HintPath>..\packages\System.Data.SQLite.1.0.79.0\lib\net40\System.Data.SQLite.Linq.dll</HintPath>
+ <Reference Include="System.Data.SQLite.Linq, Version=1.0.80.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\packages\System.Data.SQLite.1.0.80.0\lib\net40\System.Data.SQLite.Linq.dll</HintPath>
</Reference>
<Reference Include="System.Drawing" />
<Reference Include="System.Runtime.Serialization" />
xmlns:extToolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit/extended"
xmlns:Converters="clr-namespace:Pithos.Client.WPF.Converters"
x:Name="TheView"
- Title="Pithos+ Preferences" Height="436" Width="600"
+ Title="Pithos+ Preferences" Height="436" Width="732"
ShowInTaskbar="true"
WindowStartupLocation="CenterScreen"
Icon="/PithosPlus;component/Images/PithosTaskbar.png"
</Grid.ColumnDefinitions>
<TextBox Name="CurrentAccount_RootPath" Margin="1,5,5,-30" HorizontalAlignment="Stretch" IsReadOnly="True" ToolTip="{Binding CurrentAccount.RootPath}" Height="61" TextWrapping="WrapWithOverflow" />
</Grid>
- <CheckBox Name="CurrentAccount_IsActive" Content="Account is Active" Grid.Row="6" Margin="6,66,126,6" Grid.ColumnSpan="2" />
- <Button Name="SelectiveSyncFolders" Content="Selective Sync" Width="Auto" HorizontalAlignment="Right" Style="{StaticResource ButtonStyle}" Grid.Row="6" Margin="0,40,18,24" Grid.Column="1" />
- <Button Name="MoveAccountFolder" Content="Move ..." Margin="6,40,126,24" Grid.Row="6" Grid.Column="1" />
+ <CheckBox Name="CurrentAccount_IsActive" Content="Account is Active" Grid.Row="6" Margin="6,66,126,6" Grid.ColumnSpan="2" />
+ <Button Name="SelectiveSyncFolders" Content="Selective Sync" Width="Auto" HorizontalAlignment="Right" Style="{StaticResource ButtonStyle}" Grid.Row="6" Margin="0,40,138,24" Grid.Column="1" />
+ <Button Name="MoveAccountFolder" Content="Move ..." Margin="20,40,234,24" Grid.Row="6" Grid.Column="1" Width="100"/>
+ <Button Name="ClearAccountCache" Content="Clear Cache" Margin="245,40,9,24" Grid.Row="6" Grid.Column="1" Width="100" />
</Grid>
</GroupBox>
get { return (CurrentAccount != null); }
}
+ public bool CanClearAccountCache
+ {
+ get { return (CurrentAccount != null); }
+ }
+
+ public void ClearAccountCache()
+ {
+ if (MessageBoxResult.Yes == MessageBox.Show("You are about to delete all partially downloaded files from the account's cache.\n" +
+ " You will have to download all partially downloaded data again\n" +
+ "This change can not be undone\n\n" +
+ "Do you wish to delete all partially downloaded data?", "Warning! Clearing account cache",
+ MessageBoxButton.YesNo,MessageBoxImage.Question,MessageBoxResult.No))
+ {
+
+ var cachePath = Path.Combine(CurrentAccount.RootPath, FolderConstants.CacheFolder);
+ var dir = new DirectoryInfo(cachePath);
+ dir.EnumerateFiles().Apply(file=>file.Delete());
+ dir.EnumerateDirectories().Apply(folder => folder.Delete(true));
+ }
+ }
public bool ExtensionsActivated
NotifyOfPropertyChange(() => CanRemoveAccount);
NotifyOfPropertyChange(() => CanSelectiveSyncFolders);
NotifyOfPropertyChange(() => CanMoveAccountFolder);
+ NotifyOfPropertyChange(() => CanClearAccountCache);
}
}
{
var selections = account.SelectiveFolders;
- if (selections.Count == 0)
- return;
+
+
//Initially, all nodes are checked
//We need to *uncheck* the nodes that are not selected
from DirectoryRecord record in rootRecord
select record).ToList();
+ if (selections.Count == 0)
+ {
+ allNodes.Apply(record => record.IsChecked = false);
+ return;
+ }
+
var selects = (from DirectoryRecord rootRecord in RootNodes
from DirectoryRecord record in rootRecord
where record.Uri !=null && !selections.Contains(record.Uri.ToString())
</assemblyBinding>
</runtime>
-<!-- <system.diagnostics >
+<system.diagnostics >
<sources >
<source name="System.Net" switchValue="Verbose" tracemode="protocolonly" maxdatasize="65536" >
<listeners>
</listeners>
</source>
</sources>
- </system.diagnostics>-->
+ </system.diagnostics>
<userSettings>
<Pithos.Client.WPF.Properties.Settings>
<setting name="PithosPath" serializeAs="String">
<package id="Caliburn.Micro" version="1.2.0" />
<package id="Caliburn.Micro.Logging" version="1.2" />
<package id="Extended.Wpf.Toolkit" version="1.5.0" />
- <package id="System.Data.SQLite" version="1.0.79.0" />
+ <package id="System.Data.SQLite" version="1.0.80.0" />
</packages>
\ No newline at end of file
throw new NotImplementedException();
}
- public void SetFileState(string path, FileStatus fileStatus, FileOverlayStatus overlayStatus)
+ public void SetFileState(string path, FileStatus fileStatus, FileOverlayStatus overlayStatus, string localFileMissingFromServer)
{
if (String.IsNullOrWhiteSpace(path))
throw new ArgumentNullException("path", "path can't be empty");
{
class BlockUpdater
{
+ //TODO: Must clean orphaned blocks from the Cache folder.
+ //
+ //The Cache folder may have orphaned blocks. Blocks may be left in the Cache folder because:
+ //1. A download was in progress when the application terminated. These blocks are needed to proceed
+ // with partial download
+ //2. The application terminated abnormally before the blocks were cleared after a download
+ //3. The server file was deleted before the download completed.
+ //
+ //In #1, we need to keep the blocks. We need to detect the other cases and delete orphans
+ //
+ //Mitigations:
+ // - Delete blocks with no corresponding state
+ // - Check and delete possible orphans when a Deletion is detected
+ // - Add Advanced command "Clear Cache"
+ //
+ //Need a better way to differentiate between cases #2, #3 and #1
+
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public string FilePath { get; private set; }
var state = StatusKeeper.GetStateByFilePath(previousFullPath);
state.FilePath = newPath;
state.SaveCopy();
- StatusKeeper.SetFileState(previousFullPath,FileStatus.Deleted,FileOverlayStatus.Deleted);
+ StatusKeeper.SetFileState(previousFullPath,FileStatus.Deleted,FileOverlayStatus.Deleted, "Deleted");
}
}
{\r
if (gate.Failed)\r
continue;\r
- StatusKeeper.SetFileState(item.FullName, FileStatus.Conflict, FileOverlayStatus.Deleted);\r
+ StatusKeeper.SetFileState(item.FullName, FileStatus.Conflict, FileOverlayStatus.Deleted,"Local file missing from server");\r
}\r
}\r
UpdateStatus(PithosStatus.HasConflicts);\r
_lastSeen.TryRemove(item.FullName, out lastDate);\r
deletedFiles.Add(item);\r
}\r
- StatusKeeper.SetFileState(item.FullName, FileStatus.Deleted, FileOverlayStatus.Deleted);\r
+ StatusKeeper.SetFileState(item.FullName, FileStatus.Deleted, FileOverlayStatus.Deleted, "File Deleted");\r
}\r
Log.InfoFormat("[{0}] files were deleted",deletedFiles.Count);\r
StatusNotification.NotifyForFiles(deletedFiles, String.Format("{0} files were deleted", deletedFiles.Count), TraceLevel.Info);\r
Log.DebugFormat("[NEW INFO] {0}",objectInfo.Uri);\r
\r
var relativePath = objectInfo.RelativeUrlToFilePath(accountInfo.UserName);\r
- //If the object already exists, we probably have a conflict\r
+\r
+ //If the object already exists, we should check before uploading or downloading\r
if (fileAgent.Exists(relativePath))\r
{\r
- Log.DebugFormat("[SKIP EXISTING] {0}", objectInfo.Uri);\r
- //If a directory object already exists, we don't need to perform any other action \r
- var localFile = fileAgent.GetFileSystemInfo(relativePath);\r
- StatusKeeper.SetFileState(localFile.FullName, FileStatus.Conflict, FileOverlayStatus.Conflict);\r
+ var localFile= fileAgent.GetFileSystemInfo(relativePath);\r
+ var state = StatusKeeper.GetStateByFilePath(localFile.WithProperCapitalization().FullName);\r
+ yield return new CloudAction(accountInfo, CloudActionType.MustSynch,\r
+ localFile, objectInfo, state, accountInfo.BlockSize,\r
+ accountInfo.BlockHash); \r
}\r
else\r
{\r
//Remote files should be downloaded\r
yield return new CloudDownloadAction(accountInfo, objectInfo);\r
}\r
+\r
}\r
}\r
\r
_persistenceAgent.Post(() =>FileState.RenameState(oldPath, newPath));
}*/
- public void SetFileState(string path, FileStatus fileStatus, FileOverlayStatus overlayStatus)
+ public void SetFileState(string path, FileStatus fileStatus, FileOverlayStatus overlayStatus, string localFileMissingFromServer)
{
if (String.IsNullOrWhiteSpace(path))
throw new ArgumentNullException("path");
await UploadWithHashMap(accountInfo, cloudFile, fileInfo as FileInfo, cloudFile.Name, treeHash);
}
//If everything succeeds, change the file and overlay status to normal
- StatusKeeper.SetFileState(fullFileName, FileStatus.Unchanged, FileOverlayStatus.Normal);
+ StatusKeeper.SetFileState(fullFileName, FileStatus.Unchanged, FileOverlayStatus.Normal, "");
}
catch (WebException exc)
{
throw;
if (response.StatusCode == HttpStatusCode.Forbidden)
{
- StatusKeeper.SetFileState(fileInfo.FullName, FileStatus.Forbidden, FileOverlayStatus.Conflict);
+ StatusKeeper.SetFileState(fileInfo.FullName, FileStatus.Forbidden, FileOverlayStatus.Conflict, "Forbidden");
}
-
- //In any other case, propagate the error
- throw;
+ else
+ //In any other case, propagate the error
+ throw;
}
}
//Notify the Shell to update the overlays
{
Log.Error("Not allowed to upload file", exc);
var message = String.Format("Not allowed to uplad file {0}", action.LocalFile.FullName);
- StatusKeeper.SetFileState(action.LocalFile.FullName, FileStatus.Unchanged, FileOverlayStatus.Normal);
+ StatusKeeper.SetFileState(action.LocalFile.FullName, FileStatus.Unchanged, FileOverlayStatus.Normal, "");
StatusNotification.NotifyChange(message, TraceLevel.Warning);
return true;
}
{
//We reach this point only if the app closed before propagating a rename to the server
Log.WarnFormat("Unfinished rename [{0}]",state.Path);
- StatusKeeper.SetFileState(state.Path,FileStatus.Conflict,FileOverlayStatus.Conflict);
+ StatusKeeper.SetFileState(state.Path,FileStatus.Conflict,FileOverlayStatus.Conflict, "Rename without old path");
break;
}
FileSystemInfo oldInfo = Directory.Exists(state.OldPath)
var account = accountInfo;
var pendingEntries = (from state in FileState.Queryable
where state.FileStatus != FileStatus.Unchanged &&
+ state.FileStatus != FileStatus.Forbidden &&
+ state.FileStatus != FileStatus.Conflict &&
!state.FilePath.StartsWith(cachePath) &&
!state.FilePath.EndsWith(".ignore") &&
state.FilePath.StartsWith(account.AccountPath)
FileOverlayStatus GetFileOverlayStatus(string path);
void ProcessExistingFiles(IEnumerable<FileInfo> paths);
void Stop();
- void SetFileState(string path, FileStatus fileStatus, FileOverlayStatus overlayStatus);
+ void SetFileState(string path, FileStatus fileStatus, FileOverlayStatus overlayStatus, string localFileMissingFromServer);
void StoreInfo(string path, ObjectInfo objectInfo);
//T GetStatus<T>(string path,Func<FileState,T> getter,T defaultValue );
//void SetStatus(string path, Action<FileState> setter);
}
- public void SetFileState(string path, FileStatus fileStatus, FileOverlayStatus overlayStatus)
+ public void SetFileState(string path, FileStatus fileStatus, FileOverlayStatus overlayStatus, string localFileMissingFromServer)
{
Contract.Requires(!String.IsNullOrWhiteSpace(path));
Contract.Requires(Path.IsPathRooted(path));
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Core" />
- <Reference Include="System.Data.SQLite">
- <HintPath>..\packages\System.Data.SQLite.1.0.79.0\lib\net40\System.Data.SQLite.dll</HintPath>
+ <Reference Include="System.Data.SQLite, Version=1.0.80.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=x86">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\packages\System.Data.SQLite.1.0.80.0\lib\net40\System.Data.SQLite.dll</HintPath>
</Reference>
- <Reference Include="System.Data.SQLite.Linq">
- <HintPath>..\packages\System.Data.SQLite.1.0.79.0\lib\net40\System.Data.SQLite.Linq.dll</HintPath>
+ <Reference Include="System.Data.SQLite.Linq, Version=1.0.80.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\packages\System.Data.SQLite.1.0.80.0\lib\net40\System.Data.SQLite.Linq.dll</HintPath>
</Reference>
<Reference Include="System.ServiceModel" />
<Reference Include="System.Threading.Tasks.Dataflow">
<package id="NHibernate" version="3.1.0.4000" />
<package id="NHibernate.Castle" version="3.1.0.4000" />
<package id="ServiceStack.Text" version="2.27" />
- <package id="System.Data.SQLite" version="1.0.79.0" />
+ <package id="System.Data.SQLite" version="1.0.80.0" />
<package id="Windows7APICodePack" version="1.0.0.0" />
</packages>
\ No newline at end of file