using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Forms; using System.Windows.Forms.VisualStyles; using System.Windows.Input; using System.Windows.Media; using Comal.Classes; using H.Pipes.Extensions; using InABox.Clients; using InABox.Core; using InABox.DynamicGrid; using InABox.WPF; using Microsoft.Office.Interop.Outlook; using Syncfusion.UI.Xaml.Grid; using Syncfusion.UI.Xaml.TreeGrid; using Syncfusion.UI.Xaml.TreeGrid.Helpers; using JobDocumentSetFolder = Comal.Classes.JobDocumentSetFolder; using MessageBox = System.Windows.MessageBox; using UserControl = System.Windows.Controls.UserControl; namespace PRSDesktop { public class DocumentSetNode : INotifyPropertyChanged { private DocumentSetNodes _owner = null; public ObservableCollection Children => _owner.GetChilden(_id); private Guid _id; public Guid ID { get { return _id; } set { _id = value; RaisedOnPropertyChanged("ID"); } } private Guid _parent; public Guid Parent { get { return _parent; } set { _parent = value; RaisedOnPropertyChanged("Parent"); } } private string _description; public string Description { get { return _description; } set { _description = value; RaisedOnPropertyChanged("Description"); } } private string _details; public string Details { get { return _details; } set { _details = value; RaisedOnPropertyChanged("Details"); } } public Dictionary Blocks { get; private set; } public event PropertyChangedEventHandler PropertyChanged; public void RaisedOnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } public DocumentSetNode(DocumentSetNodes owner) { _owner = owner; Blocks = new Dictionary(); foreach (var column in owner.Columns) Blocks[column] = ""; } public DocumentSetNode(DocumentSetNodes owner, Guid id, Guid parent) : this(owner) { _id = id; _parent = parent; } } public class DocumentSetNodes { private List _nodes = null; public ObservableCollection Nodes => new ObservableCollection(_nodes.Where(x=>x.Parent == Guid.Empty)); public IEnumerable Columns { get; private set; } public DocumentSetNodes(IEnumerable columns) { _nodes = new List(); Columns = columns; } public DocumentSetNode Add(Guid id, Guid parent) { var node = new DocumentSetNode(this, id, parent); _nodes.Add(node); return node; } public ObservableCollection GetChilden(Guid id) { return new ObservableCollection(_nodes.Where(x => x.Parent.Equals(id) && (x.ID != id))); } } // public class GridColumnSizerExt : TreeGridColumnSizer // { // public GridColumnSizerExt(SfTreeGrid sfTreeGrid) // : base() // { // // } // public override double SetColumnWidth(TreeGridColumn column, double Width) // { // MethodInfo methodInfo = this.TreeGrid.ColumnResizingController.GetType().GetMethod("IsExpanderColumn", BindingFlags.NonPublic | BindingFlags.Instance); // if ((bool)methodInfo.Invoke(this.TreeGrid.ColumnResizingController, new object[] { column })) // { // var columnIndex = this.TreeGrid.Columns.IndexOf(column); // var scrollColumnIndex = this.TreeGrid.ResolveToScrollColumnIndex(columnIndex); // var treeGridPanel = this.TreeGrid.GetTreePanel(); // //You can hide the expander column by setting width as 0 here. // treeGridPanel.ColumnWidths[scrollColumnIndex] = 100.0; // return 100.0; // } // else // return base.SetColumnWidth(column, Width); // } // } public delegate void JobDocumentSetMileStoneSelected(JobDocumentSetMileStoneBlock block); public partial class JobDocumentSetTree : UserControl { public event JobDocumentSetMileStoneSelected MileStoneSelected; private struct MileStone { public Guid TypeID { get; set; } public CoreRow Row { get; set; } } private struct MileStoneType { public String Code { get; set; } public String Description { get; set; } public Dictionary> SetMileStones { get; set; } public List Columns { get; set; } } public Guid JobID { get; set; } public Guid FolderID { get; set; } private Dictionary _types = null; private CoreTable _milestones = null; public CoreTable Data { get; private set; } = null; private CoreTable _files = null; private bool _hidesuperceded = false; private bool _flatlist = false; private bool _includeretired = false; public JobDocumentSetTree() { InitializeComponent(); AddImage.Source = PRSDesktop.Resources.add.AsBitmapImage(); EditImage.Source = PRSDesktop.Resources.pencil.AsBitmapImage(); DeleteImage.Source = PRSDesktop.Resources.delete.AsBitmapImage(); treeGrid.Loaded += (o, e) => { treeGrid.GetTreePanel().RowHeights[1] = 0; treeGrid.UpdateDataRow(1); }; } public void Refresh() { using (new WaitCursor()) { treeGrid.ItemsSource = null; var setfilter = new Filter(x => x.Job.ID).IsEqualTo(JobID); if(FolderID != CoreUtils.FullGuid) { setfilter = setfilter.And(x => x.Folder.ID).IsEqualTo(FolderID); } if (!_includeretired) setfilter = setfilter.And(x => x.Retired).IsEqualTo(DateTime.MinValue); MultiQuery query = new MultiQuery(); query.Add( setfilter, new Columns(x => x.ID) .Add(x => x.Parent.ID) .Add(x => x.Code) .Add(x => x.Description) .Add(x => x.Date) .Add(x => x.Size) .Add(x => x.Scale) .Add(x => x.Employee.Name), new SortOrder(x => x.Code) ); var milestonefilter = new Filter(x => x.DocumentSet.Job.ID).IsEqualTo(JobID); if (FolderID != Guid.Empty && FolderID != CoreUtils.FullGuid) milestonefilter = milestonefilter.And(x => x.DocumentSet.Folder.ID).IsEqualTo(FolderID); query.Add( milestonefilter, new Columns(x => x.ID) .Add(x => x.DocumentSet.ID) .Add(x => x.Type.ID) .Add(x => x.Type.Code) .Add(x => x.Status) .Add(x => x.Notes) .Add(x => x.Revision) .Add(x => x.Due) .Add(x => x.Submitted) .Add(x => x.Closed) .Add(x => x.Attachments) .Add(x => x.Watermark) ); if (_types == null) { query.Add( null, new Columns(x => x.ID) .Add(x => x.Code) .Add(x => x.Description), new SortOrder(x => x.Sequence) ); } query.Query(); Data = query.Get(); _milestones = query.Get(); if (_types == null) { _types = query.Get().ToDictionary( x => x.ID, r => new MileStoneType() { Code = r.Get(c => c.Code), Description = r.Get(c => c.Description), SetMileStones = new Dictionary>(), Columns = new List() } ); } else { foreach (var typeid in _types.Keys) { _types[typeid].Columns.Clear(); _types[typeid].SetMileStones.Clear(); } } var milestones = _milestones.ToLookup( x => x.DocumentSet.ID, r => new MileStone() { TypeID = r.Get(c => c.Type.ID), Row = r } ); foreach (var milestone in milestones) { foreach (var entry in milestone) { if (!_types[entry.TypeID].SetMileStones.ContainsKey(milestone.Key)) _types[entry.TypeID].SetMileStones[milestone.Key] = new List(); if (_hidesuperceded) _types[entry.TypeID].SetMileStones[milestone.Key].Clear(); _types[entry.TypeID].SetMileStones[milestone.Key].Add(entry.Row); } } List columns = new List(); foreach (var typeid in _types.Keys) { int count = 1; foreach (var setkey in _types[typeid].SetMileStones.Keys) count = Math.Max(count, _types[typeid].SetMileStones[setkey].Count); for (int i = 1; i <= count; i++) { String column = String.Format("{0}_{1}", _types[typeid].Code, i); columns.Add(column); _types[typeid].Columns.Add(String.Format("Blocks[{0}]", column)); } } var documentsets = new DocumentSetNodes(columns); foreach (var setrow in Data.Rows) { Guid setid = setrow.Get(x => x.ID); Guid parentid = _flatlist ? Guid.Empty : setrow.Get(x => x.Parent.ID); var node = documentsets.Add(setid, parentid); JobDocumentSetDescriptionBlock desc = new JobDocumentSetDescriptionBlock() { ID = setid, Code = setrow.Get(c => c.Code), Description = setrow.Get(c => c.Description), }; node.Description = Serialization.Serialize(desc); JobDocumentSetDetailsBlock dets = new JobDocumentSetDetailsBlock() { ID = setid, Date = setrow.Get(c => c.Date), Size = setrow.Get(c => c.Size), Scale = setrow.Get(c => c.Scale), Employee = setrow.Get(c => c.Employee.Name) }; node.Details = Serialization.Serialize(dets); foreach (var typeid in _types.Keys) { if (_types[typeid].SetMileStones.TryGetValue(setid, out var rows)) { int i = 1; foreach (var row in rows) { JobDocumentSetMileStoneBlock block = new JobDocumentSetMileStoneBlock(); block.ID = row.Get(c => c.ID); block.Revision = row.Get(c => c.Revision); block.Status = row.Get(c => c.Status); block.Date = (block.Status == JobDocumentSetMileStoneStatus.Approved) || (block.Status == JobDocumentSetMileStoneStatus.Cancelled) || (block.Status == JobDocumentSetMileStoneStatus.Rejected) ? row.Get(c => c.Closed) : block.Status == JobDocumentSetMileStoneStatus.Submitted ? block.Date = row.Get(c => c.Submitted) : row.Get(c => c.Due); String[] notes = row.Get(c => c.Notes); block.Notes = notes != null ? String.Join("\n", notes) : ""; block.Attachments = row.Get(c => c.Attachments); block.Watermark = row.Get(c => c.Watermark); node.Blocks[String.Format("{0}_{1}", _types[typeid].Code, i)] = Serialization.Serialize(block); i++; } } } } ConfigureColumns(documentsets); ConfigureStackedHeader(); treeGrid.ItemsSource = documentsets.Nodes; } } #region Grid Configuration private void ConfigureColumns(DocumentSetNodes documentsets) { treeGrid.Columns.Clear(); treeGrid.Columns.Add(new TreeGridTemplateColumn() { CellTemplate = FindResource("descriptionTemplate") as DataTemplate, MappingName = "Description", SetCellBoundValue = true, MinimumWidth = 250, ColumnSizer = TreeColumnSizer.Star }); treeGrid.Columns.Add(new TreeGridTemplateColumn() { CellTemplate = FindResource("detailsTemplate") as DataTemplate, MappingName = "Details", SetCellBoundValue = true, Width = 120 }); foreach (var column in documentsets.Columns) { var col = new TreeGridTemplateColumn() { CellTemplate = FindResource("milestoneTemplate") as DataTemplate, MappingName = String.Format("Blocks[{0}]",column), SetCellBoundValue = true, HeaderText = " ", Width = 80, ShowToolTip = true }; treeGrid.Columns.Add(col); } } private void ConfigureStackedHeader() { stackedHeaderRow.StackedColumns.Clear(); stackedHeaderRow.StackedColumns.Add(new StackedColumn() { ChildColumns = "Description,Details", HeaderText = "Document Register" }); foreach (var typeid in _types.Keys) { stackedHeaderRow.StackedColumns.Add(new StackedColumn() { ChildColumns = String.Join(",", _types[typeid].Columns), HeaderText = _types[typeid].Code }); } } private void TreeGrid_OnItemsSourceChanged(object? sender, TreeGridItemsSourceChangedEventArgs e) { var panel = treeGrid.GetTreePanel(); panel.RowHeights[1] = 0; } private void TreeGrid_OnNodeCollapsing(object? sender, NodeCollapsingEventArgs e) { e.Cancel = true; } public MenuItem CreateCalendar(ContextMenu menu, string text, DateTime startDate, CoreRow[] milestones, Action? action) { var item = new MenuItem(); var calendarItem = new MenuItem(); var calendar = new Calendar { DisplayDate = startDate, SelectedDate = null}; calendar.SelectedDatesChanged += (o, e) => { action?.Invoke(milestones, calendar.SelectedDate); menu.IsOpen = false; }; calendarItem.Header = calendar; calendarItem.Style = DynamicGridUtils.Resources["NonHighlightMenuItem"] as Style; item.Header = text; item.Items.Add(calendarItem); item.IsCheckable = false; return item; } private void TreeGrid_OnContextMenuOpening(object sender, ContextMenuEventArgs e) { MileStoneMenu.Items.Clear(); var tag = (e.OriginalSource as FrameworkElement).Tag; Point pos = Mouse.GetPosition(treeGrid); var treeGridPanel = this.treeGrid.GetTreePanel(); // get the row and column index based on the pointer position var rowColumnIndex = treeGridPanel.PointToCellRowColumnIndex(pos); if (rowColumnIndex.IsEmpty) return; var treeNodeAtRowIndex = treeGrid.GetNodeAtRowIndex(rowColumnIndex.RowIndex); if (rowColumnIndex.ColumnIndex < 2) { var document = treeGrid.SelectedItem as DocumentSetNode; MenuItem addchild = new MenuItem(); addchild.Header = "Add Child"; addchild.Click += (o,args) => { AddChildDocument(document); }; MileStoneMenu.Items.Add(addchild); var documents = treeGrid.SelectedItems.Select(x => (x as DocumentSetNode)); MenuItem movetofolder = new MenuItem(); movetofolder.Header = "Move To Folder"; bool hasfolders = PopulateFolders(movetofolder, documents); if (hasfolders) { MileStoneMenu.Items.Add(new Separator()); MileStoneMenu.Items.Add(movetofolder); } return; } var mappingname = treeGrid.Columns[rowColumnIndex.ColumnIndex].MappingName; var blockkey = mappingname.Replace("Blocks[", "").Replace("]", ""); var typeid = _types.FirstOrDefault(x => x.Value.Columns.Contains(mappingname)).Key; //Guid setid = (treeGrid.SelectedItem as DocumentSetNode).ID; Guid[] setids = treeGrid.SelectedItems.Select(x => (x as DocumentSetNode).ID).ToArray(); //Guid.TryParse(tag.ToString(), out Guid milestoneid); var blocks = treeGrid.SelectedItems.Select(x => (x as DocumentSetNode).Blocks[blockkey]).Where(x => !String.IsNullOrWhiteSpace(x)) .ToArray(); var milestoneids = blocks.Select(x => Serialization.Deserialize(x).ID).ToArray(); //var milestone = _milestones.Rows.FirstOrDefault(r => r.Get(c => c.ID) == milestoneid); var milestones = _milestones.Rows.Where(r => milestoneids.Contains(r.Get(c => c.ID))).ToArray(); bool canCreateNewMileStones = true; foreach (var setid in setids) { var openmilestones = _milestones.Rows.Any(r => Guid.Equals(r.Get(c => c.DocumentSet.ID), setid) && Guid.Equals(r.Get(c => c.Type.ID), typeid) && (r.Get(c => c.Closed).IsEmpty() || (r.Get(c => c.Status) == JobDocumentSetMileStoneStatus.Approved)) ); if (openmilestones) canCreateNewMileStones = false; } if (canCreateNewMileStones) { MenuItem newmilestone = new MenuItem() { Header = "New Milestone", Tag = typeid }; newmilestone.Click += (o, args) => { CreateMileStone(setids, typeid, DateTime.Today); }; MileStoneMenu.Items.Add(newmilestone); } if (milestones.Any()) { MenuItem setstatus = new MenuItem() { Header = "Change Status" }; foreach (JobDocumentSetMileStoneStatus newstatus in Enum.GetValues(typeof(JobDocumentSetMileStoneStatus))) { MenuItem setstatus2 = null; switch (newstatus) { case JobDocumentSetMileStoneStatus.Unknown: break; case JobDocumentSetMileStoneStatus.NotStarted: case JobDocumentSetMileStoneStatus.InProgress: case JobDocumentSetMileStoneStatus.OnHold: case JobDocumentSetMileStoneStatus.InfoRequired: setstatus2 = new MenuItem() { Header = newstatus.ToString().SplitCamelCase() }; setstatus2.Click += (o, args) => { ChangeMileStoneStatus(milestones, newstatus, DateTime.MinValue, DateTime.MinValue); }; break; case JobDocumentSetMileStoneStatus.Submitted: setstatus2 = CreateCalendar( MileStoneMenu, newstatus.ToString().SplitCamelCase(), DateTime.Today, milestones, (r, t) => { ChangeMileStoneStatus(milestones, newstatus, t, DateTime.MinValue); } ); break; case JobDocumentSetMileStoneStatus.Approved: case JobDocumentSetMileStoneStatus.Cancelled: case JobDocumentSetMileStoneStatus.Rejected: setstatus2 = CreateCalendar( MileStoneMenu, newstatus.ToString().SplitCamelCase(), DateTime.Today, milestones, (r, t) => { ChangeMileStoneStatus(milestones, newstatus, null, t); } ); break; } if (setstatus2 != null) setstatus.Items.Add(setstatus2); } MileStoneMenu.Items.Add(setstatus); //var closed = milestones.Any(r => !r.Get(c => c.Closed).IsEmpty()); if ((setids.Length == 1) && (milestones.Length == 1)) // && !closed) { MenuItem editmilestone = new MenuItem() { Header = "Edit MileStone" }; editmilestone.Click += (o, args) => { EditMileStone(milestones[0]); }; MileStoneMenu.Items.Add(editmilestone); var attachments = milestones[0].Get(x => x.Attachments); if (attachments > 1) { MenuItem splitmilestone = new MenuItem() { Header = "Split MileStone" }; splitmilestone.Click += (o, args) => { SplitMileStone(setids[0], milestones[0]); }; MileStoneMenu.Items.Add(splitmilestone); } MileStoneMenu.Items.Add(new Separator()); MenuItem upload = new MenuItem() { Header = "Upload Files" }; upload.Click += (o, args) => { UploadFiles(milestones[0]); }; MileStoneMenu.Items.Add(upload); } if (milestones.Any()) { MenuItem download = new MenuItem() { Header = "Download Files" }; download.Items.Add(new MenuItem()); download.SubmenuOpened += (o, e) => { download.Items.Clear(); var files = new Client().Query( new Filter(x => x.EntityLink.ID).InList(milestoneids), new Columns(x => x.ID) .Add(x => x.DocumentLink.FileName) .Add(x => x.DocumentLink.ID), new SortOrder(x => x.DocumentLink.FileName) ); if (files.Rows.Any()) { foreach (var row in files.Rows) { MenuItem downloadone = new MenuItem() { Header = row.Get(x => x.DocumentLink.FileName), }; downloadone.Click += (sender, args) => { DownloadFiles( new CoreRow[] { milestones[0] }, row.Get(x => x.DocumentLink.ID) ); }; download.Items.Add(downloadone); } if (download.Items.Count > 1) { download.Items.Add(new Separator()); MenuItem downloadall = new MenuItem() { Header = "Download All", }; downloadall.Click += (sender, args) => { DownloadFiles( milestones, Guid.Empty ); }; download.Items.Add(downloadall); } } else { download.Items.Add( new MenuItem() { Header = "No Files to download", IsEnabled = false } ); } }; MileStoneMenu.Items.Add(download); } // if ((milestoneids.Length == 1)) // && !closed) // { // MenuItem managefiles = new MenuItem() // { // Header = "Manage Files" // }; // managefiles.Click += (sender, args) => { ManageFiles(milestones[0]); }; // MileStoneMenu.Items.Add(managefiles); // } MileStoneMenu.Items.Add(new Separator()); MenuItem delete = new MenuItem { Header = "Delete MileStone" }; delete.Click += (o, args) => DeleteMileStone(milestones); MileStoneMenu.Items.Add(delete); } if (MileStoneMenu.Items.Count == 0) e.Handled = true; } private bool PopulateFolders(MenuItem menu, IEnumerable documents) { CoreTable data = new Client().Query( new Filter(x => x.Job.ID).IsEqualTo(JobID), new Columns(x => x.ID) .Add(x => x.Parent.ID) .Add(x => x.Name) ); if (!data.Rows.Any()) return false; DynamicTreeNodes folders = new DynamicTreeNodes(); folders.Load(data, x => x.ID, x => x.Parent.ID, x => x.Name); foreach (var folder in folders.Nodes) DoPopulateFolder(menu, folder, documents); return true; } private void DoPopulateFolder(MenuItem header, DynamicTreeNode folder, IEnumerable documents) { MenuItem menu = new MenuItem(); menu.Header = folder.Description; menu.Click += (sender, args) => MoveToFolder(documents, folder); header.Items.Add(menu); foreach (var childfolder in folder.Children) DoPopulateFolder(menu, childfolder, documents); } private void MoveToFolder(IEnumerable documents, DynamicTreeNode folder) { if (FolderID != folder.ID) { using (new WaitCursor()) { List updates = new List(); foreach (var document in documents) { var update = new JobDocumentSet(); update.ID = document.ID; update.Folder.ID = folder.ID; update.Parent.ID = Guid.Empty; updates.Add(update); } new Client().Save(updates, "Moved to Folder: " + folder.Description); } Refresh(); } else { MessageBox.Show("Target Folder is the same as Source Folder!"); } } private void SplitMileStone(Guid setid, CoreRow milestone) { if (MessageBox.Show( "Are you sure you wish to split this Document Set?", "Confirm Delete", MessageBoxButton.YesNo ) != MessageBoxResult.Yes) return; Guid milestoneid = milestone.Get(c => c.ID); var dlg = new MultiSelectDialog( new Filter(c => c.EntityLink.ID).IsEqualTo(milestoneid), null, true ); if (dlg.ShowDialog() == true) { var files = dlg.Items(); Progress.ShowModal("Splitting Document Set", (progress) => { JobDocumentSet newset = new Client().Query( new Filter(x => x.ID).IsEqualTo(setid) ).Rows.FirstOrDefault()?.ToObject(); if (newset != null) { newset.ID = Guid.Empty; newset.CommitChanges(); newset.Parent.ID = setid; newset.Code = String.Format("{0} (COPY)", newset.Code); //newset.Description = "New Child"; new Client().Save(newset, "Created by Splitting MileStone"); progress.Report("Creating Milestone"); JobDocumentSetMileStone newms = new Client().Query( new Filter(c=>c.ID).IsEqualTo(milestoneid) ).Rows.FirstOrDefault()?.ToObject(); if (newms != null) { newms.ID = Guid.Empty; newset.CommitChanges(); newms.DocumentSet.ID = newset.ID; new Client().Save(newms, "Created By Splitting MileStone"); progress.Report("Moving Files"); foreach (var file in files) file.EntityLink.ID = newms.ID; new Client().Save(files, "Moved when Splitting MileStone"); } } }); Refresh(); } } private void AddChildDocument(DocumentSetNode node) { JobDocumentSet newset = new JobDocumentSet(); newset.Parent.ID = node.ID; newset.Job.ID = JobID; newset.Folder.ID = FolderID; var grid = new DynamicDataGrid(); if (grid.EditItems(new[] { newset })) Refresh(); } // private void ManageFiles(CoreRow milestone) // { // var grid = new JobDocumentSetMileStoneFileGrid(); // grid.OnGetWaterMark += (row) => milestone.Get(c => c.Watermark); // grid.ShowSupercededColumn = false; // Window window = new Window(); // window.Padding = new Thickness(5); // window.Content = grid; // window.Width = 300; // window.Height = 500; // window.WindowStartupLocation = WindowStartupLocation.CenterScreen; // grid.Load(milestone.ToObject(), null); // grid.Margin = new Thickness(5); // grid.Refresh(true, true); // window.ShowDialog(); // Refresh(); // } private void DownloadFiles(CoreRow[] rows, Guid id) { FolderBrowserDialog dlg = new FolderBrowserDialog(); if (dlg.ShowDialog() == DialogResult.OK) { Progress.ShowModal("Downloading Files", (progress) => { foreach (var row in rows) { var status = row.Get(c => c.Status); var stage = row.Get(c => c.Type.Code); var revision = row.Get(c => c.Revision); String tag = String.Format(" - {0}{1} ({2})", stage, String.IsNullOrWhiteSpace(revision) ? "" : " - Rev " + revision, status.ToString().SplitCamelCase()); var filter = id == Guid.Empty ? new Filter(x => x.ID).InQuery( new Filter(x => x.EntityLink.ID).IsEqualTo( row.Get(c => c.ID)), x => x.DocumentLink.ID ) : new Filter(x => x.ID).IsEqualTo(id); var files = new Client().Query(filter); foreach (var filerow in files.Rows) { string filename = filerow.Get(c => c.FileName); string extension = Path.GetExtension(filename); string basefilename = Path.GetFileNameWithoutExtension(filename); filename = String.Format("{0}{1}{2}", basefilename, tag, extension); filename = Path.Combine(dlg.SelectedPath, filename); File.WriteAllBytes(filename, filerow.Get(c => c.Data)); } } }); Process.Start(new ProcessStartInfo(dlg.SelectedPath) { UseShellExecute = true }); } } private bool SelectFiles(out String[] files) { Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog(); dlg.Filter = "PDF Files (*.pdf)|*.pdf"; dlg.Multiselect = true; if ((dlg.ShowDialog() == true) && (dlg.FileNames.Length > 0)) { files = dlg.FileNames.ToArray(); return true; } files = null; return false; } private void UploadFiles(CoreRow row) { Guid id = row.Get(c => c.ID); if (SelectFiles(out String[] filenames)) { Progress.ShowModal("Uploading Files", (progress) => { List documents = new List(); foreach (var file in filenames) { var data = File.ReadAllBytes(file); documents.Add( new Document() { FileName = Path.GetFileName(file).ToLower(), Data = data, CRC = CoreUtils.CalculateCRC(data), TimeStamp = new FileInfo(file).LastWriteTime } ); } new Client().Save(documents.ToArray(), "Uploaded by User"); progress.Report("Updating Links"); List links = new List(); foreach (var document in documents) { var link = new JobDocumentSetMileStoneFile(); link.EntityLink.ID = id; link.DocumentLink.ID = document.ID; links.Add(link); } new Client().Save(links, "Uploaded By User"); }); MessageBox.Show(String.Format("{0} files uploaded", filenames.Length)); Refresh(); } } private void CreateMileStone(Guid[] setids, Guid typeid, DateTime duedate) { List updates = new List(); foreach (var setid in setids) { JobDocumentSetMileStone milestone = new JobDocumentSetMileStone(); milestone.DocumentSet.ID = setid; milestone.Type.ID = typeid; milestone.Status = JobDocumentSetMileStoneStatus.NotStarted; milestone.Due = duedate; updates.Add(milestone); } var grid = new JobDocumentSetMileStoneGrid(); if (grid.EditItems(updates.ToArray())) Refresh(); } private void ChangeMileStoneStatus(CoreRow[] rows, JobDocumentSetMileStoneStatus newstatus, DateTime? issued, DateTime? closed) { var milestones = rows.Select(r=>r.ToObject()).ToArray(); foreach (var milestone in milestones) { if (issued.HasValue) milestone.Submitted = issued.Value; if (closed.HasValue) milestone.Closed = closed.Value; milestone.Status = newstatus; } using (new WaitCursor()) new Client().Save(milestones, "Changed Status to " + newstatus.ToString().SplitCamelCase()); Refresh(); } private void EditMileStone(CoreRow row) { var milestone = new Client().Query( new Filter(x => x.ID).IsEqualTo(row.Get(x => x.ID)) ).Rows.FirstOrDefault()?.ToObject(); var grid = new JobDocumentSetMileStoneGrid(); if (grid.EditItems(new[] { milestone })) Refresh(); } private void DeleteMileStone(CoreRow[] rows) { var milestones = rows.Select(r=>r.ToObject()).ToArray(); using (new WaitCursor()) new Client().Delete(milestones,"Deleted by User"); Refresh(); } private void TreeGrid_OnCellToolTipOpening(object? sender, TreeGridCellToolTipOpeningEventArgs e) { var column = e.Column.MappingName.Replace("Blocks[","").Replace("]",""); var data = (e.Record as DocumentSetNode).Blocks[column]; if (String.IsNullOrWhiteSpace(data)) return; var block = Serialization.Deserialize(data.ToString()); Guid id = block.ID; TextBlock text = new TextBlock(); if (!String.IsNullOrWhiteSpace(block.Notes)) { text.Inlines.Add(new Run("Milestone Notes\n") { FontWeight = FontWeights.Bold, TextDecorations = TextDecorations.Underline }); text.Inlines.Add(new Run(block.Notes.Replace("=","").Replace("\n\n","\n")) { FontStyle = FontStyles.Italic }); } if (block.Attachments > 0) { if (!String.IsNullOrWhiteSpace(block.Notes)) text.Inlines.Add(new Run("\n\n")); text.Inlines.Add(new Run("Uploaded Files") { FontWeight = FontWeights.Bold, TextDecorations = TextDecorations.Underline }); var files = new Client().Query( new Filter(x => x.EntityLink.ID).IsEqualTo(block.ID), new Columns(x => x.DocumentLink.FileName), new SortOrder(x => x.DocumentLink.FileName) ); foreach (var row in files.Rows) text.Inlines.Add(new Run("\n"+row.Get(c=>c.DocumentLink.FileName)) { FontStyle = FontStyles.Italic }); } if (!text.Inlines.Any()) { e.ToolTip.Template = null; return; } e.ToolTip.Template = TemplateGenerator.CreateControlTemplate( typeof(System.Windows.Controls.ToolTip), () => { var border = new Border { BorderBrush = new SolidColorBrush(Colors.Gray), BorderThickness = new Thickness(0.75), CornerRadius = new CornerRadius(5), Background = new SolidColorBrush(Colors.LightYellow), Padding = new Thickness(5), Child = text }; return border; } ); } #endregion #region Button Bar Actions private void AddTypes(MenuItem parent, Action addfunction) { if(_types.Count == 0) { MenuItem item = new MenuItem() { Header = "No Document Milestones", IsEnabled = false }; parent.Items.Add(item); } else { foreach (var type in _types.Keys) { MenuItem item = new MenuItem() { Header = _types[type].Description, Tag = type }; item.Click += (o, e) => addfunction(type); parent.Items.Add(item); } } } private void Add_OnClick(object sender, RoutedEventArgs e) { if (FolderID == Guid.Empty) { MessageBox.Show("Please choose a Folder first!"); return; } ContextMenu menu = new ContextMenu(); var onetoone = new MenuItem() { Header = "Add Individual Files" }; AddTypes(onetoone, AddOneToOneFiles); menu.Items.Add(onetoone); var manytoone = new MenuItem() { Header = "Add Sets of Files" }; AddTypes(manytoone, AddManyToOneFiles); menu.Items.Add(manytoone); menu.Items.Add(new Separator()); var manual = new MenuItem() { Header = "Add Document Set Manually" }; manual.Click += (o, e) => { AddDocumentSet(); }; menu.Items.Add(manual); menu.IsOpen = true; } private void AddOneToOneFiles(Guid type) { if (!SelectFiles(out String[] filenames)) return; Progress.ShowModal("Preparing Upload", (progress) => { Dictionary> map = new Dictionary>(); foreach (var filename in filenames) { var data = File.ReadAllBytes(filename); Document document = new Document() { FileName = Path.GetFileName(filename).ToLower(), Data = data, CRC = CoreUtils.CalculateCRC(data), TimeStamp = new FileInfo(filename).LastWriteTime }; JobDocumentSet set = new JobDocumentSet(); set.Job.ID = JobID; set.Folder.ID = FolderID; set.Code = Path.GetFileNameWithoutExtension(filename).ToUpper(); set.Description = Path.GetFileNameWithoutExtension(filename).ToUpper(); JobDocumentSetMileStone milestone = new JobDocumentSetMileStone(); milestone.Type.ID = type; milestone.Status = JobDocumentSetMileStoneStatus.InProgress; milestone.Due = DateTime.Today; JobDocumentSetMileStoneFile file = new JobDocumentSetMileStoneFile(); map[filename] = new Tuple( document, set, milestone, file ); } progress.Report("Uploading Files"); var docs = map.Select(x => x.Value.Item1); new Client().Save(docs, "Uploaded By File Selection"); progress.Report("Creating Document Sets"); var sets = map.Select(x => x.Value.Item2); new Client().Save(sets, "Uploaded by File Selection"); progress.Report("Creating MileStones"); foreach (var key in map.Keys) map[key].Item3.DocumentSet.ID = map[key].Item2.ID; var milestones = map.Select(x => x.Value.Item3); new Client().Save(milestones, "Uploaded by File Selection"); progress.Report("Linking Documents"); foreach (var key in map.Keys) { map[key].Item4.EntityLink.ID = map[key].Item3.ID; map[key].Item4.DocumentLink.ID = map[key].Item1.ID; } var files = map.Select(x => x.Value.Item4); new Client().Save(files, "Uploaded by File Selection"); }); MessageBox.Show(String.Format("{0} Document Sets Created", filenames.Length)); Refresh(); } private void AddManyToOneFiles(Guid type) { if (!SelectFiles(out String[] filenames)) return; JobDocumentSet set = new JobDocumentSet(); set.Job.ID = JobID; set.Folder.ID = FolderID; var grid = new DynamicDataGrid(); grid.OnAfterSave += (form, items) => { Progress.ShowModal("Creating MileStone", (progress) => { JobDocumentSetMileStone milestone = new JobDocumentSetMileStone(); milestone.DocumentSet.ID = set.ID; milestone.Type.ID = type; milestone.Status = JobDocumentSetMileStoneStatus.InProgress; milestone.Due = DateTime.Today; new Client().Save(milestone, "Uploaded By File Selection"); progress.Report("Uploading Files"); List documents = new List(); foreach (var filename in filenames) { var data = File.ReadAllBytes(filename); Document document = new Document() { FileName = Path.GetFileName(filename).ToLower(), Data = data, CRC = CoreUtils.CalculateCRC(data), TimeStamp = new FileInfo(filename).LastWriteTime }; documents.Add(document); new Client().Save(documents, "Uploaded by File Selection"); } progress.Report("Creating File Links"); List files = new List(); foreach (var document in documents) { JobDocumentSetMileStoneFile file = new JobDocumentSetMileStoneFile(); file.EntityLink.ID = milestone.ID; file.DocumentLink.ID = document.ID; files.Add(file); } new Client().Save(files, "Uploaded by File Selection"); }); }; if (grid.EditItems(new[] { set })) { MessageBox.Show(String.Format("{0} files uploaded", filenames.Length)); Refresh(); } } private void AddDocumentSet() { JobDocumentSet set = new JobDocumentSet(); set.Job.ID = JobID; set.Folder.ID = FolderID; var grid = new DynamicDataGrid(); if (grid.EditItems(new[] { set })) Refresh(); } private void Edit_OnClick(object sender, RoutedEventArgs e) { if (treeGrid.SelectedItem == null) { MessageBox.Show("Please choose a Document Set first"); return; } Guid[] setIDs = treeGrid.SelectedItems.Select(x => (x as DocumentSetNode).ID).ToArray(); var sets = new Client().Query( new Filter(x => x.ID).InList(setIDs) ).Rows.Select(x => x.ToObject()).ToArray(); var grid = new DynamicDataGrid(); if (grid.EditItems(sets)) Refresh(); } private void HideRejected_OnClick(object sender, RoutedEventArgs e) { _hidesuperceded = !_hidesuperceded; HideSupercededLabel.Content = _hidesuperceded ? "Show All" : "Last Only"; Refresh(); } private void Delete_OnClick(object sender, RoutedEventArgs e) { if ((treeGrid.SelectedItems == null) || !treeGrid.SelectedItems.Any()) { MessageBox.Show("Please choose a Document Set first"); return; } if (MessageBox.Show( "Are you sure you wish to delete the selected Document Sets?", "Confirm Delete", MessageBoxButton.YesNo ) != MessageBoxResult.Yes) return; List updates = new List(); List orphans = new List(); var items = treeGrid.SelectedItems.Select(x => (DocumentSetNode)x).ToArray(); foreach (DocumentSetNode item in items) { var children = item.Children.Where(x => !items.Contains(x)); if (children.Any()) orphans.AddRange(children); } if (orphans.Any()) { var confirm = MessageBox.Show( "These Document Sets contain children!\nDo you wish to delete these as well?", "Delete Children", MessageBoxButton.YesNoCancel ); if (confirm == MessageBoxResult.Cancel) return; if (confirm == MessageBoxResult.No) { foreach (var orphan in orphans) { var update = new JobDocumentSet(); update.ID = orphan.ID; update.Parent.ID = Guid.Empty; updates.Add(update); } return; } } Progress.ShowModal("Deleting Document Set",(progress) => { if (updates.Any()) new Client().Save(updates, "Parent Document Deleted"); var deletes = items.Select(x=>new JobDocumentSet() { ID = x.ID }).ToArray(); new Client().Delete(deletes, "Deleted By User"); }); Refresh(); } #endregion private void FlatList_OnClick(object sender, RoutedEventArgs e) { _flatlist = !_flatlist; FlatListLabel.Content = _flatlist ? "Tree View" : "Flat List"; Refresh(); } private void IncludeRetired_OnClick(object sender, RoutedEventArgs e) { _includeretired = !_includeretired; FlatListLabel.Content = _includeretired ? "Active Only" : "Include Retired"; Refresh(); } private void TreeGrid_OnSelectionChanged(object? sender, GridSelectionChangedEventArgs e) { //var treeColumn = treeGrid.Columns[e.CurrentRowColumnIndex.ColumnIndex]; //var column = treeColumn.MappingName.Replace("Blocks[","").Replace("]",""); // var column = e.Column.MappingName.Replace("Blocks[","").Replace("]",""); // var data = (e.Record as DocumentSetNode).Blocks[column]; // if (String.IsNullOrWhiteSpace(data)) // return; // // var block = Serialization.Deserialize(data.ToString()); // Guid id = block.ID; } private void TreeGrid_OnCurrentCellActivated(object? sender, CurrentCellActivatedEventArgs e) { var node = treeGrid.CurrentItem as DocumentSetNode; if (node == null) return; var treeColumn = treeGrid.Columns[e.CurrentRowColumnIndex.ColumnIndex]; var column = treeColumn.MappingName.Replace("Blocks[","").Replace("]",""); if (!node.Blocks.ContainsKey(column)) MileStoneSelected(null); else { var block = Serialization.Deserialize(node.Blocks[column]); MileStoneSelected?.Invoke(block); } } } }