using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.IO; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Media.Imaging; using System.Windows.Threading; using Comal.Classes; using InABox.Clients; using InABox.Configuration; using InABox.Core; using InABox.DynamicGrid; using InABox.WPF; using Syncfusion.UI.Xaml.Grid; using Syncfusion.UI.Xaml.TreeGrid; namespace PRSDesktop { public enum SVNFileStatus { Unknown, RemoteAddition, LocalAddition, RemoteModified, LocalModified, UpToDate } public class SVNObject : INotifyPropertyChanged { private static readonly Dictionary _images = new() { { SVNFileStatus.Unknown, Resources.warning.AsBitmapImage() }, { SVNFileStatus.UpToDate, Resources.tick.AsBitmapImage() }, { SVNFileStatus.RemoteAddition, Resources.download.AsBitmapImage() }, { SVNFileStatus.LocalAddition, Resources.upload.AsBitmapImage() }, { SVNFileStatus.RemoteModified, Resources.remotefile.AsBitmapImage() }, { SVNFileStatus.LocalModified, Resources.lightbulb.AsBitmapImage() } }; private long _localsize; private DateTime _localtime = DateTime.MinValue; private readonly SVNObject _parent; private long _remotesize; private DateTime _remotetime = DateTime.MinValue; private SVNFileStatus _status = SVNFileStatus.Unknown; public SVNObject(string[] path, string name, SVNObject parent = null) { ID = Guid.NewGuid(); _status = SVNFileStatus.Unknown; Path = path; Name = name; _parent = parent; } public SVNFileStatus Status { get => _status; set { _status = value; NotifyPropertyChanged("Status"); } } public BitmapImage Image => _images[Status]; public Guid ID { get; } public Guid ParentID => _parent != null ? _parent.ID : Guid.Empty; public string[] Path { get; } public string Name { get; } public long RemoteSize { get => _remotesize; set { _remotesize = value; Status = CheckStatus(); NotifyPropertyChanged("RemoteSize"); } } public DateTime RemoteTime { get => _remotetime; set { _remotetime = value; Status = CheckStatus(); NotifyPropertyChanged("RemoteTime"); } } public long LocalSize { get => _localsize; set { _localsize = value; Status = CheckStatus(); NotifyPropertyChanged("LocalSize"); } } public DateTime LocalTime { get => _localtime; set { _localtime = value; Status = CheckStatus(); NotifyPropertyChanged("LocalTime"); } } public string DisplayName => Uri.UnescapeDataString(Name).TrimEnd('/'); public event PropertyChangedEventHandler PropertyChanged; public bool Equals(string[] segments) { if (segments.Length != Path.Length + 1) return false; for (var i = 0; i < Path.Length; i++) if (!string.Equals(Path[i], segments[i])) return false; if (!string.Equals(Name, segments.Last())) return false; return true; } public override string ToString() { return string.Format("[{0}].[{1}]", string.Join("].[", Path), Name); } private SVNFileStatus CheckStatus() { if (_localtime.IsEmpty()) return SVNFileStatus.RemoteAddition; if (_remotetime.IsEmpty()) return SVNFileStatus.LocalAddition; if (_remotetime > _localtime) return SVNFileStatus.RemoteModified; if (_localtime > _remotetime) return SVNFileStatus.LocalModified; return SVNFileStatus.UpToDate; } public void NotifyPropertyChanged(string propName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propName)); } } public class SVNFolder : SVNObject { public SVNFolder(string[] path, string name, SVNObject parent = null) : base(path, name, parent) { } } public class SVNFile : SVNObject { public SVNFile(string[] path, string name, SVNObject parent = null) : base(path, name, parent) { } } public abstract class SyncEntry { public Uri Parent { get; set; } public Uri Path { get; set; } public long Size { get; set; } public DateTime Modified { get; set; } } public class LocalSyncEntry : SyncEntry { } public class RemoteSyncEntry : SyncEntry { } /// /// Interaction logic for JobDocuments.xaml /// public partial class JobDocuments : UserControl, IPanel, IJobControl { private readonly ObservableCollection _files = new(); private readonly ObservableCollection _folders = new(); private readonly string _localroot = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "test"); private Uri _serverroot = new("https://MacOSW10/svn/test/"); private WaitCursor _waitcursor; //private SvnClient svnclient = null; private readonly DispatcherTimer _waitcursortimer; private string jobnumber = ""; private Guid lastjobid = Guid.Empty; private readonly BackgroundWorker localfiles; private readonly BackgroundWorker localfolders; private readonly BackgroundWorker remotefiles; private readonly BackgroundWorker remotefolders; private JobDocumentsScreenSettings settings; public JobDocuments() { InitializeComponent(); remotefolders = new BackgroundWorker(); remotefolders.WorkerReportsProgress = true; remotefolders.WorkerSupportsCancellation = false; //remotefolders.DoWork += RemoteFoldersRefresh; remotefolders.ProgressChanged += (o, e) => HandleProgress(e, _folders); localfolders = new BackgroundWorker(); localfolders.WorkerReportsProgress = true; localfolders.WorkerSupportsCancellation = false; //localfolders.DoWork += LocalFoldersRefresh; localfolders.ProgressChanged += (o, e) => HandleProgress(e, _folders); remotefiles = new BackgroundWorker(); remotefiles.WorkerReportsProgress = true; remotefiles.WorkerSupportsCancellation = false; //remotefiles.DoWork += RemoteFilesRefresh; remotefiles.ProgressChanged += (o, e) => HandleProgress(e, _files); localfiles = new BackgroundWorker(); localfiles.WorkerReportsProgress = true; localfiles.WorkerSupportsCancellation = false; localfiles.DoWork += LocalFilesRefresh; localfiles.ProgressChanged += (o, e) => HandleProgress(e, _files); _waitcursortimer = new DispatcherTimer(); _waitcursortimer.Interval = TimeSpan.FromMilliseconds(50F); _waitcursortimer.Tick += CheckWaitCursor; _waitcursortimer.IsEnabled = true; } public Guid ParentID { get; set; } public JobPanelSettings Settings { get; set; } public bool IsReady { get; set; } public event DataModelUpdateEvent OnUpdateDataModel; public void CreateToolbarButtons(IPanelHost host) { } public string SectionName => "Job Documents"; public DataModel DataModel(Selection selection) { return new JobDetailsDataModel(new Filter(x => x.ID).IsEqualTo(ParentID)); } public void Heartbeat(TimeSpan time) { } public void Refresh() { if (ParentID == Guid.Empty || ParentID == CoreUtils.FullGuid) return; // using (SvnClient svnClient = new SvnClient()) // { // svnClient.Authentication.Clear(); // svnClient.Authentication.DefaultCredentials = new System.Net.NetworkCredential("admin", "admin"); // svnClient.Authentication.SslServerTrustHandlers += (o, e) => // { // e.AcceptedFailures = e.Failures; // e.Save = true; // }; // // //var statusArgs = new SvnStatusArgs(); // //statusArgs.Depth = SvnDepth.Infinity; // //statusArgs.RetrieveAllEntries = true; // //Collection statuses; // //bool bResult = svnClient.GetStatus(Path.Combine(_localroot, GetFolderPath("\\")), statusArgs, out statuses); // //foreach (SvnStatusEventArgs statusEventArgs in statuses) // //{ // // if (statusEventArgs.LocalContentStatus == SvnStatus.Modified) // // System.Console.WriteLine("Modified file: " + statusEventArgs.Path); // //} // } _folders.Clear(); ; _files.Clear(); CheckAndRunWorkers(remotefolders, localfolders, GetFolderPath("/"), GetFolderPath("\\")); } public Dictionary Selected() { return new Dictionary(); } public void Setup() { settings = new UserConfiguration().Load(); SplitPanel.View = settings.ViewType == ScreenViewType.Register ? DynamicSplitPanelView.Master : settings.ViewType == ScreenViewType.Details ? DynamicSplitPanelView.Detail : DynamicSplitPanelView.Combined; SplitPanel.AnchorWidth = settings.AnchorWidth; Folders.ParentPropertyName = "ID"; Folders.ChildPropertyName = "ParentID"; Folders.SelfRelationRootValue = Guid.Empty; Folders.AutoExpandMode = AutoExpandMode.RootNodesExpanded; Folders.ItemsSource = _folders; Files.ItemsSource = _files; } public void Shutdown() { } private void SplitPanel_OnChanged(object sender, DynamicSplitPanelSettings e) { settings.ViewType = SplitPanel.View == DynamicSplitPanelView.Master ? ScreenViewType.Register : SplitPanel.View == DynamicSplitPanelView.Detail ? ScreenViewType.Details : ScreenViewType.Combined; settings.AnchorWidth = SplitPanel.AnchorWidth; new UserConfiguration().Save(settings); } private void Folders_AutoGeneratingColumn(object sender, TreeGridAutoGeneratingColumnEventArgs e) { if (e.Column.MappingName == "DisplayName") e.Column.ColumnSizer = TreeColumnSizer.Star; else if (e.Column.MappingName.Equals("RemoteSize") || e.Column.MappingName.Equals("RemoteTime") || e.Column.MappingName.Equals("LocalSize") || e.Column.MappingName.Equals("LocalTime")) //e.Column.ColumnSizer = TreeColumnSizer.Auto; e.Cancel = true; else e.Cancel = true; } private void Folders_SizeChanged(object sender, SizeChangedEventArgs e) { } private void Folders_SelectionChanged(object sender, GridSelectionChangedEventArgs e) { _files.Clear(); var folder = (e.AddedItems.FirstOrDefault() as TreeGridRowInfo)?.RowData as SVNFolder; if (folder != null) CheckAndRunWorkers(remotefiles, localfiles, folder, folder); } private void Files_AutoGeneratingColumn(object sender, AutoGeneratingColumnArgs e) { e.Column.HeaderStyle = Resources["FileHeaderStyle"] as Style; if (e.Column.MappingName == "Status") { e.Column = new GridTemplateColumn { CellTemplate = Resources["ImageTemplate"] as DataTemplate }; e.Column.Width = 30; } else if (e.Column.MappingName == "DisplayName") { e.Column.ColumnSizer = GridLengthUnitType.Star; e.Column.HeaderText = "File Name"; } else if (e.Column.MappingName.Equals("RemoteSize") || e.Column.MappingName.Equals("RemoteTime") || e.Column.MappingName.Equals("LocalSize") || e.Column.MappingName.Equals("LocalTime")) // e.Column.ColumnSizer = GridLengthUnitType.Auto; { e.Cancel = true; } else { e.Cancel = true; } } #region Background Worker Support Functions private void CheckWaitCursor(object sender, EventArgs args) { if (!remotefolders.IsBusy && !remotefiles.IsBusy && !localfolders.IsBusy && !localfiles.IsBusy) { _waitcursortimer.IsEnabled = false; if (_waitcursor != null) { _waitcursor.Dispose(); _waitcursor = null; } } } private void CheckAndRunWorkers(BackgroundWorker remote, BackgroundWorker local, object remoteparameter, object localparameter) { if (remote.IsBusy) { MessageBox.Show(string.Format("{0} is Busy!", remote.GetType())); return; } if (localfiles.IsBusy) { MessageBox.Show(string.Format("{0} is Busy!", local.GetType())); return; } _waitcursor = new WaitCursor(); remote.RunWorkerAsync(remoteparameter); local.RunWorkerAsync(localparameter); _waitcursortimer.IsEnabled = true; } private string GetFolderPath(string separator) { if (lastjobid != ParentID) { jobnumber = new Client().Query( new Filter(x => x.ID).IsEqualTo(ParentID), new Columns(x => x.JobNumber) ).Rows.Select(r => r.Get(c => c.JobNumber)).FirstOrDefault(); lastjobid = ParentID; } return string.Join(separator, "Jobs", jobnumber); } private static Uri GetRelativeUri(string segment) { var sSeg = string.Format("./{0}/", segment.TrimStart('/').TrimEnd('/')); var uSeg = new Uri(sSeg, UriKind.Relative); return uSeg; } private void HandleProgress(ProgressChangedEventArgs e, ObservableCollection collection, Action action = null) where T : SVNObject { if (e.UserState is Exception) { var err = e.UserState as Exception; var messages = new List(); messages.Add("==============================================="); messages.Add(string.Format("{0} Exception Caught!", err.GetType())); while (err != null) { messages.Add("==============================================="); messages.Add(err.Message); messages.Add(err.StackTrace); err = err.InnerException; } messages.Add("==============================================="); MessageBox.Show(string.Join("\n", messages)); } else if (e.UserState is SyncEntry) { UpdateCollection(e.UserState as SyncEntry, collection, action); } } private void UpdateCollection(SyncEntry entry, ObservableCollection items, Action action) where T : SVNObject { if (entry == null) return; var segments = entry.Path.Segments.Skip(entry.Parent.Segments.Length) .Select(x => x.TrimStart('/').TrimEnd('/').TrimStart('\\').TrimEnd('\\')) .ToArray(); if (!segments.Any()) return; var path = segments.Reverse().Skip(1).Reverse().ToArray(); var name = segments.LastOrDefault(); var existing = items.FirstOrDefault(x => x.Equals(segments)); if (existing == null) { var parent = items.FirstOrDefault(x => x.Equals(path)); existing = Activator.CreateInstance(typeof(T), path, name, parent) as T; items.Add(existing); } if (entry is LocalSyncEntry) { existing.LocalSize = entry.Size; existing.LocalTime = entry.Modified; } else if (entry is RemoteSyncEntry) { existing.RemoteSize = entry.Size; existing.RemoteTime = entry.Modified; } } #endregion #region Local File Processing private void LocalFilesRefresh(object sender, DoWorkEventArgs args) { var folder = args.Argument as SVNFolder; if (folder == null) return; var root = Path.Combine(_localroot, GetFolderPath("\\")); foreach (var segment in folder.Path) root = Path.Combine(root, segment); root = Path.Combine(root, folder.Name); root = Uri.UnescapeDataString(root); if (!Directory.Exists(root)) return; try { var files = Directory.EnumerateFiles(root); foreach (var file in files) { var info = new FileInfo(file); localfiles.ReportProgress(0, new LocalSyncEntry { Parent = new Uri(root), Path = new Uri(file), Modified = info.LastWriteTime, Size = info.Length }); } } catch (Exception e) { localfiles.ReportProgress(100, e); } } private void LocalFoldersRefresh(object sender, DoWorkEventArgs args) { var folderroot = args.Argument as string; if (string.IsNullOrWhiteSpace(folderroot)) return; try { var root = Path.Combine(_localroot, folderroot.TrimStart('\\')); root = Uri.UnescapeDataString(root); var folders = new List { root }; while (folders.Any()) { var folder = folders.First(); folders.Remove(folder); if (Directory.Exists(folder)) { var items = Directory.EnumerateDirectories(folder); foreach (var item in items) { var info = new FileInfo(item); if (!string.IsNullOrEmpty(item) && !string.Equals(item, folder) && !string.Equals(item, root) && !info.Attributes.HasFlag(FileAttributes.Hidden)) { folders.Add(item); localfolders.ReportProgress(0, new LocalSyncEntry { Parent = new Uri(root), Path = new Uri(item), Modified = info.LastWriteTime, Size = 0L }); } } } } } catch (Exception e) { localfolders.ReportProgress(100, e); } } #endregion #region Remote File Processing // private static bool ListRemoteDirectory(SvnClient svnClient, Uri uri, out Collection items, bool create) // { // try // { // return svnClient.GetList(uri, out items); // } // catch // { // if (create) // { // svnClient.RemoteCreateDirectory(uri, new SvnCreateDirectoryArgs() { LogMessage = "Auto Created Folder" }); // return svnClient.GetList(uri, out items); // } // else // { // items = new Collection(); // return false; // } // } // } // private void RemoteFilesRefresh(object sender, DoWorkEventArgs args) // { // // var folder = args.Argument as SVNFolder; // if (folder == null) // return; // // Uri uri = new Uri(_serverroot, GetRelativeUri(GetFolderPath("/"))); // foreach (var segment in folder.Path) // { // Uri uSeg = GetRelativeUri(segment); // uri = new Uri(uri, uSeg); // } // uri = new Uri(uri, GetRelativeUri(folder.Name)); // // try // { // using (SvnClient svnClient = new SvnClient()) // { // svnClient.Authentication.Clear(); // svnClient.Authentication.DefaultCredentials = new System.Net.NetworkCredential("admin", "admin"); // svnClient.Authentication.SslServerTrustHandlers += (o, e) => // { // e.AcceptedFailures = e.Failures; // e.Save = true; // }; // // Collection items; // if (ListRemoteDirectory(svnClient, uri, out items, false)) // { // foreach (SvnListEventArgs item in items) // { // bool bInfo = svnClient.GetInfo(item.Uri, out SvnInfoEventArgs info); // // if ((!String.IsNullOrEmpty(item.Name)) && (item.Entry.NodeKind == SvnNodeKind.File)) // { // //bool bStatus = svnClient.GetStatus(item.Uri.AbsoluteUri, out Collection statuses); // remotefiles.ReportProgress(0, new RemoteSyncEntry() { Parent = uri, Path = item.Uri, Modified = item.Entry.Time, Size = info.RepositorySize }); // } // } // } // } // } // catch (Exception e) // { // remotefiles.ReportProgress(100, e); // } // } // // private void RemoteFoldersRefresh(object sender, DoWorkEventArgs args) // { // // String folderroot = args.Argument as String; // if (String.IsNullOrWhiteSpace(folderroot)) // return; // // try // { // using (SvnClient svnClient = new SvnClient()) // { // svnClient.Authentication.Clear(); // svnClient.Authentication.DefaultCredentials = new System.Net.NetworkCredential("admin", "admin"); // svnClient.Authentication.SslServerTrustHandlers += (o,e) => // { // e.AcceptedFailures = e.Failures; // e.Save = true; // }; // // Uri root = new Uri(_serverroot, GetRelativeUri(folderroot)); // List uris = new List() { root }; // while (uris.Any()) // { // var uri = uris.First(); // uris.Remove(uri); // Collection items; // if (ListRemoteDirectory(svnClient, uri, out items, true)) // { // foreach (SvnListEventArgs item in items) // { // if ((!String.IsNullOrEmpty(item.Name)) && (!Uri.Equals(root, item.Uri)) && (!Uri.Equals(uri, item.Uri)) && (item.Entry.NodeKind == SvnNodeKind.Directory)) // { // uris.Add(item.Uri); // remotefolders.ReportProgress(0, new RemoteSyncEntry() { Parent = root, Path = item.Uri, Modified = item.Entry.Time, Size = item.Entry.FileSize }); // } // } // } // } // } // } // catch (Exception e) // { // remotefolders.ReportProgress(100, e); // } // } #endregion } }