public override void Solve( IFileSystemInfo localFileSystemInfo, IObjectId remoteId, ContentChangeType localContent, ContentChangeType remoteContent) { var obj = this.Storage.GetObjectByRemoteId(remoteId.Id); string oldName = (remoteId as ICmisObject).Name; // Rename object try { (remoteId as ICmisObject).Rename(localFileSystemInfo.Name, true); } catch (CmisConstraintException e) { if (!Utils.IsValidISO885915(localFileSystemInfo.Name)) { OperationsLogger.Warn(string.Format("Server denied to rename {0} to {1}, perhaps because it contains UTF-8 characters", oldName, localFileSystemInfo.Name)); throw new InteractionNeededException(string.Format("Server denied renaming of {0}", oldName), e) { Title = string.Format("Server denied renaming of {0}", oldName), Description = string.Format("Server denied to rename {0} to {1}, perhaps because it contains UTF-8 characters", oldName, localFileSystemInfo.Name) }; } throw; } catch (CmisPermissionDeniedException) { OperationsLogger.Warn(string.Format("Unable to renamed remote object from {0} to {1}: Permission Denied", oldName, localFileSystemInfo.Name)); return; } obj.Name = localFileSystemInfo.Name; obj.Ignored = (remoteId as ICmisObject).AreAllChildrenIgnored(); this.Storage.SaveMappedObject(obj); this.changeChangeSolver.Solve(localFileSystemInfo, remoteId, localContent, remoteContent); }
public override void Solve( IFileSystemInfo localFileSystemInfo, IObjectId remoteId, ContentChangeType localContent, ContentChangeType remoteContent) { var savedObject = this.Storage.GetObjectByRemoteId(remoteId.Id); string newPath = remoteId is IFolder ? this.Storage.Matcher.CreateLocalPath(remoteId as IFolder) : this.Storage.Matcher.CreateLocalPath(remoteId as IDocument); if (remoteId is IFolder) { IDirectoryInfo dirInfo = localFileSystemInfo as IDirectoryInfo; string oldPath = dirInfo.FullName; if (!dirInfo.FullName.Equals(newPath)) { dirInfo.MoveTo(newPath); OperationsLogger.Info(string.Format("Moved local folder {0} to {1}", oldPath, newPath)); } else { return; } } else if (remoteId is IDocument) { IFileInfo fileInfo = localFileSystemInfo as IFileInfo; string oldPath = fileInfo.FullName; fileInfo.MoveTo(newPath); OperationsLogger.Info(string.Format("Moved local file {0} to {1}", oldPath, newPath)); } savedObject.Name = (remoteId as ICmisObject).Name; savedObject.Ignored = (remoteId as ICmisObject).AreAllChildrenIgnored(); savedObject.ParentId = remoteId is IFolder ? (remoteId as IFolder).ParentId : (remoteId as IDocument).Parents[0].Id; this.Storage.SaveMappedObject(savedObject); this.changeChangeSolver.Solve(localFileSystemInfo, remoteId, localContent, remoteContent); }
public override void Solve( IFileSystemInfo localFileSystemInfo, IObjectId remoteId, ContentChangeType localContent, ContentChangeType remoteContent) { var obj = this.Storage.GetObjectByRemoteId(remoteId.Id); var localParent = localFileSystemInfo is IFileInfo ? (localFileSystemInfo as IFileInfo).Directory : (localFileSystemInfo as IDirectoryInfo).Parent; var mappedLocalParent = this.Storage.GetObjectByGuid((Guid)localParent.Uuid); var remoteObject = remoteId as IFileableCmisObject; var targetId = mappedLocalParent.RemoteObjectId; var src = this.Session.GetObject(obj.ParentId); var target = this.Session.GetObject(targetId); try { OperationsLogger.Info(string.Format("Moving remote object {2} from folder {0} to folder {1}", src.Name, target.Name, remoteId.Id)); remoteObject = remoteObject.Move(src, target); } catch (CmisPermissionDeniedException) { OperationsLogger.Info(string.Format("Moving remote object failed {0}: Permission Denied", localFileSystemInfo.FullName)); return; } obj.ParentId = targetId; obj.Ignored = remoteObject.AreAllChildrenIgnored(); this.Storage.SaveMappedObject(obj); if (obj.Name != localFileSystemInfo.Name) { this.renameChangeSolver.Solve(localFileSystemInfo, remoteObject, localContent, remoteContent); } else { this.changeChangeSolver.Solve(localFileSystemInfo, remoteObject, localContent, remoteContent); } }
/// <summary> /// Initializes a new instance of the <see cref="ContentValueChangedActionItem"/> class. /// </summary> /// <param name="name">The name of this action item.</param> /// <param name="content">The <see cref="IContent"/> instance that has changed.</param> /// <param name="changeType">The type of change that occurred.</param> /// <param name="index">The index of the change if the change occurred on an item of a collection. <c>null</c> otherwise.</param> /// <param name="previousValue">The previous value of the content (or the item if the change occurred on an item of a collection).</param> /// <param name="dirtiables">The dirtiable objects associated to this action item.</param> public ContentValueChangedActionItem(string name, IContent content, ContentChangeType changeType, object index, object previousValue, IEnumerable<IDirtiable> dirtiables) : base(name, dirtiables) { Content = content; ChangeType = changeType; PreviousValue = previousValue; Index = index; }
public void AssertGotSingleFileEvent(MetaDataChangeType metaType, ContentChangeType contentType) { Assert.That(this.List.Count, Is.EqualTo(1)); Assert.That(this.List[0], Is.TypeOf(typeof(FileEvent))); var fileEvent = this.List[0] as FileEvent; Assert.That(fileEvent.Remote, Is.EqualTo(metaType), "MetaDataChangeType incorrect"); Assert.That(fileEvent.RemoteContent, Is.EqualTo(contentType), "ContentChangeType incorrect"); }
/// <summary> /// Does nothing /// </summary> /// <param name='localFile'> /// Local file. /// </param> /// <param name='remoteId'> /// Remote identifier. /// </param> /// <param name="localContent">Hint if the local content has been changed.</param> /// <param name="remoteContent">Information if the remote content has been changed.</param> public virtual void Solve( IFileSystemInfo localFile, IObjectId remoteId, ContentChangeType localContent, ContentChangeType remoteContent) { // No Operation Needed }
/// <summary> /// Initializes a new instance of the <see cref="ContentChangeEventArgs"/> class. /// </summary> /// <param name="content">The content that has changed.</param> /// <param name="index">The index in the content where the change occurred, if applicable. <c>null</c> otherwise.</param> /// <param name="changeType">The type of change that occurred.</param> /// <param name="oldValue">The old value of the content.</param> /// <param name="newValue">The new value of the content.</param> public ContentChangeEventArgs(IContent content, object index, ContentChangeType changeType, object oldValue, object newValue) { Content = content; Index = index; ChangeType = changeType; OldValue = oldValue; NewValue = newValue; }
public override void Solve( IFileSystemInfo localFileSystemInfo, IObjectId remoteId, ContentChangeType localContent = ContentChangeType.NONE, ContentChangeType remoteContent = ContentChangeType.NONE) { if (localFileSystemInfo is IFileInfo && remoteId is IDocument) { var localFile = localFileSystemInfo as IFileInfo; var remoteDocument = remoteId as IDocument; var mappedObject = this.Storage.GetObject(localFile); if (mappedObject == null) { throw new ArgumentException(string.Format("Could not find db entry for {0} => invoke crawl sync", localFileSystemInfo.FullName)); } if (mappedObject.LastChangeToken != (remoteId as ICmisObjectProperties).ChangeToken) { throw new ArgumentException(string.Format("remote {1} {0} has also been changed since last sync => invoke crawl sync", remoteId.Id, remoteId is IDocument ? "document" : "folder")); } if (localFile != null && localFile.IsContentChangedTo(mappedObject, scanOnlyIfModificationDateDiffers: true)) { Logger.Debug(string.Format("\"{0}\" is different from {1}", localFile.FullName, mappedObject.ToString())); OperationsLogger.Debug(string.Format("Local file \"{0}\" has been changed", localFile.FullName)); try { var transmission = this.transmissionManager.CreateTransmission(TransmissionType.UPLOAD_MODIFIED_FILE, localFile.FullName); mappedObject.LastChecksum = this.UploadFileWithPWC(localFile, ref remoteDocument, transmission); mappedObject.ChecksumAlgorithmName = "SHA-1"; if (remoteDocument.Id != mappedObject.RemoteObjectId) { this.TransmissionStorage.RemoveObjectByRemoteObjectId(mappedObject.RemoteObjectId); mappedObject.RemoteObjectId = remoteDocument.Id; } } catch (Exception ex) { if (ex.InnerException is CmisPermissionDeniedException) { OperationsLogger.Warn(string.Format("Local changed file \"{0}\" has not been uploaded: PermissionDenied", localFile.FullName)); return; } else if (ex.InnerException is CmisStorageException) { OperationsLogger.Warn(string.Format("Local changed file \"{0}\" has not been uploaded: StorageException", localFile.FullName), ex); return; } throw; } mappedObject.LastRemoteWriteTimeUtc = remoteDocument.LastModificationDate; mappedObject.LastLocalWriteTimeUtc = localFile.LastWriteTimeUtc; mappedObject.LastContentSize = localFile.Length; OperationsLogger.Info(string.Format("Local changed file \"{0}\" has been uploaded", localFile.FullName)); } mappedObject.LastChangeToken = remoteDocument.ChangeToken; mappedObject.LastLocalWriteTimeUtc = localFile.LastWriteTimeUtc; this.Storage.SaveMappedObject(mappedObject); } else { this.folderOrFileContentUnchangedSolver.Solve(localFileSystemInfo, remoteId, localContent, remoteContent); } return; }
/// <summary> /// Solve the specified situation by using localFile and remote object. /// </summary> /// <param name="localFile">Local file.</param> /// <param name="remoteId">Remote identifier or object.</param> /// <param name="localContent">Hint if the local content has been changed.</param> /// <param name="remoteContent">Information if the remote content has been changed.</param> public override void Solve( IFileSystemInfo localFile, IObjectId remoteId, ContentChangeType localContent = ContentChangeType.NONE, ContentChangeType remoteContent = ContentChangeType.NONE) { var mappedObject = this.Storage.GetObjectByLocalPath(localFile); this.Storage.RemoveObject(mappedObject); }
public override void Solve( IFileSystemInfo localFileSystemInfo, IObjectId remoteId, ContentChangeType localContent, ContentChangeType remoteContent) { var savedObject = this.Storage.GetObjectByRemoteId(remoteId.Id); Guid? newParentUuid = localFileSystemInfo is IFileInfo ? (localFileSystemInfo as IFileInfo).Directory.Uuid : (localFileSystemInfo as IDirectoryInfo).Parent.Uuid; string newParentId = this.Storage.GetObjectByGuid((Guid)newParentUuid).RemoteObjectId; savedObject.Ignored = (remoteId as ICmisObject).AreAllChildrenIgnored(); if (localFileSystemInfo.Name == (remoteId as ICmisObject).Name) { // Both names are equal => only move to new remote parent try { (remoteId as IFileableCmisObject).Move(this.Session.GetObject(savedObject.ParentId), this.Session.GetObject(newParentId)); } catch (CmisPermissionDeniedException) { OperationsLogger.Info(string.Format("Permission Denied: Cannot move remote object {0} from {1} to {2}", remoteId.Id, savedObject.ParentId, newParentId)); return; } savedObject.Name = localFileSystemInfo.Name; savedObject.ParentId = newParentId; this.Storage.SaveMappedObject(savedObject); this.changeChangeSolver.Solve(localFileSystemInfo, remoteId, localContent, remoteContent); } else { // Names are different to each other if (localFileSystemInfo.Name == savedObject.Name) { // Remote rename and local move => Move remote and rename locally => change change solver try { remoteId = (remoteId as IFileableCmisObject).Move(this.Session.GetObject(savedObject.ParentId), this.Session.GetObject(newParentId)); } catch (CmisPermissionDeniedException) { OperationsLogger.Info(string.Format("Permission Denied: Cannot move remote object {0} from {1} to {2}", remoteId.Id, savedObject.ParentId, newParentId)); return; } var localParentPath = localFileSystemInfo is IFileInfo ? (localFileSystemInfo as IFileInfo).Directory.FullName : (localFileSystemInfo as IDirectoryInfo).Parent.FullName; string newPath = Path.Combine(localParentPath, (remoteId as ICmisObject).Name); this.MoveTo(localFileSystemInfo, localFileSystemInfo.FullName, newPath); savedObject.Name = localFileSystemInfo.Name; savedObject.ParentId = newParentId; this.Storage.SaveMappedObject(savedObject); this.changeChangeSolver.Solve(localFileSystemInfo, remoteId, localContent, remoteContent); } else { // Both sides have been renamed => Move remote => rename rename solver try { (remoteId as IFileableCmisObject).Move(this.Session.GetObject(savedObject.ParentId), this.Session.GetObject(newParentId)); } catch (CmisPermissionDeniedException) { OperationsLogger.Info(string.Format("Permission Denied: Cannot move remote object {0} from {1} to {2}", remoteId.Id, savedObject.ParentId, newParentId)); return; } savedObject.ParentId = newParentId; this.Storage.SaveMappedObject(savedObject); this.renameRenameSolver.Solve(localFileSystemInfo, remoteId, localContent, remoteContent); } } }
/// <summary> /// Renames the specified localFile to the name of the given remoteId object by using the storage, localFile and remoteId. /// </summary> /// <param name="localFile">Local file or folder. It is the source file/folder reference, which should be renamed.</param> /// <param name="remoteId">Remote identifier. Should be an instance of IFolder or IDocument.</param> /// <param name="localContent">Hint if the local content has been changed.</param> /// <param name="remoteContent">Information if the remote content has been changed.</param> public override void Solve( IFileSystemInfo localFile, IObjectId remoteId, ContentChangeType localContent = ContentChangeType.NONE, ContentChangeType remoteContent = ContentChangeType.NONE) { IMappedObject obj = this.Storage.GetObjectByRemoteId(remoteId.Id); if (remoteId is IFolder) { // Rename local folder IFolder remoteFolder = remoteId as IFolder; IDirectoryInfo dirInfo = localFile as IDirectoryInfo; string oldPath = dirInfo.FullName; try { dirInfo.MoveTo(Path.Combine(dirInfo.Parent.FullName, remoteFolder.Name)); obj.Name = remoteFolder.Name; } catch (IOException) { if (dirInfo.Name.Equals(remoteFolder.Name, StringComparison.OrdinalIgnoreCase)) { obj.Name = dirInfo.Name; } else { throw; } } if (remoteFolder.LastModificationDate != null) { dirInfo.LastWriteTimeUtc = (DateTime)remoteFolder.LastModificationDate; } obj.LastChangeToken = remoteFolder.ChangeToken; obj.LastRemoteWriteTimeUtc = remoteFolder.LastModificationDate; obj.LastLocalWriteTimeUtc = dirInfo.LastWriteTimeUtc; obj.Ignored = remoteFolder.AreAllChildrenIgnored(); this.Storage.SaveMappedObject(obj); OperationsLogger.Info(string.Format("Renamed local folder {0} to {1}", oldPath, remoteFolder.Name)); } else if(remoteId is IDocument) { // Rename local file IDocument remoteDocument = remoteId as IDocument; IFileInfo fileInfo = localFile as IFileInfo; string oldPath = fileInfo.FullName; fileInfo.MoveTo(Path.Combine(fileInfo.Directory.FullName, remoteDocument.Name)); if (remoteDocument.LastModificationDate != null) { fileInfo.LastWriteTimeUtc = (DateTime)remoteDocument.LastModificationDate; } obj.Name = remoteDocument.Name; obj.LastChangeToken = remoteContent == ContentChangeType.NONE ? remoteDocument.ChangeToken : obj.LastChangeToken; obj.LastRemoteWriteTimeUtc = remoteContent == ContentChangeType.NONE ? remoteDocument.LastModificationDate : obj.LastRemoteWriteTimeUtc; obj.LastLocalWriteTimeUtc = fileInfo.LastWriteTimeUtc; this.Storage.SaveMappedObject(obj); OperationsLogger.Info(string.Format("Renamed local file {0} to {1}", oldPath, remoteDocument.Name)); if (remoteContent != ContentChangeType.NONE) { throw new ArgumentException("Remote documents content is also changed => force crawl sync."); } } else { throw new ArgumentException("Given remote Id is not an IFolder nor an IDocument instance"); } }
/// <summary> /// Solve the specified situation by using the storage and remote object id to remove existing db entries and forces a crawl sync by throwing an IOException. /// </summary> /// <param name="localFile">Deleted Local filesystem info instance.</param> /// <param name="remoteId">Remote identifier or object.</param> /// <param name="localContent">Hint if the local content has been changed. Is not used by this solver.</param> /// <param name="remoteContent">Information if the remote content has been changed. Is not used by this solver.</param> public override void Solve( IFileSystemInfo localFile, IObjectId remoteId, ContentChangeType localContent = ContentChangeType.NONE, ContentChangeType remoteContent = ContentChangeType.NONE) { var mappedObject = this.Storage.GetObjectByRemoteId(remoteId.Id); this.Storage.RemoveObject(mappedObject); throw new IOException( string.Format( "Local deleted {0} is renamed or moved remotely => invoking crawl sync to download them again", mappedObject.Type == MappedObjectType.File ? "file" : "directory")); }
/// <summary> /// Solve the specified situation by using localFile and remote object. /// </summary> /// <param name="localFileSystemInfo">Local filesystem info instance.</param> /// <param name="remoteId">Remote identifier or object.</param> /// <param name="localContent">Hint if the local content has been changed.</param> /// <param name="remoteContent">Information if the remote content has been changed.</param> public override void Solve( IFileSystemInfo localFileSystemInfo, IObjectId remoteId, ContentChangeType localContent, ContentChangeType remoteContent) { if (localFileSystemInfo is IDirectoryInfo) { IDirectoryInfo localFolder = localFileSystemInfo as IDirectoryInfo; IDirectoryInfo localParent = localFolder.Parent; IFolder remoteFolder = remoteId as IFolder; string remoteParentId = remoteFolder.ParentId; IMappedObject mappedParent = this.Storage.GetObjectByRemoteId(remoteParentId); IMappedObject mappedObject = this.Storage.GetObjectByRemoteId(remoteFolder.Id); if (localParent.Uuid == mappedParent.Guid) { // Both folders are in the same parent folder this.SyncNamesAndDates(localFolder, remoteFolder, mappedObject); } else { OperationsLogger.Warn( string.Format( "Synchronization Conflict: The local directory {0} has been moved to {1} with id {2},{4}" + "but the remote folder was moved to {3}{4}You can fix this situation by moving them into the same folder", localFileSystemInfo.Name, localFileSystemInfo.FullName, remoteFolder.Path, Environment.NewLine)); return; } } else if (localFileSystemInfo is IFileInfo) { IFileInfo localFile = localFileSystemInfo as IFileInfo; IDirectoryInfo localParent = localFile.Directory; IDocument remoteFile = remoteId as IDocument; string remoteParentId = remoteFile.Parents[0].Id; IMappedObject mappedParent = this.Storage.GetObjectByRemoteId(remoteParentId); IMappedObject mappedObject = this.Storage.GetObjectByRemoteId(remoteFile.Id); if (localParent.Uuid == mappedParent.Guid) { // Both files are in the same parent folder this.SyncNamesAndDates(localFile, remoteFile, mappedObject); return; } else { OperationsLogger.Warn( string.Format( "Synchronization Conflict: The local file {0} has been moved to {1} with id {2},{4}" + "but the remote file was moved to {3}{4}You can fix this situation by moving them into the same folder", localFileSystemInfo.Name, localFileSystemInfo.FullName, remoteFile.Paths[0], Environment.NewLine)); return; } } }
public override void Solve( IFileSystemInfo localFileSystemInfo, IObjectId remoteId, ContentChangeType localContent, ContentChangeType remoteContent) { // User Interaction needed or the content will be downloaded on next sync. // Possibilities: // - Download new remote content (default, because no user interaction is needed and it is simple to solve) // - Remove remote element // - Ignore until situation is cleared OperationsLogger.Warn(string.Format("The remote object {0} of the corresponding locally deleted element has been changed => Downloading the remote changes", remoteId.Id)); this.Storage.RemoveObject(this.Storage.GetObjectByRemoteId(remoteId.Id)); throw new ArgumentException("Remote object has been changed while the object was deleted locally => force crawl sync"); }
/// <summary> /// Solve the specified situation by using the session, storage, localFile and remoteId. /// If a folder is affected, simply update the local change time of the corresponding local folder. /// If it is a file and the changeToken is not equal to the saved, the new content is downloaded. /// </summary> /// <param name="localFile">Local file.</param> /// <param name="remoteId">Remote identifier.</param> /// <param name="localContent">Hint if the local content has been changed.</param> /// <param name="remoteContent">Information if the remote content has been changed.</param> public override void Solve( IFileSystemInfo localFile, IObjectId remoteId, ContentChangeType localContent = ContentChangeType.NONE, ContentChangeType remoteContent = ContentChangeType.NONE) { IMappedObject obj = this.Storage.GetObjectByRemoteId(remoteId.Id); if (remoteId is IFolder) { var remoteFolder = remoteId as IFolder; DateTime? lastModified = remoteFolder.LastModificationDate; obj.LastChangeToken = remoteFolder.ChangeToken; if (lastModified != null) { try { localFile.LastWriteTimeUtc = (DateTime)lastModified; } catch(IOException e) { Logger.Debug("Couldn't set the server side modification date", e); } obj.Ignored = remoteFolder.AreAllChildrenIgnored(); obj.LastLocalWriteTimeUtc = localFile.LastWriteTimeUtc; } } else if (remoteId is IDocument) { var remoteDocument = remoteId as IDocument; DateTime? lastModified = remoteDocument.LastModificationDate; if ((lastModified != null && lastModified != obj.LastRemoteWriteTimeUtc) || obj.LastChangeToken != remoteDocument.ChangeToken) { if (remoteContent != ContentChangeType.NONE) { if (obj.LastLocalWriteTimeUtc != localFile.LastWriteTimeUtc) { throw new ArgumentException("The local file has been changed since last write => aborting update"); } obj.LastChecksum = DownloadChanges(localFile as IFileInfo, remoteDocument, obj, this.fsFactory, this.transmissonManager, Logger); } obj.LastRemoteWriteTimeUtc = remoteDocument.LastModificationDate; if (remoteDocument.LastModificationDate != null) { localFile.LastWriteTimeUtc = (DateTime)remoteDocument.LastModificationDate; } obj.LastLocalWriteTimeUtc = localFile.LastWriteTimeUtc; obj.LastContentSize = remoteDocument.ContentStreamLength ?? 0; } obj.LastChangeToken = remoteDocument.ChangeToken; obj.LastRemoteWriteTimeUtc = lastModified; } this.Storage.SaveMappedObject(obj); }
public override void Solve( IFileSystemInfo localFileSystemInfo, IObjectId remoteId, ContentChangeType localContent, ContentChangeType remoteContent) { // Rename local object and call change/change solver var savedObject = this.Storage.GetObjectByRemoteId(remoteId.Id); string oldPath = localFileSystemInfo.FullName; string parentPath = localFileSystemInfo is IFileInfo ? (localFileSystemInfo as IFileInfo).Directory.FullName : (localFileSystemInfo as IDirectoryInfo).Parent.FullName; string newPath = Path.Combine(parentPath, (remoteId as ICmisObject).Name); this.MoveTo(localFileSystemInfo, oldPath, newPath); savedObject.Name = (remoteId as ICmisObject).Name; savedObject.Ignored = (remoteId as ICmisObject).AreAllChildrenIgnored(); this.Storage.SaveMappedObject(savedObject); this.changeChangeSolver.Solve(localFileSystemInfo, remoteId, localContent, remoteContent); }
public override void Solve( IFileSystemInfo localFileSystemInfo, IObjectId remoteId, ContentChangeType localContent, ContentChangeType remoteContent) { var mappedObject = this.Storage.GetObjectByGuid((Guid)localFileSystemInfo.Uuid); this.Storage.RemoveObject(mappedObject); if (localFileSystemInfo is IFileInfo) { this.secondSolver.Solve(localFileSystemInfo, null, ContentChangeType.CREATED, ContentChangeType.NONE); } else if (localFileSystemInfo is IDirectoryInfo) { this.secondSolver.Solve(localFileSystemInfo, null, ContentChangeType.NONE, ContentChangeType.NONE); var dir = localFileSystemInfo as IDirectoryInfo; if (dir.GetFiles().Length > 0 || dir.GetDirectories().Length > 0) { throw new IOException(string.Format("There are unsynced files in local folder {0} => starting crawl sync", dir.FullName)); } } }
/// <summary> /// Deletes the given localFileInfo on file system and removes the stored object from storage. /// </summary> /// <param name="localFileInfo">Local file info.</param> /// <param name="remoteId">Remote identifier.</param> /// <param name="localContent">Hint if the local content has been changed.</param> /// <param name="remoteContent">Information if the remote content has been changed.</param> public override void Solve( IFileSystemInfo localFileInfo, IObjectId remoteId, ContentChangeType localContent = ContentChangeType.NONE, ContentChangeType remoteContent = ContentChangeType.NONE) { if (localFileInfo is IDirectoryInfo) { if (!this.DeleteLocalObjectIfHasBeenSyncedBefore(localFileInfo)) { this.Storage.RemoveObject(this.Storage.GetObjectByLocalPath(localFileInfo)); throw new IOException(string.Format("Not all local objects under {0} have been synced yet", localFileInfo.FullName)); } else { this.Storage.RemoveObject(this.Storage.GetObjectByLocalPath(localFileInfo)); } } else if (localFileInfo is IFileInfo) { var file = localFileInfo as IFileInfo; file.Refresh(); var mappedFile = this.Storage.GetObjectByLocalPath(file); if (file.Exists) { if (mappedFile != null && file.LastWriteTimeUtc.Equals(mappedFile.LastLocalWriteTimeUtc)) { file.Delete(); OperationsLogger.Info(string.Format("Deleted local file {0} because the mapped remote object {0} has been deleted", file.FullName, mappedFile.RemoteObjectId)); } else { file.Uuid = null; if (mappedFile == null) { return; } OperationsLogger.Info(string.Format("Deletion of local file {0} skipped because of not yet uploaded changes", file.FullName)); } } else { if (mappedFile == null) { return; } else { OperationsLogger.Info(string.Format("Deletion of local file {0} skipped because it has already been deleted", file.FullName)); } } this.Storage.RemoveObject(this.Storage.GetObjectByLocalPath(localFileInfo)); file.Refresh(); if (file.Exists) { throw new IOException(string.Format("Deletion of local file {0} skipped because of not yet uploaded changes", localFileInfo.FullName)); } } }
/// <summary> /// Solves the situation by deleting the corresponding remote object. /// </summary> /// <param name="localFile">Local file.</param> /// <param name="remoteId">Remote identifier or object.</param> /// <param name="localContent">Hint if the local content has been changed.</param> /// <param name="remoteContent">Information if the remote content has been changed.</param> public override void Solve( IFileSystemInfo localFile, IObjectId remoteId, ContentChangeType localContent = ContentChangeType.NONE, ContentChangeType remoteContent = ContentChangeType.NONE) { var mappedObject = this.Storage.GetObjectByRemoteId(remoteId.Id); if (mappedObject.LastChangeToken != (remoteId as ICmisObject).ChangeToken) { throw new ArgumentException("Remote object has been changed since last sync => force crawl sync"); } bool hasBeenDeleted = this.TryDeleteObjectOnServer(remoteId, mappedObject.Type); if (hasBeenDeleted) { this.Storage.RemoveObject(mappedObject); OperationsLogger.Info(string.Format("Deleted the corresponding remote object {0} of locally deleted object {1}", remoteId.Id, mappedObject.Name)); } else { OperationsLogger.Warn(string.Format("Permission denied while trying to Delete the locally deleted object {0} on the server.", mappedObject.Name)); } }
/// <summary> /// Solve the specified situation by using the session, storage, localFile and remoteId. /// Moves the local file/folder to the new location. /// </summary> /// <param name="localFile">Old local file/folder.</param> /// <param name="remoteId">Remote identifier.</param> /// <param name="localContent">Hint if the local content has been changed.</param> /// <param name="remoteContent">Information if the remote content has been changed.</param> public override void Solve( IFileSystemInfo localFile, IObjectId remoteId, ContentChangeType localContent = ContentChangeType.NONE, ContentChangeType remoteContent = ContentChangeType.NONE) { // Move local object var savedObject = this.Storage.GetObjectByRemoteId(remoteId.Id); string newPath = remoteId is IFolder ? this.Storage.Matcher.CreateLocalPath(remoteId as IFolder) : this.Storage.Matcher.CreateLocalPath(remoteId as IDocument); if (remoteId is IFolder) { IDirectoryInfo dirInfo = localFile as IDirectoryInfo; string oldPath = dirInfo.FullName; if (!dirInfo.FullName.Equals(newPath)) { dirInfo.MoveTo(newPath); dirInfo.LastWriteTimeUtc = (remoteId as IFolder).LastModificationDate != null ? (DateTime)(remoteId as IFolder).LastModificationDate : dirInfo.LastWriteTimeUtc; OperationsLogger.Info(string.Format("Moved local folder {0} to {1}", oldPath, newPath)); } else { return; } } else if (remoteId is IDocument) { IFileInfo fileInfo = localFile as IFileInfo; string oldPath = fileInfo.FullName; fileInfo.MoveTo(newPath); fileInfo.LastWriteTimeUtc = (remoteId as IDocument).LastModificationDate != null ? (DateTime)(remoteId as IDocument).LastModificationDate : fileInfo.LastWriteTimeUtc; OperationsLogger.Info(string.Format("Moved local file {0} to {1}", oldPath, newPath)); } savedObject.Name = (remoteId as ICmisObject).Name; savedObject.ParentId = remoteId is IFolder ? (remoteId as IFolder).ParentId : (remoteId as IDocument).Parents[0].Id; savedObject.LastChangeToken = (remoteId is IDocument && remoteContent != ContentChangeType.NONE) ? savedObject.LastChangeToken : remoteId is ICmisObject ? (remoteId as ICmisObject).ChangeToken : null; savedObject.LastLocalWriteTimeUtc = localFile.LastWriteTimeUtc; savedObject.LastRemoteWriteTimeUtc = (remoteId is IDocument && remoteContent != ContentChangeType.NONE) ? savedObject.LastRemoteWriteTimeUtc : (remoteId as ICmisObject).LastModificationDate; savedObject.Ignored = (remoteId as ICmisObject).AreAllChildrenIgnored(); this.Storage.SaveMappedObject(savedObject); if (remoteId is IDocument && remoteContent != ContentChangeType.NONE) { throw new ArgumentException("Remote content has also been changed => force crawl sync."); } }
/// <inheritdoc/> protected override void UndoAction() { switch (ChangeType) { case ContentChangeType.ValueChange: var previousValue = Content.Retrieve(Index); Content.Update(PreviousValue, Index); PreviousValue = previousValue; break; case ContentChangeType.CollectionAdd: PreviousValue = Content.Retrieve(Index); Content.Remove(Index); ChangeType = ContentChangeType.CollectionRemove; break; case ContentChangeType.CollectionRemove: Content.Add(Index, PreviousValue); PreviousValue = null; ChangeType = ContentChangeType.CollectionAdd; break; default: throw new ArgumentOutOfRangeException(); } }
protected override void ApplyUndo(object oldValue, object newValue, ContentChangeType type, bool isUndo) { var memberNode = Node as IAssetMemberNode; var objectNode = Node as IAssetObjectNode; switch (type) { case ContentChangeType.ValueChange: if (memberNode == null) { throw new InvalidOperationException($"Expecting an {nameof(IAssetMemberNode)} when the change type is {nameof(ContentChangeType.ValueChange)}"); } memberNode.Update(oldValue); break; case ContentChangeType.CollectionUpdate: if (objectNode == null) { throw new InvalidOperationException($"Expecting an {nameof(IAssetObjectNode)} when the change type is {nameof(ContentChangeType.CollectionUpdate)}"); } objectNode.Update(oldValue, Index); break; case ContentChangeType.CollectionAdd: if (objectNode == null) { throw new InvalidOperationException($"Expecting an {nameof(IAssetObjectNode)} when the change type is {nameof(ContentChangeType.CollectionAdd)}"); } // Some Add might have an empty index. In this case we need to retrieve the index from the content to pass it to remove. // TODO: this way of fetching the index is not robust at all! what if there's the same item twice in the collection? var index = !Index.IsEmpty ? Index : objectNode.Indices.First(x => Equals(Node.Retrieve(x), newValue)); // When undoing (an add), we don't want to track the item as deleted. But when redoing (a remove) we do! if (isUndo) { objectNode.RemoveAndDiscard(newValue, index, itemId); } else { objectNode.Remove(newValue, index); } break; case ContentChangeType.CollectionRemove: if (objectNode == null) { throw new InvalidOperationException($"Expecting an {nameof(IAssetObjectNode)} when the change type is {nameof(ContentChangeType.CollectionRemove)}"); } if (!Index.IsEmpty) { objectNode.Restore(oldValue, Index, itemId); } else { objectNode.Restore(oldValue, itemId); } break; default: throw new ArgumentOutOfRangeException(); } }
private static void TestContentChange(GraphNodeChangeListener listener, IGraphNode contentOwner, ContentChangeType type, Index index, object oldValue, object newValue, Action change) { var i = 0; var prepareChange = new EventHandler<GraphContentChangeEventArgs>((sender, e) => { Assert.AreEqual(0, i); VerifyListenerEvent(e, contentOwner, type, index, oldValue, newValue, false); ++i; }); var changing = new EventHandler<GraphContentChangeEventArgs>((sender, e) => { Assert.AreEqual(1, i); VerifyListenerEvent(e, contentOwner, type, index, oldValue, newValue, false); ++i; }); var changed = new EventHandler<GraphContentChangeEventArgs>((sender, e) => { Assert.AreEqual(2, i); VerifyListenerEvent(e, contentOwner, type, index, oldValue, newValue, true); ++i; }); var finalizeChange = new EventHandler<GraphContentChangeEventArgs>((sender, e) => { Assert.AreEqual(3, i); VerifyListenerEvent(e, contentOwner, type, index, oldValue, newValue, true); ++i; }); listener.PrepareChange += prepareChange; listener.Changing += changing; listener.Changed += changed; listener.FinalizeChange += finalizeChange; change(); Assert.AreEqual(4, i); listener.PrepareChange -= prepareChange; listener.Changing -= changing; listener.Changed -= changed; listener.FinalizeChange -= finalizeChange; }
/// <summary> /// Solve the specified situation by using localFile and remote object. /// </summary> /// <param name="localFile">Local file.</param> /// <param name="remoteId">Remote identifier or object.</param> /// <param name="localContent">Hint if the local content has been changed.</param> /// <param name="remoteContent">Information if the remote content has been changed.</param> public override void Solve( IFileSystemInfo localFile, IObjectId remoteId, ContentChangeType localContent = ContentChangeType.NONE, ContentChangeType remoteContent = ContentChangeType.NONE) { var obj = this.Storage.GetObjectByRemoteId(remoteId.Id); // Rename remote object if (remoteId is ICmisObject) { if ((remoteId as ICmisObject).ChangeToken != obj.LastChangeToken) { throw new ArgumentException("Last changetoken is invalid => force crawl sync"); } string oldName = (remoteId as ICmisObject).Name; try { (remoteId as ICmisObject).Rename(localFile.Name, true); } catch (CmisNameConstraintViolationException e) { if (!Utils.IsValidISO885915(localFile.Name)) { OperationsLogger.Warn(string.Format("The server denies the renaming of {2} from {0} to {1}, perhaps because the new name contains UTF-8 characters", oldName, localFile.Name, localFile.FullName)); throw new InteractionNeededException(string.Format("Server denied renaming of {0}", oldName), e) { Title = string.Format("Server denied renaming of {0}", oldName), Description = string.Format("The server denies the renaming of {2} from {0} to {1}, perhaps because the new name contains UTF-8 characters", oldName, localFile.Name, localFile.FullName) }; } else { try { string wishedRemotePath = this.Storage.Matcher.CreateRemotePath(localFile.FullName); var conflictingRemoteObject = this.Session.GetObjectByPath(wishedRemotePath) as IFileableCmisObject; if (conflictingRemoteObject != null && conflictingRemoteObject.AreAllChildrenIgnored()) { OperationsLogger.Warn(string.Format("The server denies the renaming of {2} from {0} to {1}, because there is an ignored file/folder with this name already, trying to create conflict file/folder name", oldName, localFile.Name, localFile.FullName), e); string newName = localFile.Name; if (localFile is IDirectoryInfo) { (localFile as IDirectoryInfo).MoveTo(Path.Combine((localFile as IDirectoryInfo).Parent.FullName, newName + "_Conflict")); } else if (localFile is IFileInfo) { (localFile as IFileInfo).MoveTo(Path.Combine((localFile as IFileInfo).Directory.FullName, newName + "_Conflict")); } return; } } catch (CmisObjectNotFoundException) { } OperationsLogger.Warn(string.Format("The server denies the renaming of {2} from {0} to {1}", oldName, localFile.Name, localFile.FullName), e); } throw; } catch (CmisPermissionDeniedException) { OperationsLogger.Warn(string.Format("Unable to renamed remote object from {0} to {1}: Permission Denied", oldName, localFile.Name)); return; } OperationsLogger.Info(string.Format("Renamed remote object {0} from {1} to {2}", remoteId.Id, oldName, localFile.Name)); } else { throw new ArgumentException("Given remoteId type is unknown: " + remoteId.GetType().Name); } bool isContentChanged = localFile is IFileInfo ? (localFile as IFileInfo).IsContentChangedTo(obj) : false; obj.Name = (remoteId as ICmisObject).Name; obj.LastRemoteWriteTimeUtc = (remoteId as ICmisObject).LastModificationDate; obj.LastLocalWriteTimeUtc = isContentChanged ? obj.LastLocalWriteTimeUtc : localFile.LastWriteTimeUtc; obj.LastChangeToken = (remoteId as ICmisObject).ChangeToken; obj.Ignored = (remoteId as ICmisObject).AreAllChildrenIgnored(); this.Storage.SaveMappedObject(obj); if (isContentChanged) { throw new ArgumentException("Local file content is also changed => force crawl sync."); } }
/// <summary> /// Solve the specified situation by using localFile and remote object if the local file content is the only changed content. Otherwise the given fallback will be used. /// </summary> /// <param name="localFileSystemInfo">Local filesystem info instance.</param> /// <param name="remoteId">Remote identifier or object.</param> /// <param name="localContent">Hint if the local content has been changed.</param> /// <param name="remoteContent">Information if the remote content has been changed.</param> public override void Solve( IFileSystemInfo localFileSystemInfo, IObjectId remoteId, ContentChangeType localContent, ContentChangeType remoteContent) { if (!(localFileSystemInfo is IFileInfo)) { this.fallbackSolver.Solve(localFileSystemInfo, remoteId, localContent, remoteContent); return; } IFileInfo localFile = localFileSystemInfo as IFileInfo; IDocument remoteDocument = remoteId as IDocument; if (remoteContent != ContentChangeType.NONE) { this.fallbackSolver.Solve(localFile, remoteId, localContent, remoteContent); return; } bool updateLocalDate = false; bool updateRemoteDate = false; var obj = this.Storage.GetObjectByRemoteId(remoteDocument.Id); if (localFile.IsContentChangedTo(obj, true)) { updateRemoteDate = true; try { var transmission = this.transmissionManager.CreateTransmission(TransmissionType.UPLOAD_MODIFIED_FILE, localFile.FullName); obj.LastChecksum = UploadFileWithPWC(localFile, ref remoteDocument, transmission); obj.ChecksumAlgorithmName = "SHA-1"; obj.LastContentSize = remoteDocument.ContentStreamLength ?? localFile.Length; if (remoteDocument.Id != obj.RemoteObjectId) { this.TransmissionStorage.RemoveObjectByRemoteObjectId(obj.RemoteObjectId); obj.RemoteObjectId = remoteDocument.Id; } } catch (Exception ex) { if (ex.InnerException is CmisPermissionDeniedException) { OperationsLogger.Warn(string.Format("Local changed file \"{0}\" has not been uploaded: PermissionDenied", localFile.FullName)); return; } else if (ex.InnerException is CmisStorageException) { OperationsLogger.Warn(string.Format("Local changed file \"{0}\" has not been uploaded: StorageException", localFile.FullName), ex); return; } throw; } } else { // just date sync if (remoteDocument.LastModificationDate != null && localFile.LastWriteTimeUtc < remoteDocument.LastModificationDate) { updateLocalDate = true; } else { updateRemoteDate = true; } } if (this.ServerCanModifyDateTimes) { if (updateLocalDate) { localFile.LastWriteTimeUtc = (DateTime)remoteDocument.LastModificationDate; } else if (updateRemoteDate) { remoteDocument.UpdateLastWriteTimeUtc(localFile.LastWriteTimeUtc); } else { throw new ArgumentException(); } } obj.LastChangeToken = remoteDocument.ChangeToken; obj.LastLocalWriteTimeUtc = localFileSystemInfo.LastWriteTimeUtc; obj.LastRemoteWriteTimeUtc = remoteDocument.LastModificationDate; this.Storage.SaveMappedObject(obj); }
private static void TestItemChange([NotNull] GraphNodeChangeListener listener, [NotNull] Func <IObjectNode> fetchNode, ContentChangeType type, NodeIndex index, object oldValue, object newValue, [NotNull] Action <IObjectNode> change) { var i = 0; var contentOwner = fetchNode(); var changing = new EventHandler <ItemChangeEventArgs>((sender, e) => { Assert.Equal(0, i); VerifyListenerEvent(e, contentOwner, type, index, oldValue, newValue, false); ++i; }); var changed = new EventHandler <ItemChangeEventArgs>((sender, e) => { Assert.Equal(1, i); VerifyListenerEvent(e, contentOwner, type, index, oldValue, newValue, true); ++i; }); listener.ItemChanging += changing; listener.ItemChanged += changed; change(contentOwner); Assert.Equal(2, i); listener.ItemChanging -= changing; listener.ItemChanged -= changed; }
/// <summary> /// Solve the specified situation by using localFile and remote object. /// </summary> /// <param name="localFileSystemInfo">Local filesystem info instance.</param> /// <param name="remoteId">Remote identifier or object.</param> /// <param name="localContent">Hint if the local content has been changed.</param> /// <param name="remoteContent">Information if the remote content has been changed.</param> public override void Solve( IFileSystemInfo localFileSystemInfo, IObjectId remoteId, ContentChangeType localContent = ContentChangeType.NONE, ContentChangeType remoteContent = ContentChangeType.NONE) { Stopwatch completewatch = new Stopwatch(); completewatch.Start(); Logger.Debug("Starting LocalObjectAdded"); string parentId = this.GetParentId(localFileSystemInfo, this.Storage); Guid uuid = this.WriteOrUseUuidIfSupported(localFileSystemInfo); ICmisObject addedObject; try { addedObject = this.AddCmisObject(localFileSystemInfo, parentId, this.Session); } catch (CmisPermissionDeniedException e) { OperationsLogger.Warn(string.Format("Permission denied while trying to Create the locally added object {0} on the server ({1}).", localFileSystemInfo.FullName, e.Message)); return; } OperationsLogger.Info(string.Format("Created remote {2} {0} for {1}", addedObject.Id, localFileSystemInfo.FullName, addedObject is IFolder ? "folder" : "document")); MappedObject mapped = new MappedObject( localFileSystemInfo.Name, addedObject.Id, localFileSystemInfo is IDirectoryInfo ? MappedObjectType.Folder : MappedObjectType.File, parentId, addedObject.ChangeToken) { Guid = uuid, LastRemoteWriteTimeUtc = addedObject.LastModificationDate, LastLocalWriteTimeUtc = localFileSystemInfo is IFileInfo && (localFileSystemInfo as IFileInfo).Length > 0 ? (DateTime?)null : (DateTime?)localFileSystemInfo.LastWriteTimeUtc, LastChangeToken = addedObject.ChangeToken, LastContentSize = localFileSystemInfo is IDirectoryInfo ? -1 : 0, ChecksumAlgorithmName = localFileSystemInfo is IDirectoryInfo ? null : "SHA-1", LastChecksum = localFileSystemInfo is IDirectoryInfo ? null : SHA1.Create().ComputeHash(new byte[0]) }; this.Storage.SaveMappedObject(mapped); var localFile = localFileSystemInfo as IFileInfo; if (localFile != null) { FileTransmissionEvent transmissionEvent = new FileTransmissionEvent(FileTransmissionType.UPLOAD_NEW_FILE, localFile.FullName); this.transmissionManager.AddTransmission(transmissionEvent); if (localFile.Length > 0) { Stopwatch watch = new Stopwatch(); OperationsLogger.Debug(string.Format("Uploading file content of {0}", localFile.FullName)); watch.Start(); IFileUploader uploader = ContentTaskUtils.CreateUploader(); using (SHA1 hashAlg = new SHA1Managed()) using (var fileStream = localFile.Open(FileMode.Open, FileAccess.Read)) { try { uploader.UploadFile(addedObject as IDocument, fileStream, transmissionEvent, hashAlg); } catch (Exception ex) { transmissionEvent.ReportProgress(new TransmissionProgressEventArgs { FailedException = ex }); throw; } mapped.ChecksumAlgorithmName = "SHA-1"; mapped.LastChecksum = hashAlg.Hash; } watch.Stop(); if (this.ServerCanModifyDateTimes) { (addedObject as IDocument).UpdateLastWriteTimeUtc(localFile.LastWriteTimeUtc); } mapped.LastContentSize = localFile.Length; mapped.LastChangeToken = addedObject.ChangeToken; mapped.LastRemoteWriteTimeUtc = addedObject.LastModificationDate; mapped.LastLocalWriteTimeUtc = localFileSystemInfo.LastWriteTimeUtc; if (mapped.RemoteObjectId != addedObject.Id) { this.Storage.RemoveObject(mapped); mapped.RemoteObjectId = addedObject.Id; } this.Storage.SaveMappedObject(mapped); OperationsLogger.Info(string.Format("Uploaded file content of {0} in [{1} msec]", localFile.FullName, watch.ElapsedMilliseconds)); } transmissionEvent.ReportProgress(new TransmissionProgressEventArgs { Completed = true }); } completewatch.Stop(); Logger.Debug(string.Format("Finished LocalObjectAdded after [{0} msec]", completewatch.ElapsedMilliseconds)); }
private static void VerifyListenerEvent(MemberNodeChangeEventArgs e, IContentNode contentOwner, ContentChangeType type, Index index, object oldValue, object newValue, bool changeApplied) { Assert.NotNull(e); Assert.NotNull(contentOwner); Assert.AreEqual(type, e.ChangeType); Assert.AreEqual(contentOwner, e.Member); Assert.AreEqual(index, e.Index); Assert.AreEqual(newValue, e.NewValue); Assert.AreEqual(oldValue, e.OldValue); if (type == ContentChangeType.ValueChange) { Assert.AreEqual(changeApplied ? newValue : oldValue, contentOwner.Retrieve(index)); } }
/// <summary> /// Solve the specified situation by using the session, storage, localFile and remoteId. /// If a folder is affected, simply update the local change time of the corresponding local folder. /// If it is a file and the changeToken is not equal to the saved, the new content is downloaded. /// </summary> /// <param name="localFile">Local file.</param> /// <param name="remoteId">Remote identifier.</param> /// <param name="localContent">Hint if the local content has been changed.</param> /// <param name="remoteContent">Information if the remote content has been changed.</param> public override void Solve( IFileSystemInfo localFile, IObjectId remoteId, ContentChangeType localContent = ContentChangeType.NONE, ContentChangeType remoteContent = ContentChangeType.NONE) { IMappedObject obj = this.Storage.GetObjectByRemoteId(remoteId.Id); if (remoteId is IFolder) { var remoteFolder = remoteId as IFolder; DateTime?lastModified = remoteFolder.LastModificationDate; obj.LastChangeToken = remoteFolder.ChangeToken; if (lastModified != null) { try { localFile.LastWriteTimeUtc = (DateTime)lastModified; } catch (IOException e) { Logger.Debug("Couldn't set the server side modification date", e); } obj.LastLocalWriteTimeUtc = localFile.LastWriteTimeUtc; } } else if (remoteId is IDocument) { byte[] lastChecksum = obj.LastChecksum; var remoteDocument = remoteId as IDocument; DateTime?lastModified = remoteDocument.LastModificationDate; if ((lastModified != null && lastModified != obj.LastRemoteWriteTimeUtc) || obj.LastChangeToken != remoteDocument.ChangeToken) { if (remoteContent != ContentChangeType.NONE) { if (obj.LastLocalWriteTimeUtc != localFile.LastWriteTimeUtc) { throw new ArgumentException("The local file has been changed since last write => aborting update"); } // Download changes var file = localFile as IFileInfo; var cacheFile = this.fsFactory.CreateDownloadCacheFileInfo(file); var transmissionEvent = new FileTransmissionEvent(FileTransmissionType.DOWNLOAD_MODIFIED_FILE, localFile.FullName, cacheFile.FullName); this.queue.AddEvent(transmissionEvent); this.transmissonManager.AddTransmission(transmissionEvent); using (SHA1 hashAlg = new SHA1Managed()) using (var filestream = cacheFile.Open(FileMode.Create, FileAccess.Write, FileShare.None)) using (IFileDownloader download = ContentTaskUtils.CreateDownloader()) { try { download.DownloadFile(remoteDocument, filestream, transmissionEvent, hashAlg); } catch (Exception ex) { transmissionEvent.ReportProgress(new TransmissionProgressEventArgs { FailedException = ex }); throw; } obj.ChecksumAlgorithmName = "SHA-1"; obj.LastChecksum = hashAlg.Hash; } var backupFile = this.fsFactory.CreateFileInfo(file.FullName + ".bak.sync"); Guid?uuid = file.Uuid; cacheFile.Replace(file, backupFile, true); try { file.Uuid = uuid; } catch (RestoreModificationDateException e) { Logger.Debug("Failed to restore modification date of original file", e); } try { backupFile.Uuid = null; } catch (RestoreModificationDateException e) { Logger.Debug("Failed to restore modification date of backup file", e); } byte[] checksumOfOldFile = null; using (var oldFileStream = backupFile.Open(FileMode.Open, FileAccess.Read, FileShare.None)) { checksumOfOldFile = SHA1Managed.Create().ComputeHash(oldFileStream); } if (!lastChecksum.SequenceEqual(checksumOfOldFile)) { var conflictFile = this.fsFactory.CreateConflictFileInfo(file); backupFile.MoveTo(conflictFile.FullName); OperationsLogger.Info(string.Format("Updated local content of \"{0}\" with content of remote document {1} and created conflict file {2}", file.FullName, remoteId.Id, conflictFile.FullName)); } else { backupFile.Delete(); OperationsLogger.Info(string.Format("Updated local content of \"{0}\" with content of remote document {1}", file.FullName, remoteId.Id)); } transmissionEvent.ReportProgress(new TransmissionProgressEventArgs { Completed = true }); } obj.LastRemoteWriteTimeUtc = remoteDocument.LastModificationDate; if (remoteDocument.LastModificationDate != null) { localFile.LastWriteTimeUtc = (DateTime)remoteDocument.LastModificationDate; } obj.LastLocalWriteTimeUtc = localFile.LastWriteTimeUtc; obj.LastContentSize = remoteDocument.ContentStreamLength ?? 0; } obj.LastChangeToken = remoteDocument.ChangeToken; obj.LastRemoteWriteTimeUtc = lastModified; } this.Storage.SaveMappedObject(obj); }
/// <summary> /// Solve the specified situation by taking renaming the local or remote object to the name of the last changed object. /// </summary> /// <param name="localFileSystemInfo">Local file system info.</param> /// <param name="remoteId">Remote object.</param> /// <param name="localContent">Hint if the local content has been changed.</param> /// <param name="remoteContent">Information if the remote content has been changed.</param> public override void Solve( IFileSystemInfo localFileSystemInfo, IObjectId remoteId, ContentChangeType localContent = ContentChangeType.NONE, ContentChangeType remoteContent = ContentChangeType.NONE) { if (localFileSystemInfo is IDirectoryInfo) { var localFolder = localFileSystemInfo as IDirectoryInfo; var remoteFolder = remoteId as IFolder; var mappedObject = this.Storage.GetObjectByRemoteId(remoteFolder.Id); if (localFolder.Name.Equals(remoteFolder.Name)) { mappedObject.Name = localFolder.Name; } else if (localFolder.LastWriteTimeUtc.CompareTo((DateTime)remoteFolder.LastModificationDate) > 0) { string oldName = remoteFolder.Name; try { remoteFolder.Rename(localFolder.Name, true); } catch (CmisConstraintException e) { if (!Utils.IsValidISO885915(localFolder.Name)) { OperationsLogger.Warn(string.Format("Server denied to rename {0} to {1}, perhaps because it contains UTF-8 characters", oldName, localFolder.Name)); throw new InteractionNeededException(string.Format("Server denied renaming of {0}", oldName), e) { Title = string.Format("Server denied renaming of {0}", oldName), Description = string.Format("Server denied to rename {0} to {1}, perhaps because it contains UTF-8 characters", oldName, localFolder.Name) }; } throw; } mappedObject.Name = remoteFolder.Name; OperationsLogger.Info(string.Format("Renamed remote folder {0} with id {2} to {1}", oldName, remoteFolder.Id, remoteFolder.Name)); } else { string oldName = localFolder.Name; localFolder.MoveTo(Path.Combine(localFolder.Parent.FullName, remoteFolder.Name)); mappedObject.Name = remoteFolder.Name; OperationsLogger.Info(string.Format("Renamed local folder {0} to {1}", Path.Combine(localFolder.Parent.FullName, oldName), remoteFolder.Name)); } mappedObject.LastLocalWriteTimeUtc = localFolder.LastWriteTimeUtc; mappedObject.LastRemoteWriteTimeUtc = (DateTime)remoteFolder.LastModificationDate; mappedObject.LastChangeToken = remoteFolder.ChangeToken; mappedObject.Ignored = remoteFolder.AreAllChildrenIgnored(); this.Storage.SaveMappedObject(mappedObject); } else if (localFileSystemInfo is IFileInfo) { var localFile = localFileSystemInfo as IFileInfo; var remoteFile = remoteId as IDocument; var mappedObject = this.Storage.GetObjectByRemoteId(remoteFile.Id); if (localFile.Name.Equals(remoteFile.Name)) { mappedObject.Name = localFile.Name; this.Storage.SaveMappedObject(mappedObject); this.changeChangeSolver.Solve(localFileSystemInfo, remoteId, localContent, remoteContent); } else { string desc = string.Format( "The local file {0} has been locally renamed from {1} to {2} and remotely to {3}. " + "Fix this conflict by renaming the remote file to {2} or the local file to {3}.", localFile.FullName, mappedObject.Name, localFile.Name, remoteFile.Name); OperationsLogger.Warn("Synchronization Conflict: " + desc); throw new InteractionNeededException("Synchronization Conflict") { Title = "Synchronization Conflict", Description = desc }; } } }
private static void TestContentChange(GraphNodeChangeListener listener, IGraphNode contentOwner, ContentChangeType type, Index index, object oldValue, object newValue, GraphNodePath path, Action change) { var i = 0; var prepareChange = new EventHandler <GraphContentChangeEventArgs>((sender, e) => { Assert.AreEqual(0, i); VerifyListenerEvent(e, contentOwner, type, index, oldValue, newValue, path, false); ++i; }); var changing = new EventHandler <GraphContentChangeEventArgs>((sender, e) => { Assert.AreEqual(1, i); VerifyListenerEvent(e, contentOwner, type, index, oldValue, newValue, path, false); ++i; }); var changed = new EventHandler <GraphContentChangeEventArgs>((sender, e) => { Assert.AreEqual(2, i); VerifyListenerEvent(e, contentOwner, type, index, oldValue, newValue, path, true); ++i; }); var finalizeChange = new EventHandler <GraphContentChangeEventArgs>((sender, e) => { Assert.AreEqual(3, i); VerifyListenerEvent(e, contentOwner, type, index, oldValue, newValue, path, true); ++i; }); listener.PrepareChange += prepareChange; listener.Changing += changing; listener.Changed += changed; listener.FinalizeChange += finalizeChange; change(); Assert.AreEqual(4, i); listener.PrepareChange -= prepareChange; listener.Changing -= changing; listener.Changed -= changed; listener.FinalizeChange -= finalizeChange; }
private static void VerifyListenerEvent(GraphContentChangeEventArgs e, IGraphNode contentOwner, ContentChangeType type, Index index, object oldValue, object newValue, GraphNodePath path, bool changeApplied) { Assert.NotNull(e); Assert.NotNull(contentOwner); Assert.NotNull(path); Assert.AreEqual(type, e.ChangeType); Assert.AreEqual(contentOwner.Content, e.Content); Assert.AreEqual(index, e.Index); Assert.AreEqual(newValue, e.NewValue); Assert.AreEqual(oldValue, e.OldValue); Assert.AreEqual(path, e.Path); if (type == ContentChangeType.ValueChange) { Assert.AreEqual(changeApplied ? newValue : oldValue, contentOwner.Content.Retrieve(index)); } }
/// <summary> /// Solve the specified situation by using localFile and remote object. /// </summary> /// <param name="localFileSystemInfo">Local filesystem info instance.</param> /// <param name="remoteId">Remote identifier or object.</param> /// <param name="localContent">Hint if the local content has been changed.</param> /// <param name="remoteContent">Information if the remote content has been changed.</param> public override void Solve( IFileSystemInfo localFileSystemInfo, IObjectId remoteId, ContentChangeType localContent, ContentChangeType remoteContent) { if (localFileSystemInfo is IDirectoryInfo) { IDirectoryInfo localFolder = localFileSystemInfo as IDirectoryInfo; IDirectoryInfo localParent = localFolder.Parent; IFolder remoteFolder = remoteId as IFolder; string remoteParentId = remoteFolder.ParentId; IMappedObject mappedParent = this.Storage.GetObjectByRemoteId(remoteParentId); IMappedObject mappedObject = this.Storage.GetObjectByRemoteId(remoteFolder.Id); if (localParent.Uuid == mappedParent.Guid) { // Both folders are in the same parent folder this.SyncNamesAndDates(localFolder, remoteFolder, mappedObject); } else { OperationsLogger.Warn( string.Format( "Synchronization Conflict: The local directory {0} has been moved to {1} with id {2},{4}" + "but the remote folder was moved to {3}{4}You can fix this situation by moving them into the same folder", localFileSystemInfo.Name, localFileSystemInfo.FullName, remoteFolder.Path, Environment.NewLine)); return; } } else if (localFileSystemInfo is IFileInfo) { IFileInfo localFile = localFileSystemInfo as IFileInfo; IDirectoryInfo localParent = localFile.Directory; IDocument remoteFile = remoteId as IDocument; string remoteParentId = remoteFile.Parents[0].Id; IMappedObject mappedParent = this.Storage.GetObjectByRemoteId(remoteParentId); IMappedObject mappedObject = this.Storage.GetObjectByRemoteId(remoteFile.Id); if (localParent.Uuid == mappedParent.Guid) { // Both files are in the same parent folder mappedObject.ParentId = remoteParentId; this.Storage.SaveMappedObject(mappedObject); throw new ArgumentException("Solved move conflict => invoke crawl sync to detect other changes"); } else { OperationsLogger.Warn( string.Format( "Synchronization Conflict: The local file {0} has been moved to {1} with id {2},{4}" + "but the remote file was moved to {3}{4}You can fix this situation by moving them into the same folder", localFileSystemInfo.Name, localFileSystemInfo.FullName, remoteFile.Paths[0], Environment.NewLine)); return; } } }
public override void Solve( IFileSystemInfo localFileSystemInfo, IObjectId remoteId, ContentChangeType localContent, ContentChangeType remoteContent) { throw new NotImplementedException(); }
/// <summary> /// Solve the specified situation by using localFile and remote object. /// </summary> /// <param name="localFileSystemInfo">Local filesystem info instance.</param> /// <param name="remoteId">Remote identifier or object.</param> /// <param name="localContent">Signalizes how the local content has been modified.</param> /// <param name="remoteContent">Signalizes how the remote content has been modified.</param> public abstract void Solve( IFileSystemInfo localFileSystemInfo, IObjectId remoteId, ContentChangeType localContent, ContentChangeType remoteContent);
/// <summary> /// Solve the specified situation by using the storage, localFile and remoteId. /// Uploads the file content if content has been changed. Otherwise simply saves the /// last modification date. /// </summary> /// <param name="localFileSystemInfo">Local filesystem info instance.</param> /// <param name="remoteId">Remote identifier or object.</param> /// <param name="localContent">Hint if the local content has been changed.</param> /// <param name="remoteContent">Information if the remote content has been changed.</param> public override void Solve( IFileSystemInfo localFileSystemInfo, IObjectId remoteId, ContentChangeType localContent = ContentChangeType.NONE, ContentChangeType remoteContent = ContentChangeType.NONE) { if (!localFileSystemInfo.Exists) { throw new ArgumentException("Given local path does not exists: " + localFileSystemInfo.FullName); } // Match local changes to remote changes and updated them remotely IMappedObject mappedObject = null; try { string ea = localFileSystemInfo.GetExtendedAttribute(MappedObject.ExtendedAttributeKey); Guid guid; if (Guid.TryParse(ea, out guid)) { mappedObject = this.Storage.GetObjectByGuid(guid); } } catch (Exception) { } if (mappedObject == null) { mappedObject = this.Storage.GetObjectByLocalPath(localFileSystemInfo); } if (mappedObject == null) { throw new ArgumentException(string.Format("Could not find db entry for {0} => invoke crawl sync", localFileSystemInfo.FullName)); } IFileInfo localFile = localFileSystemInfo as IFileInfo; if (localFile != null && localFile.IsContentChangedTo(mappedObject, scanOnlyIfModificationDateDiffers: true)) { Logger.Debug(string.Format("\"{0}\" is different from {1}", localFile.FullName, mappedObject.ToString())); OperationsLogger.Debug(string.Format("Local file \"{0}\" has been changed", localFile.FullName)); IFileUploader uploader = FileTransmission.ContentTaskUtils.CreateUploader(); var doc = remoteId as IDocument; FileTransmissionEvent transmissionEvent = new FileTransmissionEvent(FileTransmissionType.UPLOAD_MODIFIED_FILE, localFile.FullName); this.transmissionManager.AddTransmission(transmissionEvent); transmissionEvent.ReportProgress(new TransmissionProgressEventArgs { Started = true }); using (var hashAlg = new SHA1Managed()) using (var file = localFile.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete)) { try { uploader.UploadFile(doc, file, transmissionEvent, hashAlg); } catch (Exception ex) { transmissionEvent.ReportProgress(new TransmissionProgressEventArgs { FailedException = ex }); if (ex.InnerException is CmisPermissionDeniedException) { OperationsLogger.Warn(string.Format("Local changed file \"{0}\" has been not been uploaded: PermissionDenied", localFile.FullName)); return; } throw; } mappedObject.LastChecksum = hashAlg.Hash; } mappedObject.LastChangeToken = doc.ChangeToken; mappedObject.LastRemoteWriteTimeUtc = doc.LastModificationDate; mappedObject.LastLocalWriteTimeUtc = localFile.LastWriteTimeUtc; mappedObject.LastContentSize = localFile.Length; OperationsLogger.Info(string.Format("Local changed file \"{0}\" has been uploaded", localFile.FullName)); transmissionEvent.ReportProgress(new TransmissionProgressEventArgs { Completed = true }); } if (this.ServerCanModifyDateTimes) { try { if (remoteId is IDocument) { (remoteId as IDocument).UpdateLastWriteTimeUtc(localFileSystemInfo.LastWriteTimeUtc); mappedObject.LastRemoteWriteTimeUtc = localFileSystemInfo.LastWriteTimeUtc; } else if (remoteId is IFolder) { (remoteId as IFolder).UpdateLastWriteTimeUtc(localFileSystemInfo.LastWriteTimeUtc); mappedObject.LastRemoteWriteTimeUtc = localFileSystemInfo.LastWriteTimeUtc; } } catch (CmisPermissionDeniedException) { Logger.Debug(string.Format("Locally changed modification date \"{0}\"is not uploaded to the server: PermissionDenied", localFileSystemInfo.LastWriteTimeUtc)); } } mappedObject.LastLocalWriteTimeUtc = localFileSystemInfo.LastWriteTimeUtc; this.Storage.SaveMappedObject(mappedObject); }
/// <summary> /// Initializes a new instance of the <see cref="GraphContentChangeEventArgs"/> class. /// </summary> /// <param name="content">The content that has changed.</param> /// <param name="index">The index in the content where the change occurred.</param> /// <param name="changeType">The type of change that occurred.</param> /// <param name="oldValue">The old value of the content.</param> /// <param name="newValue">The new value of the content.</param> public GraphContentChangeEventArgs(IContent content, Index index, ContentChangeType changeType, object oldValue, object newValue) : base(content, index, changeType, oldValue, newValue) { }
// TODO: move this in AssetPropertyGraph as a private method, it's the only usage (could also be inlined or split in 3 methods) internal Index RetrieveDerivedIndex(Index baseIndex, ContentChangeType changeType) { var baseMemberContent = BaseContent as MemberContent; if (baseMemberContent == null) return Index.Empty; switch (changeType) { case ContentChangeType.ValueChange: { if (baseIndex.IsEmpty) return baseIndex; var baseNode = (AssetNode)BaseContent.OwnerNode; ItemId baseId; if (!baseNode.TryIndexToId(baseIndex, out baseId)) return Index.Empty; Index index; // Find the index of the item in this instance corresponding to the modified item in the base. return TryIdToIndex(baseId, out index) ? index : Index.Empty; } case ContentChangeType.CollectionAdd: { if (baseIndex.IsEmpty) return Index.Empty; var baseNode = (AssetNode)BaseContent.OwnerNode; ItemId baseId; if (!baseNode.TryIndexToId(baseIndex, out baseId)) throw new InvalidOperationException("Cannot find an identifier matching the index in the base collection"); if (BaseContent.Descriptor is CollectionDescriptor) { var currentBaseIndex = baseIndex.Int - 1; // Find the first item before the new one that also exists (in term of id) in the local node while (currentBaseIndex >= 0) { if (!baseNode.TryIndexToId(new Index(currentBaseIndex), out baseId)) throw new InvalidOperationException("Cannot find an identifier matching the index in the base collection"); Index localIndex; // If we have an matching item, we want to insert right after it if (TryIdToIndex(baseId, out localIndex)) return new Index(localIndex.Int + 1); currentBaseIndex--; } // Otherwise, insert at 0 return new Index(0); } return baseIndex; } case ContentChangeType.CollectionRemove: { // If we're removing, we need to find the item id that still exists in our instance but not in the base anymore. var baseIds = CollectionItemIdHelper.GetCollectionItemIds(baseMemberContent.Retrieve()); var instanceIds = CollectionItemIdHelper.GetCollectionItemIds(Content.Retrieve()); var missingIds = baseIds.FindMissingIds(instanceIds); var foundUnique = false; var index = Index.Empty; foreach (var id in missingIds) { if (TryIdToIndex(id, out index)) { if (foundUnique) throw new InvalidOperationException("Couldn't find a unique item id in the instance collection corresponding to the item removed in the base collection"); foundUnique = true; } } return index; } default: throw new ArgumentException(@"Cannot retrieve index in derived asset for a remove operation.", nameof(changeType)); } }
private static void VerifyListenerEvent(INodeChangeEventArgs e, IGraphNode nodeOwner, ContentChangeType type, NodeIndex index, object oldValue, object newValue, bool changeApplied) { Assert.NotNull(e); Assert.NotNull(nodeOwner); Assert.Equal(type, e.ChangeType); Assert.Equal(nodeOwner, e.Node); Assert.Equal(index, (e as ItemChangeEventArgs)?.Index ?? NodeIndex.Empty); Assert.Equal(newValue, e.NewValue); Assert.Equal(oldValue, e.OldValue); if (type == ContentChangeType.ValueChange) { Assert.Equal(changeApplied ? newValue : oldValue, nodeOwner.Retrieve(index)); } }
/// <summary> /// Adds the Object to Disk and Database /// </summary> /// <param name='localFile'> /// Local file. /// </param> /// <param name='remoteId'> /// Remote Object (already fetched). /// </param> /// <param name="localContent">Hint if the local content has been changed.</param> /// <param name="remoteContent">Information if the remote content has been changed.</param> /// <exception cref='ArgumentException'> /// Is thrown when remoteId is not prefetched. /// </exception> public override void Solve( IFileSystemInfo localFile, IObjectId remoteId, ContentChangeType localContent = ContentChangeType.NONE, ContentChangeType remoteContent = ContentChangeType.NONE) { if (localFile is IDirectoryInfo) { if (!(remoteId is IFolder)) { throw new ArgumentException("remoteId has to be a prefetched Folder"); } var remoteFolder = remoteId as IFolder; IDirectoryInfo localFolder = localFile as IDirectoryInfo; localFolder.Create(); Guid uuid = Guid.Empty; if (localFolder.IsExtendedAttributeAvailable()) { uuid = Guid.NewGuid(); try { localFolder.Uuid = uuid; } catch (RestoreModificationDateException) { } } if (remoteFolder.LastModificationDate != null) { localFolder.LastWriteTimeUtc = (DateTime)remoteFolder.LastModificationDate; } var mappedObject = new MappedObject(remoteFolder); mappedObject.Guid = uuid; mappedObject.LastRemoteWriteTimeUtc = remoteFolder.LastModificationDate; mappedObject.LastLocalWriteTimeUtc = localFolder.LastWriteTimeUtc; this.Storage.SaveMappedObject(mappedObject); OperationsLogger.Info(string.Format("New local folder {0} created and mapped to remote folder {1}", localFolder.FullName, remoteId.Id)); } else if (localFile is IFileInfo) { Guid guid = Guid.NewGuid(); var file = localFile as IFileInfo; if (!(remoteId is IDocument)) { throw new ArgumentException("remoteId has to be a prefetched Document"); } if (file.Exists) { Guid?uuid = file.Uuid; if (uuid != null) { if (this.Storage.GetObjectByGuid((Guid)uuid) != null) { throw new ArgumentException("This file has already been synced => force crawl sync"); } } Logger.Debug(string.Format("This file {0} conflicts with remote file => conflict file will be produced after download", file.FullName)); } var cacheFile = this.fsFactory.CreateDownloadCacheFileInfo(guid); IDocument remoteDoc = remoteId as IDocument; var transmissionEvent = new FileTransmissionEvent(FileTransmissionType.DOWNLOAD_NEW_FILE, localFile.FullName, cacheFile.FullName); this.manager.AddTransmission(transmissionEvent); byte[] hash = null; using (var hashAlg = new SHA1Managed()) using (var fileStream = cacheFile.Open(FileMode.Create, FileAccess.Write, FileShare.Read)) using (var downloader = FileTransmission.ContentTaskUtils.CreateDownloader()) { try { downloader.DownloadFile(remoteDoc, fileStream, transmissionEvent, hashAlg); } catch (Exception ex) { transmissionEvent.ReportProgress(new TransmissionProgressEventArgs { FailedException = ex }); throw; } hash = hashAlg.Hash; } cacheFile.Uuid = guid; try { cacheFile.MoveTo(file.FullName); } catch (IOException e) { file.Refresh(); if (file.Exists) { IFileInfo conflictFile = this.fsFactory.CreateConflictFileInfo(file); IFileInfo targetFile = cacheFile.Replace(file, conflictFile, true); targetFile.SetExtendedAttribute(MappedObject.ExtendedAttributeKey, guid.ToString(), true); conflictFile.SetExtendedAttribute(MappedObject.ExtendedAttributeKey, null, true); } else { transmissionEvent.ReportProgress(new TransmissionProgressEventArgs { FailedException = e }); throw; } } file.Refresh(); if (remoteDoc.LastModificationDate != null) { try { file.LastWriteTimeUtc = (DateTime)remoteDoc.LastModificationDate; } catch (IOException e) { Logger.Debug("Cannot set last modification date", e); } } MappedObject mappedObject = new MappedObject( file.Name, remoteDoc.Id, MappedObjectType.File, remoteDoc.Parents[0].Id, remoteDoc.ChangeToken, remoteDoc.ContentStreamLength ?? 0) { Guid = guid, LastLocalWriteTimeUtc = file.LastWriteTimeUtc, LastRemoteWriteTimeUtc = remoteDoc.LastModificationDate, LastChecksum = hash, ChecksumAlgorithmName = "SHA-1" }; this.Storage.SaveMappedObject(mappedObject); OperationsLogger.Info(string.Format("New local file {0} created and mapped to remote file {1}", file.FullName, remoteId.Id)); transmissionEvent.ReportProgress(new TransmissionProgressEventArgs { Completed = true }); } }
// TODO: move this in AssetPropertyGraph as a private method, it's the only usage (could also be inlined or split in 3 methods) internal Index RetrieveDerivedIndex(Index baseIndex, ContentChangeType changeType) { var baseMemberContent = BaseContent as MemberContent; if (baseMemberContent == null) { return(Index.Empty); } switch (changeType) { case ContentChangeType.ValueChange: { if (baseIndex.IsEmpty) { return(baseIndex); } var baseNode = (AssetNode)BaseContent.OwnerNode; ItemId baseId; if (!baseNode.TryIndexToId(baseIndex, out baseId)) { return(Index.Empty); } Index index; // Find the index of the item in this instance corresponding to the modified item in the base. return(TryIdToIndex(baseId, out index) ? index : Index.Empty); } case ContentChangeType.CollectionAdd: { if (baseIndex.IsEmpty) { return(Index.Empty); } var baseNode = (AssetNode)BaseContent.OwnerNode; ItemId baseId; if (!baseNode.TryIndexToId(baseIndex, out baseId)) { throw new InvalidOperationException("Cannot find an identifier matching the index in the base collection"); } if (BaseContent.Descriptor is CollectionDescriptor) { var currentBaseIndex = baseIndex.Int - 1; // Find the first item before the new one that also exists (in term of id) in the local node while (currentBaseIndex >= 0) { if (!baseNode.TryIndexToId(new Index(currentBaseIndex), out baseId)) { throw new InvalidOperationException("Cannot find an identifier matching the index in the base collection"); } Index localIndex; // If we have an matching item, we want to insert right after it if (TryIdToIndex(baseId, out localIndex)) { return(new Index(localIndex.Int + 1)); } currentBaseIndex--; } // Otherwise, insert at 0 return(new Index(0)); } return(baseIndex); } case ContentChangeType.CollectionRemove: { // If we're removing, we need to find the item id that still exists in our instance but not in the base anymore. var baseIds = CollectionItemIdHelper.GetCollectionItemIds(baseMemberContent.Retrieve()); var instanceIds = CollectionItemIdHelper.GetCollectionItemIds(Content.Retrieve()); var missingIds = baseIds.FindMissingIds(instanceIds); var foundUnique = false; var index = Index.Empty; foreach (var id in missingIds) { if (TryIdToIndex(id, out index)) { if (foundUnique) { throw new InvalidOperationException("Couldn't find a unique item id in the instance collection corresponding to the item removed in the base collection"); } foundUnique = true; } } return(index); } default: throw new ArgumentException(@"Cannot retrieve index in derived asset for a remove operation.", nameof(changeType)); } }
public override void Solve( IFileSystemInfo localFileSystemInfo, IObjectId remoteId, ContentChangeType localContent, ContentChangeType remoteContent) { var savedObject = this.Storage.GetObjectByRemoteId(remoteId.Id); string oldPath = localFileSystemInfo.FullName; string oldName = (remoteId as ICmisObject).Name; if ((remoteId as ICmisObject).Name != savedObject.Name) { // Both are renamed and remote is also moved => move & rename/rename string newPath = remoteId is IFolder ? this.Storage.Matcher.CreateLocalPath(remoteId as IFolder) : this.Storage.Matcher.CreateLocalPath(remoteId as IDocument); if ((remoteId as ICmisObject).Name == localFileSystemInfo.Name) { // Move local object to new name, bacause it is the same => only change/change solver is needed this.MoveTo(localFileSystemInfo, oldPath, newPath); savedObject.Name = localFileSystemInfo.Name; savedObject.ParentId = remoteId is IFolder ? (remoteId as IFolder).ParentId : (remoteId as IDocument).Parents[0].Id; this.Storage.SaveMappedObject(savedObject); this.changeChangeSolver.Solve(localFileSystemInfo, remoteId, localContent, remoteContent); } else { // Only move local object to new folder but keep the old name => both names are different => rename/rename solver needed newPath = newPath.TrimEnd(Path.DirectorySeparatorChar); newPath = newPath.Substring(0, newPath.Length - (remoteId as ICmisObject).Name.Length) + oldName; this.MoveTo(localFileSystemInfo, oldPath, newPath); savedObject.ParentId = remoteId is IFolder ? (remoteId as IFolder).ParentId : (remoteId as IDocument).Parents[0].Id; this.Storage.SaveMappedObject(savedObject); this.renameRenameSolver.Solve(localFileSystemInfo, remoteId, localContent, remoteContent); } } else { // Local rename and remote move => move locally and rename remote => change/change try { // rename remote file (remoteId as ICmisObject).Rename(localFileSystemInfo.Name, true); OperationsLogger.Info(string.Format("Renamed remote object {0} from {1} to {2}", remoteId.Id, oldName, localFileSystemInfo.Name)); savedObject.Name = (remoteId as ICmisObject).Name; } catch (CmisConstraintException e) { if (!Utils.IsValidISO885915(localFileSystemInfo.Name)) { OperationsLogger.Warn(string.Format("Server denied to rename {0} to {1}, perhaps because it contains UTF-8 characters", oldName, localFileSystemInfo.Name)); throw new InteractionNeededException(string.Format("Server denied renaming of {0}", oldName), e) { Title = string.Format("Server denied renaming of {0}", oldName), Description = string.Format("Server denied to rename {0} to {1}, perhaps because it contains UTF-8 characters", oldName, localFileSystemInfo.Name) }; } throw; } catch (CmisPermissionDeniedException) { OperationsLogger.Info(string.Format("Permission Denied: Cannot rename remote object ({1}): {0}", (remoteId as ICmisObject).Name, remoteId.Id)); return; } string newPath = remoteId is IFolder ? this.Storage.Matcher.CreateLocalPath(remoteId as IFolder) : this.Storage.Matcher.CreateLocalPath(remoteId as IDocument); // move local object to same directory as the remote object is this.MoveTo(localFileSystemInfo, oldPath, newPath); savedObject.ParentId = remoteId is IFolder ? (remoteId as IFolder).ParentId : (remoteId as IDocument).Parents[0].Id; this.Storage.SaveMappedObject(savedObject); // Synchronize the rest with the change change solver this.changeChangeSolver.Solve(localFileSystemInfo, remoteId, localContent, remoteContent); } }
/// <summary> /// Solve the specified situation by using the storage, localFile and remoteId. /// Uploads the file content if content has been changed. Otherwise simply saves the /// last modification date. /// </summary> /// <param name="localFileSystemInfo">Local filesystem info instance.</param> /// <param name="remoteId">Remote identifier or object.</param> /// <param name="localContent">Hint if the local content has been changed.</param> /// <param name="remoteContent">Information if the remote content has been changed.</param> public override void Solve( IFileSystemInfo localFileSystemInfo, IObjectId remoteId, ContentChangeType localContent = ContentChangeType.NONE, ContentChangeType remoteContent = ContentChangeType.NONE) { if (!localFileSystemInfo.Exists) { throw new ArgumentException("Given local path does not exists: " + localFileSystemInfo.FullName); } // Match local changes to remote changes and updated them remotely IMappedObject mappedObject = this.Storage.GetObject(localFileSystemInfo); if (mappedObject == null) { throw new ArgumentException(string.Format("Could not find db entry for {0} => invoke crawl sync", localFileSystemInfo.FullName)); } if (mappedObject.LastChangeToken != (remoteId as ICmisObjectProperties).ChangeToken) { throw new ArgumentException(string.Format("remote {1} {0} has also been changed since last sync => invoke crawl sync", remoteId.Id, remoteId is IDocument ? "document" : "folder")); } IFileInfo localFile = localFileSystemInfo as IFileInfo; if (localFile != null && localFile.IsContentChangedTo(mappedObject, scanOnlyIfModificationDateDiffers: true)) { Logger.Debug(string.Format("\"{0}\" is different from {1}", localFile.FullName, mappedObject.ToString())); OperationsLogger.Debug(string.Format("Local file \"{0}\" has been changed", localFile.FullName)); var doc = remoteId as IDocument; try { var transmission = this.transmissionManager.CreateTransmission(TransmissionType.UPLOAD_MODIFIED_FILE, localFile.FullName); mappedObject.LastChecksum = UploadFile(localFile, doc, transmission); } catch(Exception ex) { if (ex.InnerException is CmisPermissionDeniedException) { OperationsLogger.Warn(string.Format("Local changed file \"{0}\" has not been uploaded: PermissionDenied", localFile.FullName)); return; } else if (ex.InnerException is CmisStorageException) { OperationsLogger.Warn(string.Format("Local changed file \"{0}\" has not been uploaded: StorageException", localFile.FullName), ex); return; } throw; } mappedObject.LastRemoteWriteTimeUtc = doc.LastModificationDate; mappedObject.LastLocalWriteTimeUtc = localFile.LastWriteTimeUtc; mappedObject.LastContentSize = localFile.Length; OperationsLogger.Info(string.Format("Local changed file \"{0}\" has been uploaded", localFile.FullName)); } if (this.ServerCanModifyDateTimes) { try { if (remoteId is IDocument) { var doc = remoteId as IDocument; doc.UpdateLastWriteTimeUtc(localFileSystemInfo.LastWriteTimeUtc); mappedObject.LastRemoteWriteTimeUtc = doc.LastModificationDate ?? localFileSystemInfo.LastWriteTimeUtc; } else if (remoteId is IFolder) { var folder = remoteId as IFolder; folder.UpdateLastWriteTimeUtc(localFileSystemInfo.LastWriteTimeUtc); mappedObject.LastRemoteWriteTimeUtc = folder.LastModificationDate ?? localFileSystemInfo.LastWriteTimeUtc; } } catch(CmisPermissionDeniedException) { Logger.Debug(string.Format("Locally changed modification date \"{0}\"is not uploaded to the server: PermissionDenied", localFileSystemInfo.LastWriteTimeUtc)); } } mappedObject.LastChangeToken = (remoteId as ICmisObjectProperties).ChangeToken; mappedObject.LastLocalWriteTimeUtc = localFileSystemInfo.LastWriteTimeUtc; this.Storage.SaveMappedObject(mappedObject); }
public override void Solve( IFileSystemInfo localFileSystemInfo, IObjectId remoteId, ContentChangeType localContent, ContentChangeType remoteContent) { var savedObject = this.Storage.GetObjectByRemoteId(remoteId.Id); Guid? newParentUuid = localFileSystemInfo is IFileInfo ? (localFileSystemInfo as IFileInfo).Directory.Uuid : (localFileSystemInfo as IDirectoryInfo).Parent.Uuid; string newParentId = this.Storage.GetObjectByGuid((Guid)newParentUuid).RemoteObjectId; if (localFileSystemInfo.Name == (remoteId as ICmisObject).Name) { // Both names are equal => only move to new remote parent try { (remoteId as IFileableCmisObject).Move(this.Session.GetObject(savedObject.ParentId), this.Session.GetObject(newParentId)); } catch (CmisPermissionDeniedException) { OperationsLogger.Info(string.Format("Permission Denied: Cannot move remote object {0} from {1} to {2}", remoteId.Id, savedObject.ParentId, newParentId)); return; } savedObject.Name = localFileSystemInfo.Name; savedObject.ParentId = newParentId; this.Storage.SaveMappedObject(savedObject); this.changeChangeSolver.Solve(localFileSystemInfo, remoteId, localContent, remoteContent); } else { // Names are different to each other if (localFileSystemInfo.Name == savedObject.Name) { // Remote rename and local move => Move remote and rename locally => change change solver try { remoteId = (remoteId as IFileableCmisObject).Move(this.Session.GetObject(savedObject.ParentId), this.Session.GetObject(newParentId)); } catch (CmisPermissionDeniedException) { OperationsLogger.Info(string.Format("Permission Denied: Cannot move remote object {0} from {1} to {2}", remoteId.Id, savedObject.ParentId, newParentId)); return; } var localParentPath = localFileSystemInfo is IFileInfo ? (localFileSystemInfo as IFileInfo).Directory.FullName : (localFileSystemInfo as IDirectoryInfo).Parent.FullName; string newPath = Path.Combine(localParentPath, (remoteId as ICmisObject).Name); this.MoveTo(localFileSystemInfo, localFileSystemInfo.FullName, newPath); savedObject.Name = localFileSystemInfo.Name; savedObject.ParentId = newParentId; this.Storage.SaveMappedObject(savedObject); this.changeChangeSolver.Solve(localFileSystemInfo, remoteId, localContent, remoteContent); } else { // Both sides have been renamed => Move remote => rename rename solver try { (remoteId as IFileableCmisObject).Move(this.Session.GetObject(savedObject.ParentId), this.Session.GetObject(newParentId)); } catch (CmisPermissionDeniedException) { OperationsLogger.Info(string.Format("Permission Denied: Cannot move remote object {0} from {1} to {2}", remoteId.Id, savedObject.ParentId, newParentId)); return; } savedObject.ParentId = newParentId; this.Storage.SaveMappedObject(savedObject); this.renameRenameSolver.Solve(localFileSystemInfo, remoteId, localContent, remoteContent); } } }
private static void VerifyListenerEvent(GraphContentChangeEventArgs e, IGraphNode contentOwner, ContentChangeType type, Index index, object oldValue, object newValue, bool changeApplied) { Assert.NotNull(e); Assert.NotNull(contentOwner); Assert.AreEqual(type, e.ChangeType); Assert.AreEqual(contentOwner.Content, e.Content); Assert.AreEqual(index, e.Index); Assert.AreEqual(newValue, e.NewValue); Assert.AreEqual(oldValue, e.OldValue); if (type == ContentChangeType.ValueChange) { Assert.AreEqual(changeApplied ? newValue : oldValue, contentOwner.Content.Retrieve(index)); } }
/// <summary> /// Solve the specified situation by using localFile and remote object. /// </summary> /// <param name="localFileSystemInfo">Local filesystem info instance.</param> /// <param name="remoteId">Remote identifier or object.</param> /// <param name="localContent">Hint if the local content has been changed.</param> /// <param name="remoteContent">Information if the remote content has been changed.</param> public override void Solve( IFileSystemInfo localFileSystemInfo, IObjectId remoteId, ContentChangeType localContent, ContentChangeType remoteContent) { var obj = this.Storage.GetObjectByRemoteId(remoteId.Id); if (localFileSystemInfo is IDirectoryInfo) { obj.LastLocalWriteTimeUtc = localFileSystemInfo.LastWriteTimeUtc; obj.LastRemoteWriteTimeUtc = (remoteId as IFolder).LastModificationDate; obj.LastChangeToken = (remoteId as IFolder).ChangeToken; this.Storage.SaveMappedObject(obj); } else if (localFileSystemInfo is IFileInfo) { var fileInfo = localFileSystemInfo as IFileInfo; var doc = remoteId as IDocument; bool updateLocalDate = false; bool updateRemoteDate = false; if (remoteContent == ContentChangeType.NONE) { if (fileInfo.IsContentChangedTo(obj, true)) { // Upload local content updateRemoteDate = true; try { obj.LastChecksum = LocalObjectChanged.UploadFile(fileInfo, doc, this.transmissionManager); obj.LastContentSize = doc.ContentStreamLength ?? fileInfo.Length; } catch (Exception ex) { if (ex.InnerException is CmisPermissionDeniedException) { OperationsLogger.Warn(string.Format("Local changed file \"{0}\" has not been uploaded: PermissionDenied", fileInfo.FullName), ex.InnerException); return; } throw; } } else { // Just date sync if (doc.LastModificationDate != null && fileInfo.LastWriteTimeUtc < (DateTime)doc.LastModificationDate) { updateLocalDate = true; } else { updateRemoteDate = true; } } } else { byte[] actualLocalHash; if (fileInfo.IsContentChangedTo(obj, out actualLocalHash, true)) { // Check if both are changed to the same value if (actualLocalHash == null) { using (var f = fileInfo.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete)) { actualLocalHash = SHA1Managed.Create().ComputeHash(f); } } byte[] remoteHash = doc.ContentStreamHash(); if (remoteHash != null && actualLocalHash.SequenceEqual(remoteHash)) { // Both files are equal obj.LastChecksum = remoteHash; obj.LastContentSize = fileInfo.Length; // Sync dates if (doc.LastModificationDate != null && fileInfo.LastWriteTimeUtc < (DateTime)doc.LastModificationDate) { updateLocalDate = true; } else { updateRemoteDate = true; } } else { // Both are different => Check modification dates // Download remote version and create conflict file updateLocalDate = true; obj.LastChecksum = DownloadChanges(fileInfo, doc, obj, this.fsFactory, this.transmissionManager, Logger); obj.LastContentSize = doc.ContentStreamLength ?? 0; } } else { // Download remote content updateLocalDate = true; obj.LastChecksum = DownloadChanges(fileInfo, doc, obj, this.fsFactory, this.transmissionManager, Logger); obj.LastContentSize = doc.ContentStreamLength ?? 0; } } if (this.ServerCanModifyDateTimes) { if (updateLocalDate) { fileInfo.LastWriteTimeUtc = (DateTime)doc.LastModificationDate; } else if (updateRemoteDate) { doc.UpdateLastWriteTimeUtc(fileInfo.LastWriteTimeUtc); } else { throw new ArgumentException(); } } obj.LastChangeToken = doc.ChangeToken; obj.LastLocalWriteTimeUtc = localFileSystemInfo.LastWriteTimeUtc; obj.LastRemoteWriteTimeUtc = doc.LastModificationDate; this.Storage.SaveMappedObject(obj); } }
/// <summary> /// Initializes a new instance of the <see cref="GraphContentChangeEventArgs"/> class. /// </summary> /// <param name="content">The content that has changed.</param> /// <param name="index">The index in the content where the change occurred.</param> /// <param name="changeType">The type of change that occurred.</param> /// <param name="oldValue">The old value of the content.</param> /// <param name="newValue">The new value of the content.</param> /// <param name="path">The path to the node of content that has been modified.</param> public GraphContentChangeEventArgs(IContent content, Index index, ContentChangeType changeType, object oldValue, object newValue, GraphNodePath path) : base(content, index, changeType, oldValue, newValue) { Path = path; }
private async Task UpdateGameSideReference([NotNull] AssetCompositeEditorViewModel editor, [NotNull] IGraphNode gameSideNode, ContentChangeType changeType, object oldValue, object newValue, Index index) { if (editor == null) { throw new ArgumentNullException(nameof(editor)); } if (!AssetRegistry.IsContentType(gameSideNode.Descriptor.GetInnerCollectionType())) { return; } // Grab the old referenced object if it's not null AttachedReference reference = null; if (!ReferenceEquals(oldValue, null)) { reference = AttachedReferenceManager.GetAttachedReference(oldValue); } // Switch to game thread to actually update objects await editor.Controller.InvokeTask(() => { // For references, push null instead of the real value, the editor asset loader will set the actual value later switch (changeType) { case ContentChangeType.ValueChange: ((IMemberNode)gameSideNode).Update(null); break; case ContentChangeType.CollectionUpdate: ((IObjectNode)gameSideNode).Update(null, index); break; case ContentChangeType.CollectionAdd: ((IObjectNode)gameSideNode).Add(null, index); break; case ContentChangeType.CollectionRemove: var oldValueGameSide = gameSideNode.Retrieve(index); ((IObjectNode)gameSideNode).Remove(oldValueGameSide, index); break; default: throw new ArgumentOutOfRangeException(nameof(changeType), changeType, null); } if (oldValue == newValue) { return(Task.CompletedTask); } // Unregister the previous value if (reference != null) { return(editor.Controller.Loader.Manager.ClearContentReference(Owner.Id, reference.Id, gameSideNode, index)); } return(Task.CompletedTask); }); }
/// <summary> /// Solve the specified situation by using localFile and remote object. /// </summary> /// <param name="localFileSystemInfo">Local filesystem info instance.</param> /// <param name="remoteId">Remote identifier or object.</param> /// <param name="localContent">Hint if the local content has been changed.</param> /// <param name="remoteContent">Information if the remote content has been changed.</param> public override void Solve( IFileSystemInfo localFileSystemInfo, IObjectId remoteId, ContentChangeType localContent = ContentChangeType.NONE, ContentChangeType remoteContent = ContentChangeType.NONE) { Stopwatch completewatch = new Stopwatch(); completewatch.Start(); Logger.Debug("Starting LocalObjectAdded"); localFileSystemInfo.Refresh(); if (!localFileSystemInfo.Exists) { throw new FileNotFoundException(string.Format("Local file/folder {0} has been renamed/moved/deleted", localFileSystemInfo.FullName)); } string parentId = Storage.GetRemoteId(this.GetParent(localFileSystemInfo)); if (parentId == null) { if (this.IsParentReadOnly(localFileSystemInfo)) { return; } else { throw new ArgumentException("ParentId is null => invoke crawl sync to create parent first"); } } ICmisObject addedObject; try { addedObject = this.AddCmisObject(localFileSystemInfo, parentId, this.Session); } catch (CmisConstraintException e) { this.EnsureThatLocalFileNameContainsLegalCharacters(localFileSystemInfo, e); throw; } catch (CmisPermissionDeniedException e) { OperationsLogger.Warn(string.Format("Permission denied while trying to Create the locally added object {0} on the server ({1}).", localFileSystemInfo.FullName, e.Message)); return; } Guid uuid = this.WriteOrUseUuidIfSupported(localFileSystemInfo); OperationsLogger.Info(string.Format("Created remote {2} {0} for {1}", addedObject.Id, localFileSystemInfo.FullName, addedObject is IFolder ? "folder" : "document")); MappedObject mapped = new MappedObject( localFileSystemInfo.Name, addedObject.Id, localFileSystemInfo is IDirectoryInfo ? MappedObjectType.Folder : MappedObjectType.File, parentId, addedObject.ChangeToken) { Guid = uuid, LastRemoteWriteTimeUtc = addedObject.LastModificationDate, LastLocalWriteTimeUtc = localFileSystemInfo is IFileInfo && (localFileSystemInfo as IFileInfo).Length > 0 ? (DateTime?)null : (DateTime?)localFileSystemInfo.LastWriteTimeUtc, LastChangeToken = addedObject.ChangeToken, LastContentSize = localFileSystemInfo is IDirectoryInfo ? -1 : 0, ChecksumAlgorithmName = localFileSystemInfo is IDirectoryInfo ? null : "SHA-1", LastChecksum = localFileSystemInfo is IDirectoryInfo ? null : SHA1.Create().ComputeHash(new byte[0]) }; this.Storage.SaveMappedObject(mapped); var localFile = localFileSystemInfo as IFileInfo; if (localFile != null) { var transmission = this.transmissionManager.CreateTransmission(TransmissionType.UPLOAD_NEW_FILE, localFile.FullName); if (localFile.Length > 0) { Stopwatch watch = new Stopwatch(); OperationsLogger.Debug(string.Format("Uploading file content of {0}", localFile.FullName)); watch.Start(); try { mapped.LastChecksum = this.UploadFile(localFile, addedObject as IDocument, transmission); mapped.ChecksumAlgorithmName = "SHA-1"; } catch (Exception ex) { if (ex is UploadFailedException && (ex as UploadFailedException).InnerException is CmisStorageException) { OperationsLogger.Warn(string.Format("Could not upload file content of {0}:", localFile.FullName), (ex as UploadFailedException).InnerException); return; } throw; } watch.Stop(); if (this.ServerCanModifyDateTimes) { (addedObject as IDocument).UpdateLastWriteTimeUtc(localFile.LastWriteTimeUtc); } mapped.LastContentSize = localFile.Length; mapped.LastChangeToken = addedObject.ChangeToken; mapped.LastRemoteWriteTimeUtc = addedObject.LastModificationDate; mapped.LastLocalWriteTimeUtc = localFileSystemInfo.LastWriteTimeUtc; this.Storage.SaveMappedObject(mapped); OperationsLogger.Info(string.Format("Uploaded file content of {0} in [{1} msec]", localFile.FullName, watch.ElapsedMilliseconds)); } else { transmission.Length = 0; transmission.Position = 0; transmission.Status = TransmissionStatus.FINISHED; } } completewatch.Stop(); Logger.Debug(string.Format("Finished LocalObjectAdded after [{0} msec]", completewatch.ElapsedMilliseconds)); }
/// <summary> /// Solve the specified situation by using localFile and remote object. /// </summary> /// <param name="localFileSystemInfo">Local filesystem info instance.</param> /// <param name="remoteId">Remote identifier or object.</param> /// <param name="localContent">Hint if the local content has been changed.</param> /// <param name="remoteContent">Information if the remote content has been changed.</param> public override void Solve( IFileSystemInfo localFileSystemInfo, IObjectId remoteId, ContentChangeType localContent = ContentChangeType.NONE, ContentChangeType remoteContent = ContentChangeType.NONE) { if (localFileSystemInfo is IFileInfo) { IFileInfo localFile = localFileSystemInfo as IFileInfo; localFile.Refresh(); if (!localFile.Exists) { throw new FileNotFoundException(string.Format("Local file {0} has been renamed/moved/deleted", localFile.FullName)); } if (localFile.Length == 0) { this.folderOrEmptyFileAddedSolver.Solve(localFileSystemInfo, null, localContent, remoteContent); return; } string parentId = this.Storage.GetRemoteId(localFile.Directory); if (parentId == null) { if (this.IsParentReadOnly(localFile)) { return; } else { throw new ArgumentException("ParentId is null => invoke crawl sync to create parent first"); } } IDocument remoteDocument; try { remoteDocument = this.CreateOrLoadExistingRemoteDocument(localFile, new ObjectId(parentId)); } catch (CmisPermissionDeniedException e) { OperationsLogger.Warn(string.Format("Permission denied while trying to Create the locally added object {0} on the server ({1}).", localFile.FullName, e.Message)); return; } Guid uuid = this.WriteOrUseUuidIfSupported(localFile); var transmission = this.transmissionManager.CreateTransmission(TransmissionType.UPLOAD_NEW_FILE, localFile.FullName); MappedObject mapped = new MappedObject( localFile.Name, remoteDocument.Id, MappedObjectType.File, parentId, remoteDocument.ChangeToken) { Guid = uuid, LastRemoteWriteTimeUtc = remoteDocument.LastModificationDate, LastLocalWriteTimeUtc = (DateTime?)localFileSystemInfo.LastWriteTimeUtc, LastChangeToken = remoteDocument.ChangeToken, LastContentSize = 0, ChecksumAlgorithmName = "SHA-1", LastChecksum = SHA1.Create().ComputeHash(new byte[0]) }; Stopwatch watch = new Stopwatch(); OperationsLogger.Debug(string.Format("Uploading file content of {0}", localFile.FullName)); watch.Start(); try { mapped.LastChecksum = this.UploadFileWithPWC(localFile, ref remoteDocument, transmission); mapped.ChecksumAlgorithmName = "SHA-1"; mapped.RemoteObjectId = remoteDocument.Id; } catch (Exception ex) { if (ex is UploadFailedException && (ex as UploadFailedException).InnerException is CmisStorageException) { OperationsLogger.Warn(string.Format("Could not upload file content of {0}:", localFile.FullName), (ex as UploadFailedException).InnerException); return; } throw; } watch.Stop(); mapped.LastContentSize = localFile.Length; mapped.LastChangeToken = remoteDocument.ChangeToken; mapped.LastRemoteWriteTimeUtc = remoteDocument.LastModificationDate; mapped.LastLocalWriteTimeUtc = localFileSystemInfo.LastWriteTimeUtc; this.Storage.SaveMappedObject(mapped); OperationsLogger.Info(string.Format("Uploaded file content of {0} in [{1} msec]", localFile.FullName, watch.ElapsedMilliseconds)); } else { this.folderOrEmptyFileAddedSolver.Solve(localFileSystemInfo, remoteId, localContent, remoteContent); } }
/// <summary> /// Renames the specified localFile to the name of the given remoteId object by using the storage, localFile and remoteId. /// </summary> /// <param name="localFile">Local file or folder. It is the source file/folder reference, which should be renamed.</param> /// <param name="remoteId">Remote identifier. Should be an instance of IFolder or IDocument.</param> /// <param name="localContent">Hint if the local content has been changed.</param> /// <param name="remoteContent">Information if the remote content has been changed.</param> public override void Solve( IFileSystemInfo localFile, IObjectId remoteId, ContentChangeType localContent = ContentChangeType.NONE, ContentChangeType remoteContent = ContentChangeType.NONE) { IMappedObject obj = this.Storage.GetObjectByRemoteId(remoteId.Id); if (remoteId is IFolder) { // Rename local folder IFolder remoteFolder = remoteId as IFolder; IDirectoryInfo dirInfo = localFile as IDirectoryInfo; string oldPath = dirInfo.FullName; try { dirInfo.MoveTo(Path.Combine(dirInfo.Parent.FullName, remoteFolder.Name)); obj.Name = remoteFolder.Name; } catch (IOException) { if (dirInfo.Name.Equals(remoteFolder.Name, StringComparison.OrdinalIgnoreCase)) { obj.Name = dirInfo.Name; } else { throw; } } if (remoteFolder.LastModificationDate != null) { dirInfo.LastWriteTimeUtc = (DateTime)remoteFolder.LastModificationDate; } obj.LastChangeToken = remoteFolder.ChangeToken; obj.LastRemoteWriteTimeUtc = remoteFolder.LastModificationDate; obj.LastLocalWriteTimeUtc = dirInfo.LastWriteTimeUtc; this.Storage.SaveMappedObject(obj); OperationsLogger.Info(string.Format("Renamed local folder {0} to {1}", oldPath, remoteFolder.Name)); } else if (remoteId is IDocument) { // Rename local file IDocument remoteDocument = remoteId as IDocument; IFileInfo fileInfo = localFile as IFileInfo; string oldPath = fileInfo.FullName; fileInfo.MoveTo(Path.Combine(fileInfo.Directory.FullName, remoteDocument.Name)); if (remoteDocument.LastModificationDate != null) { fileInfo.LastWriteTimeUtc = (DateTime)remoteDocument.LastModificationDate; } obj.Name = remoteDocument.Name; obj.LastChangeToken = remoteContent == ContentChangeType.NONE ? remoteDocument.ChangeToken : obj.LastChangeToken; obj.LastRemoteWriteTimeUtc = remoteContent == ContentChangeType.NONE ? remoteDocument.LastModificationDate : obj.LastRemoteWriteTimeUtc; obj.LastLocalWriteTimeUtc = fileInfo.LastWriteTimeUtc; this.Storage.SaveMappedObject(obj); OperationsLogger.Info(string.Format("Renamed local file {0} to {1}", oldPath, remoteDocument.Name)); if (remoteContent != ContentChangeType.NONE) { throw new ArgumentException("Remote documents content is also changed => force crawl sync."); } } else { throw new ArgumentException("Given remote Id is not an IFolder nor an IDocument instance"); } }
/// <summary> /// Adds the Object to Disk and Database /// </summary> /// <param name='localFile'> /// Local file. /// </param> /// <param name='remoteId'> /// Remote Object (already fetched). /// </param> /// <param name="localContent">Hint if the local content has been changed.</param> /// <param name="remoteContent">Information if the remote content has been changed.</param> /// <exception cref='ArgumentException'> /// Is thrown when remoteId is not prefetched. /// </exception> public override void Solve( IFileSystemInfo localFile, IObjectId remoteId, ContentChangeType localContent = ContentChangeType.NONE, ContentChangeType remoteContent = ContentChangeType.NONE) { if (localFile is IDirectoryInfo) { if (!(remoteId is IFolder)) { throw new ArgumentException("remoteId has to be a prefetched Folder"); } var remoteFolder = remoteId as IFolder; IDirectoryInfo localFolder = localFile as IDirectoryInfo; localFolder.Create(); Guid uuid = Guid.Empty; if (localFolder.IsExtendedAttributeAvailable()) { uuid = Guid.NewGuid(); try { localFolder.Uuid = uuid; } catch (RestoreModificationDateException) { } } if (remoteFolder.LastModificationDate != null) { try { localFolder.LastWriteTimeUtc = (DateTime)remoteFolder.LastModificationDate; } catch (IOException e) { Logger.Info("Directory modification date could not be synced", e); } } var mappedObject = new MappedObject(remoteFolder); mappedObject.Guid = uuid; mappedObject.LastRemoteWriteTimeUtc = remoteFolder.LastModificationDate; mappedObject.LastLocalWriteTimeUtc = localFolder.LastWriteTimeUtc; mappedObject.Ignored = remoteFolder.AreAllChildrenIgnored(); this.Storage.SaveMappedObject(mappedObject); OperationsLogger.Info(string.Format("New local folder {0} created and mapped to remote folder {1}", localFolder.FullName, remoteId.Id)); } else if (localFile is IFileInfo) { if (!(remoteId is IDocument)) { throw new ArgumentException("remoteId has to be a prefetched Document"); } Guid guid = Guid.NewGuid(); byte[] localFileHash = null; DateTime?lastLocalFileModificationDate = null; var file = localFile as IFileInfo; if (file.Exists) { Guid?uuid = file.Uuid; if (uuid != null) { if (this.Storage.GetObjectByGuid((Guid)uuid) != null) { throw new ArgumentException("This file has already been synced => force crawl sync"); } } lastLocalFileModificationDate = file.LastWriteTimeUtc; if (this.MergeExistingFileWithRemoteFile(file, remoteId as IDocument, guid, out localFileHash)) { return; } Logger.Debug(string.Format("This file {0} conflicts with remote file => conflict file will could be produced after download", file.FullName)); } var cacheFile = this.fsFactory.CreateDownloadCacheFileInfo(guid); IDocument remoteDoc = remoteId as IDocument; var transmission = this.manager.CreateTransmission(TransmissionType.DOWNLOAD_NEW_FILE, localFile.FullName, cacheFile.FullName); byte[] hash = DownloadCacheFile(cacheFile, remoteDoc, transmission, this.fsFactory); try { cacheFile.Uuid = guid; } catch (RestoreModificationDateException e) { Logger.Debug("Could not retore the last modification date of " + cacheFile.FullName, e); } try { cacheFile.MoveTo(file.FullName); } catch (IOException e) { file.Refresh(); if (file.Exists) { if (localFileHash == null || lastLocalFileModificationDate == null || !lastLocalFileModificationDate.Equals(file.LastWriteTimeUtc)) { lastLocalFileModificationDate = file.LastWriteTimeUtc; using (var f = file.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete)) { localFileHash = SHA1Managed.Create().ComputeHash(f); } } if (localFileHash.SequenceEqual(hash)) { file.Uuid = guid; try { cacheFile.Delete(); } catch (IOException) { } } else { IFileInfo conflictFile = this.fsFactory.CreateConflictFileInfo(file); try { IFileInfo targetFile = cacheFile.Replace(file, conflictFile, true); try { targetFile.Uuid = guid; } catch (RestoreModificationDateException restoreException) { Logger.Debug("Could not retore the last modification date of " + targetFile.FullName, restoreException); } } catch (Exception ex) { transmission.FailedException = ex; throw; } try { conflictFile.Uuid = null; } catch (RestoreModificationDateException restoreException) { Logger.Debug("Could not retore the last modification date of " + conflictFile.FullName, restoreException); } } } else { transmission.FailedException = e; throw; } } file.Refresh(); if (remoteDoc.LastModificationDate != null) { try { file.LastWriteTimeUtc = (DateTime)remoteDoc.LastModificationDate; } catch (IOException e) { Logger.Debug("Cannot set last modification date", e); } } MappedObject mappedObject = new MappedObject( file.Name, remoteDoc.Id, MappedObjectType.File, remoteDoc.Parents[0].Id, remoteDoc.ChangeToken, remoteDoc.ContentStreamLength ?? 0) { Guid = guid, LastLocalWriteTimeUtc = file.LastWriteTimeUtc, LastRemoteWriteTimeUtc = remoteDoc.LastModificationDate, LastChecksum = hash, ChecksumAlgorithmName = "SHA-1" }; this.Storage.SaveMappedObject(mappedObject); OperationsLogger.Info(string.Format("New local file {0} created and mapped to remote file {1}", file.FullName, remoteId.Id)); transmission.Status = TransmissionStatus.FINISHED; } }
protected static void UpdateGameSideContent(IGraphNode gameSideNode, object value, ContentChangeType changeType, Index index) { switch (changeType) { case ContentChangeType.ValueChange: ((IMemberNode)gameSideNode).Update(value); break; case ContentChangeType.CollectionUpdate: ((IObjectNode)gameSideNode).Update(value, index); break; case ContentChangeType.CollectionAdd: ((IObjectNode)gameSideNode).Add(value, index); break; case ContentChangeType.CollectionRemove: var item = gameSideNode.Retrieve(index); ((IObjectNode)gameSideNode).Remove(item, index); break; default: throw new ArgumentOutOfRangeException(); } }
/// <summary> /// Initializes a new instance of the <see cref="GraphContentChangeEventArgs"/> class. /// </summary> /// <param name="content">The content that has changed.</param> /// <param name="index">The index in the content where the change occurred, if applicable. <c>null</c> otherwise.</param> /// <param name="changeType">The type of change that occurred.</param> /// <param name="oldValue">The old value of the content.</param> /// <param name="newValue">The new value of the content.</param> /// <param name="path">The path to the node of content that has been modified.</param> public GraphContentChangeEventArgs(IContent content, object index, ContentChangeType changeType, object oldValue, object newValue, GraphNodePath path) : base(content, index, changeType, oldValue, newValue) { Path = path; }
/// <summary> /// Solve the specified situation by using the storage, localFile and remoteId. /// Uploads the file content if content has been changed. Otherwise simply saves the /// last modification date. /// </summary> /// <param name="localFileSystemInfo">Local filesystem info instance.</param> /// <param name="remoteId">Remote identifier or object.</param> /// <param name="localContent">Hint if the local content has been changed.</param> /// <param name="remoteContent">Information if the remote content has been changed.</param> public override void Solve( IFileSystemInfo localFileSystemInfo, IObjectId remoteId, ContentChangeType localContent = ContentChangeType.NONE, ContentChangeType remoteContent = ContentChangeType.NONE) { if (!localFileSystemInfo.Exists) { throw new ArgumentException("Given local path does not exists: " + localFileSystemInfo.FullName); } // Match local changes to remote changes and updated them remotely IMappedObject mappedObject = null; try { Guid?guid = localFileSystemInfo.Uuid; if (guid != null) { mappedObject = this.Storage.GetObjectByGuid((Guid)guid); } } catch (Exception) { } if (mappedObject == null) { mappedObject = this.Storage.GetObjectByLocalPath(localFileSystemInfo); } if (mappedObject == null) { throw new ArgumentException(string.Format("Could not find db entry for {0} => invoke crawl sync", localFileSystemInfo.FullName)); } if (mappedObject.LastChangeToken != (remoteId as ICmisObjectProperties).ChangeToken) { throw new ArgumentException(string.Format("remote {1} {0} has also been changed since last sync => invoke crawl sync", remoteId.Id, remoteId is IDocument ? "document" : "folder")); } IFileInfo localFile = localFileSystemInfo as IFileInfo; if (localFile != null && localFile.IsContentChangedTo(mappedObject, scanOnlyIfModificationDateDiffers: true)) { Logger.Debug(string.Format("\"{0}\" is different from {1}", localFile.FullName, mappedObject.ToString())); OperationsLogger.Debug(string.Format("Local file \"{0}\" has been changed", localFile.FullName)); var doc = remoteId as IDocument; try { mappedObject.LastChecksum = AbstractEnhancedSolver.UploadFile(localFile, doc, this.transmissionManager); } catch (Exception ex) { if (ex.InnerException is CmisPermissionDeniedException) { OperationsLogger.Warn(string.Format("Local changed file \"{0}\" has not been uploaded: PermissionDenied", localFile.FullName)); return; } else if (ex.InnerException is CmisStorageException) { OperationsLogger.Warn(string.Format("Local changed file \"{0}\" has not been uploaded: StorageException", localFile.FullName), ex); return; } throw; } mappedObject.LastRemoteWriteTimeUtc = doc.LastModificationDate; mappedObject.LastLocalWriteTimeUtc = localFile.LastWriteTimeUtc; mappedObject.LastContentSize = localFile.Length; OperationsLogger.Info(string.Format("Local changed file \"{0}\" has been uploaded", localFile.FullName)); } if (this.ServerCanModifyDateTimes) { try { if (remoteId is IDocument) { var doc = remoteId as IDocument; doc.UpdateLastWriteTimeUtc(localFileSystemInfo.LastWriteTimeUtc); mappedObject.LastRemoteWriteTimeUtc = doc.LastModificationDate ?? localFileSystemInfo.LastWriteTimeUtc; } else if (remoteId is IFolder) { var folder = remoteId as IFolder; folder.UpdateLastWriteTimeUtc(localFileSystemInfo.LastWriteTimeUtc); mappedObject.LastRemoteWriteTimeUtc = folder.LastModificationDate ?? localFileSystemInfo.LastWriteTimeUtc; } } catch (CmisPermissionDeniedException) { Logger.Debug(string.Format("Locally changed modification date \"{0}\"is not uploaded to the server: PermissionDenied", localFileSystemInfo.LastWriteTimeUtc)); } } mappedObject.LastChangeToken = (remoteId as ICmisObjectProperties).ChangeToken; mappedObject.LastLocalWriteTimeUtc = localFileSystemInfo.LastWriteTimeUtc; this.Storage.SaveMappedObject(mappedObject); }
public void ResetOverride(Index index, object overriddenValue, ContentChangeType changeType) { if (BaseContent == null) return; if (changeType == ContentChangeType.ValueChange) { // Make sure that what we're trying to reset is actually overridden. if ((index != Index.Empty && !IsItemOverridden(index)) || (index == Index.Empty && !IsContentOverridden())) return; } object baseValue; object clonedValue; ResettingOverride = true; switch (changeType) { case ContentChangeType.ValueChange: baseValue = RetrieveBaseContent(index); clonedValue = Cloner(baseValue); Content.Update(clonedValue, index); break; case ContentChangeType.CollectionRemove: baseValue = RetrieveBaseContent(index); clonedValue = Cloner(baseValue); Content.Add(clonedValue, index); break; case ContentChangeType.CollectionAdd: var value = Content.Retrieve(index); Content.Remove(value, index); break; } ResettingOverride = false; }
// TODO: this method is should be called in every scenario of ReconcileWithBase, it is not the case yet. protected virtual bool CanUpdate(IAssetNode node, ContentChangeType changeType, Index index, object value) { return(true); }