public void SelectiveSyncFolders()
{
- var monitor = Shell.Monitors[CurrentAccount.AccountKey];
+ //var monitor = Shell.Monitors[CurrentAccount.AccountKey];
- var model = new SelectiveSynchViewModel(monitor,_events,CurrentAccount.Account);
+ var model = new SelectiveSynchViewModel(/*monitor,*/_events,CurrentAccount.Account,CurrentAccount.ApiKey);
if (_windowManager.ShowDialog(model) == true)
{
get
{
if (ObjectInfo != null)
- return ObjectInfo.Name;
+ return ObjectInfo.Name.Split('/').Last();
return _displayName;
}
set { _displayName = value; }
}
+ public bool IsExplicitlyChecked
+ {
+ set { _isChecked=value; }
+ }
+
public DirectoryRecord(ObjectInfo info)
{
ObjectInfo = info;
using Pithos.Client.WPF.Utils;
using Pithos.Core;
using Pithos.Interfaces;
+using Pithos.Network;
namespace Pithos.Client.WPF.SelectiveSynch
{
}
private ObservableCollection<ObjectInfo> _checks;
- private readonly PithosMonitor _monitor;
+ //private readonly PithosMonitor _monitor;
private bool _isBusy=true;
+ private string _apiKey;
public ObservableCollection<ObjectInfo> Checks
{
NotifyOfPropertyChange(() => Checks);
}
- public SelectiveSynchViewModel(PithosMonitor monitor, IEventAggregator events, AccountSettings account)
+ public SelectiveSynchViewModel(/*PithosMonitor monitor,*/ IEventAggregator events, AccountSettings account, string apiKey)
{
Account = account;
AccountName = account.AccountName;
DisplayName = String.Format("Selective folder synchronization for {0}",account.AccountName);
- _monitor = monitor;
+ //_monitor = monitor;
_events = events;
+ _apiKey = apiKey;
TaskEx.Run(LoadRootNode);
}
private void LoadRootNode()
- {
- var client = _monitor.CloudClient;
+ {
+ //TODO: Check this
+ var client = new CloudFilesClient(AccountName,_apiKey){AuthenticationUrl=Account.ServerUrl,UsePithos=true};
+ client.Authenticate();
+
- var dirs = from container in client.ListContainers(_monitor.UserName)
+ var dirs = from container in client.ListContainers(AccountName)
select new DirectoryRecord
{
DisplayName = container.Name,
Uri=new Uri(client.StorageUrl,String.Format(@"{0}/{1}",Account.AccountName, container.Name)),
- Directories = (from dir in client.ListObjects(_monitor.UserName, container.Name)
+ Directories = (from dir in client.ListObjects(AccountName, container.Name)
where dir.IsDirectory
select dir).ToTree()
};
from DirectoryRecord record in rootRecord
select record).ToList();
+ allNodes.Apply(record => record.IsChecked = false);
+
if (selections.Count == 0)
{
- allNodes.Apply(record => record.IsChecked = false);
+ // 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())
+ where record.Uri !=null && selections.Contains(record.Uri.ToString())
select record).ToList();
- var shouldBeChecked = allNodes.Except(selects).ToList();
+ //var shouldBeChecked = allNodes.Except(selects).ToList();
- selects.Apply(record=>record.IsChecked=false);
+ selects.Apply(record=>record.IsExplicitlyChecked=true);
- shouldBeChecked.Apply(record => record.IsChecked = true);
+ //shouldBeChecked.Apply(record => record.IsChecked = true);
#region Event Handlers
public void Handle(SelectiveSynchChanges message)
- {
- PithosMonitor monitor;
- if (Monitors.TryGetValue(message.Account.AccountKey, out monitor))
- {
- monitor.SetSelectivePaths(message.Uris,message.Added,message.Removed);
+ {
+ PithosMonitor monitor;
+ if (Monitors.TryGetValue(message.Account.AccountKey, out monitor))
+ {
+ monitor.SetSelectivePaths(message.Uris, message.Added, message.Removed);
- }
+ }
- var account = Accounts.First(acc => acc.AccountKey == message.Account.AccountKey);
- this._pollAgent.SetSelectivePaths(account, message.Added, message.Removed);
-
+ var account = Accounts.FirstOrDefault(acc => acc.AccountKey == message.Account.AccountKey);
+ if (account!=null)
+ {
+ this._pollAgent.SetSelectivePaths(account, message.Added, message.Removed);
+ }
- }
+
+ }
private bool _pollStarted;
foreach (var item in orderedItems)
{
var path = item.Uri.ToString();
- var newNode = new DirectoryRecord{ DisplayName=item.Name,ObjectInfo=item};
+ var newNode = new DirectoryRecord{ DisplayName=item.Name.Split('/').Last(),ObjectInfo=item};
lookups[path] = newNode;
var lastIndex = path.LastIndexOf("/", StringComparison.Ordinal);
var capitalizedFileInfo = fileInfo.WithProperCapitalization();
var fullLocalName = capitalizedFileInfo.FullName;
var othersPath = Path.Combine(accountInfo.AccountPath, FolderConstants.OthersFolder);
-
+
+ ObjectInfo objectInfo;
+
var isShared = fullLocalName.StartsWith(othersPath, StringComparison.InvariantCultureIgnoreCase);
if (isShared)
{
var otherParts = pathRelativeToOther.Split('\\');
var otherName = otherParts[0];
var otherContainer = otherParts[1];
- return new ObjectInfo
+ objectInfo=new ObjectInfo
{
Account = otherName,
Container = otherContainer,
Name = String.Join("/", otherParts.Splice(2))
};
}
- return new ObjectInfo(accountInfo.AccountPath, accountInfo.UserName, fileInfo);
+ else
+ objectInfo=new ObjectInfo(accountInfo.AccountPath, accountInfo.UserName, fileInfo);
+
+ objectInfo.Content_Type= (fileInfo is DirectoryInfo)
+ ? "appication/directory"
+ : "application/octet-stream";
+ return objectInfo;
}
}
public override string ToString()
{
- return String.Format("{0}: _ <- {1}", this.Action, this.CloudFile.Name);
+ return String.Format("{0}: _ <- {1}", Action, CloudFile.Name);
}
}
//the target is not below the root\r
//DON'T FORGET that Uri segments include the slashes. Must remove them to ensure proper checks\r
var mismatch = rootSegments\r
- .Where((t, i) => !String.Equals(targetSegments[i].TrimEnd('/'), t.TrimEnd('/')))\r
+ .Where((t, i) => !String.Equals(targetSegments[i].TrimEnd('/'), t.TrimEnd('/'),StringComparison.InvariantCultureIgnoreCase))\r
.Any();\r
return !mismatch;\r
}\r
DownloadWithBlocks(accountInfo, client, cloudFile, relativeUrl, localPath,
serverHash);
- if (cloudFile.AllowedTo == "read")
+ if (!cloudFile.IsWritable(accountInfo.UserName))
{
var attributes = File.GetAttributes(localPath);
File.SetAttributes(localPath, attributes | FileAttributes.ReadOnly);
command.Parameters.AddWithValue("path", path);
var affected = command.ExecuteNonQuery();
+ if (affected == 0)
+ {
+ var createdState = FileState.CreateFor(FileInfoExtensions.FromPath(path));
+ createdState.FileStatus = status;
+ _persistenceAgent.Post(createdState.Create);
+ }
return affected;
}
}
command.Parameters.AddWithValue("overlayStatus", overlayStatus);
var affected = command.ExecuteNonQuery();
+ if (affected == 0)
+ {
+ var createdState=FileState.CreateFor(FileInfoExtensions.FromPath(absolutePath));
+ createdState.FileStatus = fileStatus;
+ createdState.OverlayStatus = overlayStatus;
+ _persistenceAgent.Post(createdState.Create);
+ }
return affected;
}
}
return;
}
+ var latestState = action.FileState;
+
//Do not upload files in conflict
- if (action.FileState.FileStatus == FileStatus.Conflict)
+ if (latestState.FileStatus == FileStatus.Conflict)
{
Log.InfoFormat("Skipping file in conflict [{0}]", fileInfo.FullName);
return;
}
//Do not upload files when we have no permission
- if (action.FileState.FileStatus == FileStatus.Forbidden)
+ if (latestState.FileStatus == FileStatus.Forbidden)
{
Log.InfoFormat("Skipping forbidden file [{0}]", fileInfo.FullName);
return;
var cloudInfo = client.GetObjectInfo(account, cloudFile.Container, cloudFile.Name);
//If this is a read-only file, do not upload changes
- if (cloudInfo.AllowedTo == "read")
+ if (!cloudInfo.IsWritable(action.AccountInfo.UserName))
return;
if (fileInfo is DirectoryInfo)
if (response.StatusCode == HttpStatusCode.Forbidden)
{
StatusKeeper.SetFileState(fileInfo.FullName, FileStatus.Forbidden, FileOverlayStatus.Conflict, "Forbidden");
+
}
else
//In any other case, propagate the error
[System.ComponentModel.Composition.Import]
public IPithosSettings Settings { get; set; }
+ private List<string> _selectivePaths = new List<string>();
+ public List<string> SelectivePaths
+ {
+ get { return _selectivePaths; }
+ set { _selectivePaths = value; }
+ }
public WorkflowAgent()
{
var pendingStates = pendingEntries
.Select(state => new WorkflowState(account, state))
.ToList();
-
+
+
if (Log.IsDebugEnabled)
Log.DebugFormat("Found {0} interrupted files", pendingStates.Count);
return;*/
//TODO: Need to handle folder renames
+ //If there are selective sync paths defined
+ if (SelectivePaths.Count > 0
+ //And the target file is not below any of the selective paths
+ && !SelectivePaths.Any(workflowState.Path.IsAtOrDirectlyBelow))
+ //abort the post
+ {
+ Log.InfoFormat("File skipped, not under a selected folder [{0}] ",workflowState.Path);
+ return;
+ }
+
+
Debug.Assert(workflowState.Path.StartsWith(workflowState.AccountInfo.AccountPath, StringComparison.InvariantCultureIgnoreCase), "File from wrong account posted");
_agent.Post(workflowState);
}
_cancellationSource = new CancellationTokenSource();
-
- CloudClient = new CloudFilesClient(UserName, ApiKey)
- {UsePithos = true, AuthenticationUrl = AuthenticationUrl};
-
-
- _accountInfo = CloudClient.Authenticate();
+ lock (this)
+ {
+ CloudClient = new CloudFilesClient(UserName, ApiKey)
+ {UsePithos = true, AuthenticationUrl = AuthenticationUrl};
+ _accountInfo = CloudClient.Authenticate();
+ }
_accountInfo.SiteUri = AuthenticationUrl;
_accountInfo.AccountPath = RootPath;
var selectivePaths = UrisToFilePaths(uris);
FileAgent.SelectivePaths=selectivePaths;
+ WorkflowAgent.SelectivePaths = selectivePaths;
PollAgent.SetSyncUris(_accountInfo.AccountKey,uris);
var removedPaths = UrisToFilePaths(removed);
&& (this.Name == null || other.Name.StartsWith(this.Name));
}
+ public bool IsWritable(string account)
+ {
+ //If the Allowed To header has no value, try to determine the Share permissions
+ if (AllowedTo == null)
+ {
+ //If this file has no permissions defined, we can probably write it
+ //This case should occur only when the info comes from a listing of the user's own files
+ if (Permissions == null || Permissions.Count == 0)
+ return true;
+ string perms;
+
+ //Do we have an explicit write share to this account?
+ return Permissions.TryGetValue(account, out perms)
+ && perms.Equals("write",StringComparison.InvariantCultureIgnoreCase);
+
+ }
+ //Otherwise return the permissions specified by AllowedTo
+ return AllowedTo.Equals("write",StringComparison.InvariantCultureIgnoreCase) ;
+ }
public ObjectInfo Previous { get; private set; }
{
get
{
+ if (Content_Type == null)
+ return false;
if (Content_Type.StartsWith(@"application/directory",StringComparison.InvariantCultureIgnoreCase))
return true;
if (Content_Type.StartsWith(@"application/folder",StringComparison.InvariantCultureIgnoreCase))
: base()
{
if (other==null)
- Log.ErrorFormat("[ERROR] No parameters provided to the rest client. \n{0}\n", other);
- //throw new ArgumentNullException("other");
+ //Log.ErrorFormat("[ERROR] No parameters provided to the rest client. \n{0}\n", other);
+ throw new ArgumentNullException("other");
Contract.EndContractBlock();
//The maximum error response must be large because missing server hashes are return as a Conflivt (409) error response