JobDocumentSetTree.xaml.cs 69 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.ObjectModel;
  4. using System.ComponentModel;
  5. using System.Diagnostics;
  6. using System.IO;
  7. using System.Linq;
  8. using System.Windows;
  9. using System.Windows.Controls;
  10. using System.Windows.Documents;
  11. using System.Windows.Forms;
  12. using System.Windows.Input;
  13. using System.Windows.Media;
  14. using Comal.Classes;
  15. using InABox.Clients;
  16. using InABox.Configuration;
  17. using InABox.Core;
  18. using InABox.DynamicGrid;
  19. using InABox.WPF;
  20. using net.sf.mpxj.phoenix.schema.phoenix5;
  21. using Syncfusion.Data.Extensions;
  22. using Syncfusion.UI.Xaml.Grid;
  23. using Syncfusion.UI.Xaml.TreeGrid;
  24. using Syncfusion.UI.Xaml.TreeGrid.Helpers;
  25. using JobDocumentSetFolder = Comal.Classes.JobDocumentSetFolder;
  26. using MessageBox = System.Windows.MessageBox;
  27. using UserControl = System.Windows.Controls.UserControl;
  28. namespace PRSDesktop
  29. {
  30. public class JobDocumentSetTreeSettings : UserConfigurationSettings
  31. {
  32. public bool DetailsVisible { get; set; }
  33. public JobDocumentSetTreeSettings()
  34. {
  35. DetailsVisible = true;
  36. }
  37. }
  38. public delegate void JobDocumentSetMileStoneSelected(JobDocumentSetMileStoneBlock block);
  39. public partial class JobDocumentSetTree : UserControl
  40. {
  41. public event JobDocumentSetMileStoneSelected MileStoneSelected;
  42. private struct MileStone
  43. {
  44. public Guid TypeID { get; set; }
  45. public CoreRow Row { get; set; }
  46. }
  47. private struct MileStoneType
  48. {
  49. public String Code { get; set; }
  50. public String Description { get; set; }
  51. public Dictionary<Guid,List<CoreRow>> SetMileStones { get; set; }
  52. public List<String> Columns { get; set; }
  53. }
  54. public Guid JobID { get; set; }
  55. public Guid[] FolderIDs{ get; set; }
  56. //public bool DisciplineVisible { get; set; }
  57. public Guid DisciplineID { get; set; }
  58. //public bool TypeVisible { get; set; }
  59. public Guid TypeID { get; set; }
  60. //public bool CategoryVisible { get; set; }
  61. public Guid CategoryID { get; set; }
  62. //public bool AreaVisible { get; set; }
  63. public Guid AreaID { get; set; }
  64. public String SearchText { get; set; }
  65. private Dictionary<Guid,MileStoneType> _types = null;
  66. private CoreTable _milestones = null;
  67. public CoreTable Data { get; private set; } = null;
  68. private CoreTable _files = null;
  69. private bool _hidesuperceded = false;
  70. private bool _flatlist = false;
  71. private bool _includeretired = false;
  72. private DocumentSetNodes _documentsets = null;
  73. private JobDocumentSetTreeSettings _settings;
  74. public JobDocumentSetTree()
  75. {
  76. InitializeComponent();
  77. AddImage.Source = PRSDesktop.Resources.add.AsBitmapImage();
  78. EditImage.Source = PRSDesktop.Resources.pencil.AsBitmapImage();
  79. DeleteImage.Source = PRSDesktop.Resources.delete.AsBitmapImage();
  80. _settings = new UserConfiguration<JobDocumentSetTreeSettings>().Load();
  81. treeGrid.Loaded += (o, e) =>
  82. {
  83. treeGrid.GetTreePanel().RowHeights[1] = 0;
  84. treeGrid.UpdateDataRow(1);
  85. };
  86. }
  87. public void Refresh()
  88. {
  89. using (new WaitCursor())
  90. {
  91. var scrollviewer = WPFUtils.FindVisualChildren<ScrollViewer>(treeGrid).FirstOrDefault();
  92. var verticalOffset = scrollviewer != null ? scrollviewer.VerticalOffset : 0;
  93. var horizontalOffset = treeGrid.SelectedItem != null ? scrollviewer.HorizontalOffset : 0;
  94. treeGrid.ItemsSource = null;
  95. var setfilter = new Filter<JobDocumentSet>(x => x.Job.ID).IsEqualTo(JobID);
  96. if ((FolderIDs?.Any() == true) && !FolderIDs.Contains(CoreUtils.FullGuid))
  97. setfilter = setfilter.And(x => x.Folder.ID).InList(FolderIDs);
  98. if (DisciplineID != Guid.Empty)
  99. setfilter = setfilter.And(x => x.Discipline.ID).IsEqualTo(DisciplineID);
  100. if (TypeID != Guid.Empty)
  101. setfilter = setfilter.And(x => x.Type.ID).IsEqualTo(TypeID);
  102. if (CategoryID != Guid.Empty)
  103. setfilter = setfilter.And(x => x.Category.ID).IsEqualTo(CategoryID);
  104. if (AreaID != Guid.Empty)
  105. setfilter = setfilter.And(x => x.Area.ID).IsEqualTo(AreaID);
  106. if (!_includeretired)
  107. setfilter = setfilter.And(x => x.Retired).IsEqualTo(DateTime.MinValue);
  108. if (!String.IsNullOrWhiteSpace(SearchText))
  109. setfilter = setfilter.TextSearch(SearchText, x => x.Code, x => x.Description);
  110. MultiQuery query = new MultiQuery();
  111. query.Add(
  112. setfilter,
  113. new Columns<JobDocumentSet>(x => x.ID)
  114. .Add(x => x.Parent.ID)
  115. .Add(x => x.Code)
  116. .Add(x => x.Description)
  117. .Add(x => x.Date)
  118. .Add(x => x.Size)
  119. .Add(x => x.Scale)
  120. .Add(x => x.Employee.Name)
  121. .Add(x=>x.Folder.ID)
  122. .Add(x=>x.Discipline.Description)
  123. .Add(x=>x.Category.Description)
  124. .Add(x=>x.Type.Description)
  125. .Add(x=>x.Area.Description),
  126. new SortOrder<JobDocumentSet>(x => x.Code)
  127. );
  128. var milestonefilter = new Filter<JobDocumentSetMileStone>(x => x.DocumentSet.Job.ID).IsEqualTo(JobID);
  129. if ((FolderIDs?.Any() == true) && !FolderIDs.Contains(CoreUtils.FullGuid))
  130. milestonefilter = milestonefilter.And(x => x.DocumentSet.Folder.ID).InList(FolderIDs);
  131. query.Add(
  132. milestonefilter,
  133. new Columns<JobDocumentSetMileStone>(x => x.ID)
  134. .Add(x => x.DocumentSet.ID)
  135. .Add(x => x.DocumentSet.Code)
  136. .Add(x => x.Type.ID)
  137. .Add(x => x.Type.Code)
  138. .Add(x => x.Status)
  139. .Add(x => x.Notes)
  140. .Add(x => x.Revision)
  141. .Add(x => x.Due)
  142. .Add(x => x.Submitted)
  143. .Add(x => x.Closed)
  144. .Add(x => x.Attachments)
  145. .Add(x => x.Watermark)
  146. );
  147. if (_types == null)
  148. {
  149. query.Add<JobDocumentSetMileStoneType>(
  150. null,
  151. new Columns<JobDocumentSetMileStoneType>(x => x.ID)
  152. .Add(x => x.Code)
  153. .Add(x => x.Description),
  154. new SortOrder<JobDocumentSetMileStoneType>(x => x.Sequence)
  155. );
  156. }
  157. query.Query();
  158. Data = query.Get<JobDocumentSet>();
  159. _milestones = query.Get<JobDocumentSetMileStone>();
  160. if (_types == null)
  161. {
  162. _types = query.Get<JobDocumentSetMileStoneType>().ToDictionary<JobDocumentSetMileStoneType, Guid, MileStoneType>(
  163. x => x.ID,
  164. r => new MileStoneType()
  165. {
  166. Code = r.Get<JobDocumentSetMileStoneType, String>(c => c.Code),
  167. Description = r.Get<JobDocumentSetMileStoneType, String>(c => c.Description),
  168. SetMileStones = new Dictionary<Guid, List<CoreRow>>(),
  169. Columns = new List<string>()
  170. }
  171. );
  172. }
  173. else
  174. {
  175. foreach (var typeid in _types.Keys)
  176. {
  177. _types[typeid].Columns.Clear();
  178. _types[typeid].SetMileStones.Clear();
  179. }
  180. }
  181. var milestones = _milestones.ToLookup<JobDocumentSetMileStone, Guid, MileStone>(
  182. x => x.DocumentSet.ID,
  183. r => new MileStone()
  184. {
  185. TypeID = r.Get<JobDocumentSetMileStone, Guid>(c => c.Type.ID),
  186. Row = r
  187. }
  188. );
  189. foreach (var milestone in milestones)
  190. {
  191. foreach (var entry in milestone)
  192. {
  193. if (!_types[entry.TypeID].SetMileStones.ContainsKey(milestone.Key))
  194. _types[entry.TypeID].SetMileStones[milestone.Key] = new List<CoreRow>();
  195. if (_hidesuperceded)
  196. _types[entry.TypeID].SetMileStones[milestone.Key].Clear();
  197. _types[entry.TypeID].SetMileStones[milestone.Key].Add(entry.Row);
  198. }
  199. }
  200. List<String> columns = new List<string>();
  201. foreach (var typeid in _types.Keys)
  202. {
  203. int count = 1;
  204. foreach (var setkey in _types[typeid].SetMileStones.Keys)
  205. count = Math.Max(count, _types[typeid].SetMileStones[setkey].Count);
  206. for (int i = 1; i <= count; i++)
  207. {
  208. String column = String.Format("{0}_{1}", _types[typeid].Code, i);
  209. columns.Add(column);
  210. _types[typeid].Columns.Add(String.Format("Blocks[{0}]", column));
  211. }
  212. }
  213. _documentsets = new DocumentSetNodes(columns);
  214. foreach (var setrow in Data.Rows)
  215. {
  216. Guid setid = setrow.Get<JobDocumentSet, Guid>(x => x.ID);
  217. Guid parentid = _flatlist ? Guid.Empty : setrow.Get<JobDocumentSet, Guid>(x => x.Parent.ID);
  218. String code = setrow.Get<JobDocumentSet, String>(c => c.Code);
  219. String description = setrow.Get<JobDocumentSet, String>(c => c.Description);
  220. var tags = new List<String>()
  221. {
  222. setrow.Get<JobDocumentSet, String>(c => c.Discipline.Description),
  223. setrow.Get<JobDocumentSet, String>(c => c.Type.Description),
  224. setrow.Get<JobDocumentSet, String>(c => c.Category.Description),
  225. setrow.Get<JobDocumentSet, String>(c => c.Area.Description)
  226. }.Where(x=>!String.IsNullOrWhiteSpace(x)).Distinct().ToArray();
  227. var node = _documentsets.Add(setid, parentid);
  228. JobDocumentSetDescriptionBlock desc = new JobDocumentSetDescriptionBlock(
  229. setid, code, description, tags);
  230. node.Description = Serialization.Serialize(desc);
  231. JobDocumentSetDetailsBlock dets = new JobDocumentSetDetailsBlock()
  232. {
  233. ID = setid,
  234. Date = setrow.Get<JobDocumentSet, DateTime>(c => c.Date),
  235. Size = setrow.Get<JobDocumentSet, PaperSize>(c => c.Size),
  236. Scale = setrow.Get<JobDocumentSet, String>(c => c.Scale),
  237. Employee = setrow.Get<JobDocumentSet, String>(c => c.Employee.Name)
  238. };
  239. node.Details = Serialization.Serialize(dets);
  240. foreach (var typeid in _types.Keys)
  241. {
  242. if (_types[typeid].SetMileStones.TryGetValue(setid, out var rows))
  243. {
  244. int i = 1;
  245. foreach (var row in rows)
  246. {
  247. JobDocumentSetMileStoneBlock block = new JobDocumentSetMileStoneBlock();
  248. block.ID = row.Get<JobDocumentSetMileStone, Guid>(c => c.ID);
  249. block.Revision = row.Get<JobDocumentSetMileStone, String>(c => c.Revision);
  250. block.Status = row.Get<JobDocumentSetMileStone, JobDocumentSetMileStoneStatus>(c => c.Status);
  251. block.Date = (block.Status == JobDocumentSetMileStoneStatus.Approved) ||
  252. (block.Status == JobDocumentSetMileStoneStatus.Cancelled) ||
  253. (block.Status == JobDocumentSetMileStoneStatus.Rejected)
  254. ? row.Get<JobDocumentSetMileStone, DateTime>(c => c.Closed)
  255. : block.Status == JobDocumentSetMileStoneStatus.Submitted
  256. ? block.Date = row.Get<JobDocumentSetMileStone, DateTime>(c => c.Submitted)
  257. : row.Get<JobDocumentSetMileStone, DateTime>(c => c.Due);
  258. String[] notes = row.Get<JobDocumentSetMileStone, String[]>(c => c.Notes);
  259. block.Notes = notes != null ? String.Join("\n", notes) : "";
  260. block.Attachments = row.Get<JobDocumentSetMileStone, int>(c => c.Attachments);
  261. block.Watermark = row.Get<JobDocumentSetMileStone, String>(c => c.Watermark);
  262. node.Blocks[String.Format("{0}_{1}", _types[typeid].Code, i)] = Serialization.Serialize(block);
  263. i++;
  264. }
  265. }
  266. }
  267. }
  268. ConfigureColumns(_documentsets);
  269. ConfigureStackedHeader();
  270. treeGrid.ItemsSource = _documentsets.Nodes;
  271. DocumentCount.Content = $"{_documentsets.Nodes.Count} {(_documentsets.Nodes.Count > 1 ? "Records" : "Record")}";
  272. if (scrollviewer != null)
  273. {
  274. scrollviewer.ScrollToVerticalOffset(verticalOffset);
  275. scrollviewer.ScrollToHorizontalOffset(horizontalOffset);
  276. }
  277. }
  278. }
  279. #region Grid Configuration
  280. private void ConfigureColumns(DocumentSetNodes documentsets)
  281. {
  282. treeGrid.Columns.Clear();
  283. treeGrid.Columns.Add(new TreeGridTemplateColumn()
  284. {
  285. CellTemplate = FindResource("descriptionTemplate") as DataTemplate,
  286. MappingName = "Description",
  287. SetCellBoundValue = true,
  288. MinimumWidth = 250,
  289. ColumnSizer = TreeColumnSizer.Star
  290. });
  291. treeGrid.Columns.Add(new TreeGridTemplateColumn()
  292. {
  293. CellTemplate = FindResource("detailsTemplate") as DataTemplate,
  294. MappingName = "Details",
  295. SetCellBoundValue = true,
  296. Width = _settings.DetailsVisible ? 120 : 0
  297. });
  298. foreach (var column in documentsets.Columns)
  299. {
  300. var col = new TreeGridTemplateColumn()
  301. {
  302. CellTemplate = FindResource("milestoneTemplate") as DataTemplate,
  303. MappingName = String.Format("Blocks[{0}]",column),
  304. SetCellBoundValue = true,
  305. HeaderText = " ",
  306. Width = 80,
  307. ShowToolTip = true
  308. };
  309. treeGrid.Columns.Add(col);
  310. }
  311. }
  312. private void ConfigureStackedHeader()
  313. {
  314. stackedHeaderRow.StackedColumns.Clear();
  315. stackedHeaderRow.StackedColumns.Add(new StackedColumn()
  316. {
  317. ChildColumns = "Description,Details",
  318. HeaderText = "Document Register"
  319. });
  320. foreach (var typeid in _types.Keys)
  321. {
  322. stackedHeaderRow.StackedColumns.Add(new StackedColumn()
  323. {
  324. ChildColumns = String.Join(",", _types[typeid].Columns),
  325. HeaderText = _types[typeid].Code
  326. });
  327. }
  328. }
  329. private void TreeGrid_OnItemsSourceChanged(object? sender, TreeGridItemsSourceChangedEventArgs e)
  330. {
  331. var panel = treeGrid.GetTreePanel();
  332. panel.RowHeights[1] = 0;
  333. }
  334. private void TreeGrid_OnNodeCollapsing(object? sender, NodeCollapsingEventArgs e)
  335. {
  336. e.Cancel = true;
  337. }
  338. public MenuItem CreateCalendar(ContextMenu menu, string text, DateTime startDate, CoreRow[] milestones, Action<CoreRow[], DateTime?>? action)
  339. {
  340. var item = new MenuItem();
  341. var calendarItem = new MenuItem();
  342. var calendar = new System.Windows.Controls.Calendar { DisplayDate = startDate, SelectedDate = null};
  343. calendar.SelectedDatesChanged += (o, e) =>
  344. {
  345. action?.Invoke(milestones, calendar.SelectedDate);
  346. menu.IsOpen = false;
  347. };
  348. calendarItem.Header = calendar;
  349. calendarItem.Style = DynamicGridUtils.Resources["NonHighlightMenuItem"] as Style;
  350. item.Header = text;
  351. item.Items.Add(calendarItem);
  352. item.IsCheckable = false;
  353. return item;
  354. }
  355. private void TreeGrid_OnContextMenuOpening(object sender, ContextMenuEventArgs e)
  356. {
  357. if (treeGrid.SelectedItem == null)
  358. {
  359. e.Handled = true;
  360. return;
  361. }
  362. MileStoneMenu.Items.Clear();
  363. var tag = (e.OriginalSource as FrameworkElement).Tag;
  364. Point pos = Mouse.GetPosition(treeGrid);
  365. var treeGridPanel = this.treeGrid.GetTreePanel();
  366. // get the row and column index based on the pointer position
  367. var rowColumnIndex = treeGridPanel.PointToCellRowColumnIndex(pos);
  368. if (rowColumnIndex.IsEmpty)
  369. return;
  370. var treeNodeAtRowIndex = treeGrid.GetNodeAtRowIndex(rowColumnIndex.RowIndex);
  371. if (rowColumnIndex.ColumnIndex < 2)
  372. {
  373. var documents = treeGrid.SelectedItems.Select(x => (x as DocumentSetNode)).ToArray();
  374. var ids = documents.Select(x => x.ID).ToArray();
  375. MenuItem edit = new MenuItem();
  376. edit.Header = "Edit Document Set";
  377. edit.Click += (o, args) => { EditDocumentSets(ids); };
  378. MileStoneMenu.Items.Add(edit);
  379. if (documents.Length == 1)
  380. {
  381. MileStoneMenu.Items.Add(new Separator());
  382. MenuItem addchild = new MenuItem();
  383. addchild.Header = "Add Child";
  384. addchild.Click += (o, args) => { AddChildDocument(documents.First()); };
  385. MileStoneMenu.Items.Add(addchild);
  386. }
  387. MenuItem movetofolder = new MenuItem();
  388. movetofolder.Header = "Move To Folder";
  389. bool hasfolders = PopulateFolders(movetofolder, documents);
  390. if (hasfolders)
  391. {
  392. MileStoneMenu.Items.Add(new Separator());
  393. MileStoneMenu.Items.Add(movetofolder);
  394. }
  395. MileStoneMenu.Items.Add(new Separator());
  396. MenuItem detailscolumn = new MenuItem();
  397. detailscolumn.Header = (treeGrid.Columns[1].Width > 0) ? "Hide Detail Column" : "Show Detail Column";
  398. detailscolumn.Click += ShowHideDetailsColumn;
  399. MileStoneMenu.Items.Add(detailscolumn);
  400. return;
  401. }
  402. var mappingname = treeGrid.Columns[rowColumnIndex.ColumnIndex].MappingName;
  403. var blockkey = mappingname.Replace("Blocks[", "").Replace("]", "");
  404. var typeid = _types.FirstOrDefault(x => x.Value.Columns.Contains(mappingname)).Key;
  405. //Guid setid = (treeGrid.SelectedItem as DocumentSetNode).ID;
  406. Guid[] setids = treeGrid.SelectedItems.Select(x => (x as DocumentSetNode).ID).ToArray();
  407. //Guid.TryParse(tag.ToString(), out Guid milestoneid);
  408. var blocks = treeGrid.SelectedItems.Select(x => (x as DocumentSetNode).Blocks[blockkey]).Where(x => !String.IsNullOrWhiteSpace(x))
  409. .ToArray();
  410. var milestoneids = blocks.Select(x => Serialization.Deserialize<JobDocumentSetMileStoneBlock>(x).ID).ToArray();
  411. //var milestone = _milestones.Rows.FirstOrDefault(r => r.Get<JobDocumentSetMileStone, Guid>(c => c.ID) == milestoneid);
  412. var milestones = _milestones.Rows.Where(r => milestoneids.Contains(r.Get<JobDocumentSetMileStone, Guid>(c => c.ID))).ToArray();
  413. bool canCreateNewMileStones = true;
  414. foreach (var setid in setids)
  415. {
  416. var openmilestones = _milestones.Rows.Any(r =>
  417. Guid.Equals(r.Get<JobDocumentSetMileStone, Guid>(c => c.DocumentSet.ID), setid)
  418. && Guid.Equals(r.Get<JobDocumentSetMileStone, Guid>(c => c.Type.ID), typeid)
  419. && (r.Get<JobDocumentSetMileStone, DateTime>(c => c.Closed).IsEmpty() ||
  420. (r.Get<JobDocumentSetMileStone, JobDocumentSetMileStoneStatus>(c => c.Status) == JobDocumentSetMileStoneStatus.Approved))
  421. );
  422. if (openmilestones)
  423. canCreateNewMileStones = false;
  424. }
  425. if (canCreateNewMileStones)
  426. {
  427. MenuItem newmilestone = new MenuItem()
  428. {
  429. Header = "New Milestone",
  430. Tag = typeid
  431. };
  432. newmilestone.Click += (o, args) => { CreateMileStone(setids, typeid, DateTime.Today); };
  433. MileStoneMenu.Items.Add(newmilestone);
  434. }
  435. if (milestones.Any())
  436. {
  437. MenuItem setstatus = new MenuItem() { Header = "Change Status" };
  438. foreach (JobDocumentSetMileStoneStatus newstatus in Enum.GetValues(typeof(JobDocumentSetMileStoneStatus)))
  439. {
  440. MenuItem setstatus2 = null;
  441. switch (newstatus)
  442. {
  443. case JobDocumentSetMileStoneStatus.Unknown:
  444. break;
  445. case JobDocumentSetMileStoneStatus.NotStarted:
  446. case JobDocumentSetMileStoneStatus.InProgress:
  447. case JobDocumentSetMileStoneStatus.OnHold:
  448. case JobDocumentSetMileStoneStatus.InfoRequired:
  449. setstatus2 = new MenuItem() { Header = newstatus.ToString().SplitCamelCase() };
  450. setstatus2.Click += (o, args) => { ChangeMileStoneStatus(milestones, newstatus, DateTime.MinValue, DateTime.MinValue); };
  451. break;
  452. case JobDocumentSetMileStoneStatus.Submitted:
  453. setstatus2 = CreateCalendar(
  454. MileStoneMenu,
  455. newstatus.ToString().SplitCamelCase(),
  456. DateTime.Today,
  457. milestones,
  458. (r, t) => { ChangeMileStoneStatus(milestones, newstatus, t, DateTime.MinValue); }
  459. );
  460. break;
  461. case JobDocumentSetMileStoneStatus.Approved:
  462. case JobDocumentSetMileStoneStatus.Cancelled:
  463. case JobDocumentSetMileStoneStatus.Rejected:
  464. setstatus2 = CreateCalendar(
  465. MileStoneMenu,
  466. newstatus.ToString().SplitCamelCase(),
  467. DateTime.Today,
  468. milestones,
  469. (r, t) => { ChangeMileStoneStatus(milestones, newstatus, null, t); }
  470. );
  471. break;
  472. }
  473. if (setstatus2 != null)
  474. setstatus.Items.Add(setstatus2);
  475. }
  476. MileStoneMenu.Items.Add(setstatus);
  477. MenuItem editmilestone = new MenuItem() { Header = "Edit MileStone" };
  478. editmilestone.Click += (o, args) => { EditMileStones(milestones); };
  479. MileStoneMenu.Items.Add(editmilestone);
  480. //var closed = milestones.Any(r => !r.Get<JobDocumentSetMileStone, DateTime>(c => c.Closed).IsEmpty());
  481. if ((setids.Length == 1) && (milestones.Length == 1)) // && !closed)
  482. {
  483. var attachments = milestones[0].Get<JobDocumentSetMileStone, int>(x => x.Attachments);
  484. if (attachments > 1)
  485. {
  486. MenuItem splitmilestone = new MenuItem() { Header = "Split MileStone" };
  487. splitmilestone.Click += (o, args) => { SplitMileStone(setids[0], milestones[0]); };
  488. MileStoneMenu.Items.Add(splitmilestone);
  489. }
  490. }
  491. if (milestones.Any())
  492. {
  493. MileStoneMenu.Items.Add(new Separator());
  494. MenuItem upload = new MenuItem() { Header = milestones.Length > 1 ? "Upload and Match File Names" : "Upload Files" };
  495. upload.Click += (o, args) => { UploadFiles(milestones); };
  496. MileStoneMenu.Items.Add(upload);
  497. MenuItem download = new MenuItem() { Header = "Download Files" };
  498. download.Items.Add(new MenuItem());
  499. download.SubmenuOpened += (o, e) =>
  500. {
  501. download.Items.Clear();
  502. var files = new Client<JobDocumentSetMileStoneFile>().Query(
  503. new Filter<JobDocumentSetMileStoneFile>(x => x.EntityLink.ID).InList(milestoneids),
  504. new Columns<JobDocumentSetMileStoneFile>(x => x.ID)
  505. .Add(x => x.DocumentLink.FileName)
  506. .Add(x => x.DocumentLink.ID),
  507. new SortOrder<JobDocumentSetMileStoneFile>(x => x.DocumentLink.FileName)
  508. );
  509. if (files.Rows.Any())
  510. {
  511. foreach (var row in files.Rows)
  512. {
  513. MenuItem downloadone = new MenuItem()
  514. {
  515. Header = row.Get<JobDocumentSetMileStoneFile, String>(x => x.DocumentLink.FileName),
  516. };
  517. downloadone.Click += (sender, args) =>
  518. {
  519. DownloadFiles(
  520. new CoreRow[] { milestones[0] },
  521. row.Get<JobDocumentSetMileStoneFile, Guid>(x => x.DocumentLink.ID)
  522. );
  523. };
  524. download.Items.Add(downloadone);
  525. }
  526. if (download.Items.Count > 1)
  527. {
  528. download.Items.Add(new Separator());
  529. MenuItem downloadall = new MenuItem()
  530. {
  531. Header = "Download All",
  532. };
  533. downloadall.Click += (sender, args) =>
  534. {
  535. DownloadFiles(
  536. milestones,
  537. Guid.Empty
  538. );
  539. };
  540. download.Items.Add(downloadall);
  541. }
  542. }
  543. else
  544. {
  545. download.Items.Add(
  546. new MenuItem()
  547. {
  548. Header = "No Files to download",
  549. IsEnabled = false
  550. }
  551. );
  552. }
  553. };
  554. MileStoneMenu.Items.Add(download);
  555. }
  556. // if ((milestoneids.Length == 1)) // && !closed)
  557. // {
  558. // MenuItem managefiles = new MenuItem()
  559. // {
  560. // Header = "Manage Files"
  561. // };
  562. // managefiles.Click += (sender, args) => { ManageFiles(milestones[0]); };
  563. // MileStoneMenu.Items.Add(managefiles);
  564. // }
  565. MileStoneMenu.Items.Add(new Separator());
  566. MenuItem delete = new MenuItem { Header = "Delete MileStone" };
  567. delete.Click += (o, args) => DeleteMileStone(milestones);
  568. MileStoneMenu.Items.Add(delete);
  569. }
  570. if (MileStoneMenu.Items.Count == 0)
  571. e.Handled = true;
  572. }
  573. private void ShowHideDetailsColumn(object sender, RoutedEventArgs e)
  574. {
  575. _settings.DetailsVisible = !_settings.DetailsVisible;
  576. new UserConfiguration<JobDocumentSetTreeSettings>().Save(_settings);
  577. treeGrid.Columns[1].Width = _settings.DetailsVisible ? 120: 0;
  578. }
  579. private bool PopulateFolders(MenuItem menu, IEnumerable<DocumentSetNode> documents)
  580. {
  581. CoreTable data = new Client<JobDocumentSetFolder>().Query(
  582. new Filter<JobDocumentSetFolder>(x => x.Job.ID).IsEqualTo(JobID),
  583. new Columns<JobDocumentSetFolder>(x => x.ID)
  584. .Add(x => x.Parent.ID)
  585. .Add(x => x.Name)
  586. );
  587. if (!data.Rows.Any())
  588. return false;
  589. DynamicTreeNodes folders = new DynamicTreeNodes();
  590. folders.Load<JobDocumentSetFolder>(data, x => x.ID, x => x.Parent.ID, x => x.Name);
  591. foreach (var folder in folders.Nodes)
  592. DoPopulateFolder(menu, folder, documents);
  593. return true;
  594. }
  595. private void DoPopulateFolder(MenuItem header, DynamicTreeNode folder, IEnumerable<DocumentSetNode> documents)
  596. {
  597. MenuItem menu = new MenuItem();
  598. menu.Header = folder.Description;
  599. menu.Click += (sender, args) => MoveToFolder(documents, folder);
  600. header.Items.Add(menu);
  601. foreach (var childfolder in folder.Children)
  602. DoPopulateFolder(menu, childfolder, documents);
  603. }
  604. private void MoveToFolder(IEnumerable<DocumentSetNode> documents, DynamicTreeNode folder)
  605. {
  606. using (new WaitCursor())
  607. {
  608. List<JobDocumentSet> updates = new List<JobDocumentSet>();
  609. foreach (var document in documents)
  610. {
  611. var folderid = Data.Rows.FirstOrDefault(r => r.Get<JobDocumentSet, Guid>(c => c.ID) == document.ID)?.Get<JobDocumentSet, Guid>(c => c.Folder.ID) ?? Guid.Empty;
  612. if (folderid != folder.ID)
  613. {
  614. var update = new JobDocumentSet();
  615. update.ID = document.ID;
  616. update.CommitChanges();
  617. update.Folder.ID = folder.ID;
  618. update.Parent.ID = Guid.Empty;
  619. updates.Add(update);
  620. }
  621. }
  622. if (updates.Any())
  623. new Client<JobDocumentSet>().Save(updates, "Moved to Folder: " + folder.Description);
  624. else
  625. MessageBox.Show("Nothing to Do!");
  626. }
  627. Refresh();
  628. }
  629. private void SplitMileStone(Guid setid, CoreRow milestone)
  630. {
  631. if (MessageBox.Show(
  632. "Are you sure you wish to split this Document Set?",
  633. "Confirm Delete",
  634. MessageBoxButton.YesNo
  635. ) != MessageBoxResult.Yes)
  636. return;
  637. Guid milestoneid = milestone.Get<JobDocumentSetMileStone, Guid>(c => c.ID);
  638. var dlg = new MultiSelectDialog<JobDocumentSetMileStoneFile>(
  639. new Filter<JobDocumentSetMileStoneFile>(c => c.EntityLink.ID).IsEqualTo(milestoneid),
  640. null,
  641. true
  642. );
  643. if (dlg.ShowDialog() == true)
  644. {
  645. var files = dlg.Items();
  646. Progress.ShowModal("Splitting Document Set", (progress) =>
  647. {
  648. JobDocumentSet newset = new Client<JobDocumentSet>().Query(
  649. new Filter<JobDocumentSet>(x => x.ID).IsEqualTo(setid)
  650. ).Rows.FirstOrDefault()?.ToObject<JobDocumentSet>();
  651. if (newset != null)
  652. {
  653. newset.ID = Guid.Empty;
  654. newset.CommitChanges();
  655. newset.Parent.ID = setid;
  656. newset.Code = String.Format("{0} (COPY)", newset.Code);
  657. //newset.Description = "New Child";
  658. new Client<JobDocumentSet>().Save(newset, "Created by Splitting MileStone");
  659. progress.Report("Creating Milestone");
  660. JobDocumentSetMileStone newms = new Client<JobDocumentSetMileStone>().Query(
  661. new Filter<JobDocumentSetMileStone>(c=>c.ID).IsEqualTo(milestoneid)
  662. ).Rows.FirstOrDefault()?.ToObject<JobDocumentSetMileStone>();
  663. if (newms != null)
  664. {
  665. newms.ID = Guid.Empty;
  666. newset.CommitChanges();
  667. newms.DocumentSet.ID = newset.ID;
  668. new Client<JobDocumentSetMileStone>().Save(newms, "Created By Splitting MileStone");
  669. progress.Report("Moving Files");
  670. foreach (var file in files)
  671. file.EntityLink.ID = newms.ID;
  672. new Client<JobDocumentSetMileStoneFile>().Save(files, "Moved when Splitting MileStone");
  673. }
  674. }
  675. });
  676. Refresh();
  677. }
  678. }
  679. private void AddChildDocument(DocumentSetNode node)
  680. {
  681. if (node == null)
  682. return;
  683. var folderid = Data.Rows.FirstOrDefault(r => r.Get<JobDocumentSet, Guid>(c => c.ID) == node.ID)?.Get<JobDocumentSet, Guid>(c => c.Folder.ID) ?? Guid.Empty;
  684. JobDocumentSet newset = new JobDocumentSet();
  685. newset.Parent.ID = node.ID;
  686. newset.Job.ID = JobID;
  687. newset.Folder.ID = folderid;
  688. var grid = new DynamicDataGrid<JobDocumentSet>();
  689. if (grid.EditItems(new[] { newset }))
  690. Refresh();
  691. }
  692. // private void ManageFiles(CoreRow milestone)
  693. // {
  694. // var grid = new JobDocumentSetMileStoneFileGrid();
  695. // grid.OnGetWaterMark += (row) => milestone.Get<JobDocumentSetMileStone, String>(c => c.Watermark);
  696. // grid.ShowSupercededColumn = false;
  697. // Window window = new Window();
  698. // window.Padding = new Thickness(5);
  699. // window.Content = grid;
  700. // window.Width = 300;
  701. // window.Height = 500;
  702. // window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
  703. // grid.Load(milestone.ToObject<JobDocumentSetMileStone>(), null);
  704. // grid.Margin = new Thickness(5);
  705. // grid.Refresh(true, true);
  706. // window.ShowDialog();
  707. // Refresh();
  708. // }
  709. private void DownloadFiles(CoreRow[] rows, Guid id)
  710. {
  711. FolderBrowserDialog dlg = new FolderBrowserDialog();
  712. if (dlg.ShowDialog() == DialogResult.OK)
  713. {
  714. Progress.ShowModal("Downloading Files", (progress) =>
  715. {
  716. foreach (var row in rows)
  717. {
  718. var status = row.Get<JobDocumentSetMileStone, JobDocumentSetMileStoneStatus>(c => c.Status);
  719. var stage = row.Get<JobDocumentSetMileStone, String>(c => c.Type.Code);
  720. var revision = row.Get<JobDocumentSetMileStone, String>(c => c.Revision);
  721. String tag = String.Format(" - {0}{1} ({2})", stage, String.IsNullOrWhiteSpace(revision) ? "" : " - Rev " + revision,
  722. status.ToString().SplitCamelCase());
  723. var filter = id == Guid.Empty
  724. ? new Filter<Document>(x => x.ID).InQuery(
  725. new Filter<JobDocumentSetMileStoneFile>(x => x.EntityLink.ID).IsEqualTo(
  726. row.Get<JobDocumentSetMileStone, Guid>(c => c.ID)),
  727. x => x.DocumentLink.ID
  728. )
  729. : new Filter<Document>(x => x.ID).IsEqualTo(id);
  730. var files = new Client<Document>().Query(filter);
  731. foreach (var filerow in files.Rows)
  732. {
  733. string filename = filerow.Get<Document, String>(c => c.FileName);
  734. string extension = Path.GetExtension(filename);
  735. string basefilename = Path.GetFileNameWithoutExtension(filename);
  736. filename = String.Format("{0}{1}{2}", basefilename, tag, extension);
  737. filename = Path.Combine(dlg.SelectedPath, filename);
  738. File.WriteAllBytes(filename, filerow.Get<Document, byte[]>(c => c.Data));
  739. }
  740. }
  741. });
  742. Process.Start(new ProcessStartInfo(dlg.SelectedPath) { UseShellExecute = true });
  743. }
  744. }
  745. private bool SelectFiles(out String[] files)
  746. {
  747. Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
  748. dlg.Filter = "PDF Files (*.pdf)|*.pdf";
  749. dlg.Multiselect = true;
  750. if ((dlg.ShowDialog() == true) && (dlg.FileNames.Length > 0))
  751. {
  752. files = dlg.FileNames.ToArray();
  753. return true;
  754. }
  755. files = null;
  756. return false;
  757. }
  758. private void UploadFiles(CoreRow[] rows)
  759. {
  760. if (rows?.Length < 1)
  761. {
  762. MessageBox.Show("No Rows Selected");
  763. return;
  764. }
  765. if (SelectFiles(out String[] filenames))
  766. {
  767. Dictionary<String, Guid>? setlookups = rows.Length > 1
  768. ? new Dictionary<string, Guid>(
  769. rows.Select(
  770. r => new KeyValuePair<String, Guid>(
  771. r.Get<JobDocumentSetMileStone, String>(c => c.DocumentSet.Code),
  772. r.Get<JobDocumentSetMileStone, Guid>(c => c.ID)
  773. )
  774. )
  775. )
  776. : null;
  777. if ((setlookups != null) && (rows.Length > 1))
  778. {
  779. var unmatched = filenames.Where(filename => !setlookups.Keys.Any(key => Path.GetFileName(filename).ToLower().ToLower().StartsWith(key.ToLower())));
  780. if (unmatched.Any())
  781. {
  782. MessageBox.Show("Unable to match the following files:\n" + String.Join("\n", unmatched));
  783. return;
  784. }
  785. }
  786. var milestoneids = rows.Select(r => r.Get<JobDocumentSetMileStone, Guid>(c => c.ID)).ToArray();
  787. var currentfiles = new Client<JobDocumentSetMileStoneFile>().Query(
  788. new Filter<JobDocumentSetMileStoneFile>(x => x.EntityLink.ID).InList(milestoneids),
  789. new Columns<JobDocumentSetMileStoneFile>(x => x.DocumentLink.ID)
  790. .Add(x => x.DocumentLink.FileName)
  791. ).ToDictionary<JobDocumentSetMileStoneFile, String, Guid>(x => x.DocumentLink.FileName, x => x.DocumentLink.ID);
  792. var matched = filenames.Where(filename => currentfiles.Keys.Any(key => Path.GetFileName(filename).ToLower().StartsWith(key.ToLower())));
  793. bool replace = false;
  794. if (matched.Any())
  795. {
  796. var confirm = MessageBox.Show(
  797. "The following files already exist!\n\n Do you wish to replace them?\n\n" + String.Join("\n", matched),
  798. "Replace Files",
  799. MessageBoxButton.YesNoCancel);
  800. if (confirm == MessageBoxResult.Cancel)
  801. return;
  802. replace = confirm == MessageBoxResult.Yes;
  803. }
  804. int doccount = 0;
  805. Progress.ShowModal("Uploading Files", (progress) =>
  806. {
  807. List<Document> documents = new List<Document>();
  808. foreach (var file in filenames)
  809. {
  810. if (!matched.Contains(file) || replace)
  811. {
  812. var filename = Path.GetFileName(file).ToLower();
  813. var code = currentfiles.Keys.FirstOrDefault(key => filename.StartsWith(key.ToLower()));
  814. var doc = new Document();
  815. doc.ID = String.IsNullOrWhiteSpace(code) ? Guid.Empty : currentfiles[code];
  816. doc.CommitChanges();
  817. doc.Data = File.ReadAllBytes(file);
  818. doc.FileName = filename;
  819. doc.CRC = CoreUtils.CalculateCRC(doc.Data);
  820. doc.TimeStamp = new FileInfo(file).LastWriteTime;
  821. documents.Add(doc);
  822. }
  823. }
  824. if (documents.Any())
  825. new Client<Document>().Save(documents.ToArray(), "Uploaded by User");
  826. progress.Report("Updating Links");
  827. List<JobDocumentSetMileStoneFile> links = new List<JobDocumentSetMileStoneFile>();
  828. foreach (var document in documents)
  829. {
  830. if (!currentfiles.Any(x => x.Value == document.ID))
  831. {
  832. var link = new JobDocumentSetMileStoneFile();
  833. if (setlookups != null)
  834. {
  835. var filename = Path.GetFileName(document.FileName).ToLower();
  836. var code = setlookups.Keys.FirstOrDefault(key => filename.StartsWith(key.ToLower()));
  837. link.EntityLink.ID = setlookups[code];
  838. }
  839. else
  840. link.EntityLink.ID = rows.First().Get<JobDocumentSetMileStone, Guid>(c => c.ID);
  841. link.DocumentLink.ID = document.ID;
  842. links.Add(link);
  843. }
  844. }
  845. if (links.Any())
  846. new Client<JobDocumentSetMileStoneFile>().Save(links, "Uploaded By User");
  847. doccount = documents.Count;
  848. });
  849. MessageBox.Show(String.Format("{0} Files Uploaded", doccount > 0 ? doccount : "No"));
  850. Refresh();
  851. }
  852. }
  853. private Dictionary<Guid, JobDocumentSetMileStone> GetPreviousMileStones(Guid[] setids, Guid typeid)
  854. {
  855. var result = new Dictionary<Guid, JobDocumentSetMileStone>();
  856. foreach (var setid in setids)
  857. {
  858. var typeindex = _types.Keys.IndexOf(typeid);
  859. JobDocumentSetMileStone? last = null;
  860. while ((last == null) && (typeindex > 0))
  861. {
  862. last = _milestones.Rows.LastOrDefault(r =>
  863. (r.Get<JobDocumentSetMileStone, Guid>(c => c.DocumentSet.ID) == setid) &&
  864. (r.Get<JobDocumentSetMileStone, Guid>(c => c.Type.ID) == _types.Keys.ToArray()[typeindex]))
  865. ?.ToObject<JobDocumentSetMileStone>();
  866. typeindex--;
  867. }
  868. if (last != null)
  869. result[setid] = last;
  870. }
  871. return result;
  872. }
  873. private void CreateMileStone(Guid[] setids, Guid typeid, DateTime duedate)
  874. {
  875. bool bCopy = false;
  876. var lastmilestones = GetPreviousMileStones(setids, typeid);
  877. if (lastmilestones.Any(x => x.Value.Attachments > 0))
  878. {
  879. var confirm = MessageBox.Show("Do you wish to copy the files from the previous milestones?", "Copy Files",
  880. MessageBoxButton.YesNoCancel);
  881. if (confirm == MessageBoxResult.Cancel)
  882. return;
  883. bCopy = confirm == MessageBoxResult.Yes;
  884. }
  885. Dictionary<JobDocumentSetMileStone,JobDocumentSetMileStoneFile[]> updates = new Dictionary<JobDocumentSetMileStone, JobDocumentSetMileStoneFile[]>();
  886. foreach (var setid in setids)
  887. {
  888. JobDocumentSetMileStone milestone = new JobDocumentSetMileStone();
  889. milestone.DocumentSet.ID = setid;
  890. milestone.Type.ID = typeid;
  891. milestone.Status = JobDocumentSetMileStoneStatus.NotStarted;
  892. milestone.Due = duedate;
  893. JobDocumentSetMileStoneFile[] files = new JobDocumentSetMileStoneFile[] { };
  894. if (bCopy && lastmilestones.TryGetValue(setid, out var lastmilestone))
  895. {
  896. if (lastmilestone.Attachments > 0)
  897. {
  898. files = new Client<JobDocumentSetMileStoneFile>().Query(
  899. new Filter<JobDocumentSetMileStoneFile>(x => x.EntityLink.ID).InList(lastmilestone.ID),
  900. new Columns<JobDocumentSetMileStoneFile>(x=>x.EntityLink.DocumentSet.ID)
  901. .Add(x => x.DocumentLink.FileName)
  902. .Add(x => x.DocumentLink.ID),
  903. new SortOrder<JobDocumentSetMileStoneFile>(x => x.DocumentLink.FileName)
  904. ).Rows.Select(r=>r.ToObject<JobDocumentSetMileStoneFile>()).ToArray();
  905. }
  906. }
  907. updates[milestone] = files;
  908. }
  909. var grid = new JobDocumentSetMileStoneGrid();
  910. grid.OnAfterSave += (editor, items) =>
  911. {
  912. if (updates.Keys.Count == 1)
  913. return;
  914. List<JobDocumentSetMileStoneFile> fileupdates = new List<JobDocumentSetMileStoneFile>();
  915. foreach (var milestone in updates.Keys)
  916. {
  917. foreach (var file in updates[milestone])
  918. {
  919. file.EntityLink.ID = milestone.ID;
  920. fileupdates.Add(file);
  921. }
  922. }
  923. if (fileupdates.Any())
  924. new Client<JobDocumentSetMileStoneFile>().Save(fileupdates,"");
  925. };
  926. if (grid.EditItems(updates.Keys.ToArray(), (t) =>
  927. {
  928. if ((t == typeof(JobDocumentSetMileStoneFile)) && (updates.Keys.Count == 1))
  929. {
  930. CoreTable result = new CoreTable();
  931. result.LoadColumns(typeof(JobDocumentSetMileStoneFile));
  932. result.LoadRows(updates[updates.Keys.First()]);
  933. return result;
  934. }
  935. return null;
  936. }, true)
  937. )
  938. Refresh();
  939. }
  940. private void ChangeMileStoneStatus(CoreRow[] rows, JobDocumentSetMileStoneStatus newstatus, DateTime? issued, DateTime? closed)
  941. {
  942. var milestones = rows.Select(r=>r.ToObject<JobDocumentSetMileStone>()).ToArray();
  943. foreach (var milestone in milestones)
  944. {
  945. if (issued.HasValue)
  946. milestone.Submitted = issued.Value;
  947. if (closed.HasValue)
  948. milestone.Closed = closed.Value;
  949. milestone.Status = newstatus;
  950. }
  951. using (new WaitCursor())
  952. new Client<JobDocumentSetMileStone>().Save(milestones, "Changed Status to " + newstatus.ToString().SplitCamelCase());
  953. Refresh();
  954. }
  955. private void EditMileStones(CoreRow[] rows)
  956. {
  957. var ids = rows.Select(r => r.Get<JobDocumentSetMileStone, Guid>(x => x.ID)).ToArray();
  958. var milestones = new Client<JobDocumentSetMileStone>().Query(
  959. new Filter<JobDocumentSetMileStone>(x => x.ID).InList(ids)
  960. ).Rows.Select(r=>r.ToObject<JobDocumentSetMileStone>()).ToArray();
  961. var grid = new JobDocumentSetMileStoneGrid();
  962. if (grid.EditItems(milestones))
  963. Refresh();
  964. }
  965. private void DeleteMileStone(CoreRow[] rows)
  966. {
  967. var milestones = rows.Select(r=>r.ToObject<JobDocumentSetMileStone>()).ToArray();
  968. using (new WaitCursor())
  969. new Client<JobDocumentSetMileStone>().Delete(milestones,"Deleted by User");
  970. Refresh();
  971. }
  972. private void TreeGrid_OnCellToolTipOpening(object? sender, TreeGridCellToolTipOpeningEventArgs e)
  973. {
  974. var column = e.Column.MappingName.Replace("Blocks[","").Replace("]","");
  975. var data = (e.Record as DocumentSetNode).Blocks[column];
  976. if (String.IsNullOrWhiteSpace(data))
  977. return;
  978. var block = Serialization.Deserialize<JobDocumentSetMileStoneBlock>(data.ToString());
  979. Guid id = block.ID;
  980. TextBlock text = new TextBlock();
  981. if (!String.IsNullOrWhiteSpace(block.Notes))
  982. {
  983. text.Inlines.Add(new Run("Milestone Notes\n") { FontWeight = FontWeights.Bold, TextDecorations = TextDecorations.Underline });
  984. text.Inlines.Add(new Run(block.Notes.Replace("=","").Replace("\n\n","\n")) { FontStyle = FontStyles.Italic });
  985. }
  986. if (block.Attachments > 0)
  987. {
  988. if (!String.IsNullOrWhiteSpace(block.Notes))
  989. text.Inlines.Add(new Run("\n\n"));
  990. text.Inlines.Add(new Run("Uploaded Files") { FontWeight = FontWeights.Bold, TextDecorations = TextDecorations.Underline });
  991. var files = new Client<JobDocumentSetMileStoneFile>().Query(
  992. new Filter<JobDocumentSetMileStoneFile>(x => x.EntityLink.ID).IsEqualTo(block.ID),
  993. new Columns<JobDocumentSetMileStoneFile>(x => x.DocumentLink.FileName),
  994. new SortOrder<JobDocumentSetMileStoneFile>(x => x.DocumentLink.FileName)
  995. );
  996. foreach (var row in files.Rows)
  997. text.Inlines.Add(new Run("\n"+row.Get<JobDocumentSetMileStoneFile,String>(c=>c.DocumentLink.FileName)) { FontStyle = FontStyles.Italic });
  998. }
  999. if (!text.Inlines.Any())
  1000. {
  1001. e.ToolTip.Template = null;
  1002. return;
  1003. }
  1004. e.ToolTip.Template = TemplateGenerator.CreateControlTemplate(
  1005. typeof(System.Windows.Controls.ToolTip),
  1006. () =>
  1007. {
  1008. var border = new Border
  1009. {
  1010. BorderBrush = new SolidColorBrush(Colors.Gray),
  1011. BorderThickness = new Thickness(0.75),
  1012. CornerRadius = new CornerRadius(5),
  1013. Background = new SolidColorBrush(Colors.LightYellow),
  1014. Padding = new Thickness(5),
  1015. Child = text
  1016. };
  1017. return border;
  1018. }
  1019. );
  1020. }
  1021. #endregion
  1022. #region Button Bar Actions
  1023. private void AddTypes(MenuItem parent, Action<Guid> addfunction)
  1024. {
  1025. if(_types.Count == 0)
  1026. {
  1027. MenuItem item = new MenuItem() { Header = "No Document Milestones", IsEnabled = false };
  1028. parent.Items.Add(item);
  1029. }
  1030. else
  1031. {
  1032. foreach (var type in _types.Keys)
  1033. {
  1034. MenuItem item = new MenuItem() { Header = _types[type].Description, Tag = type };
  1035. item.Click += (o, e) => addfunction(type);
  1036. parent.Items.Add(item);
  1037. }
  1038. }
  1039. }
  1040. private void Add_OnClick(object sender, RoutedEventArgs e)
  1041. {
  1042. if (FolderIDs?.Any() != true)
  1043. {
  1044. MessageBox.Show("Please choose a Folder first!");
  1045. return;
  1046. }
  1047. else if(FolderIDs.First() == CoreUtils.FullGuid)
  1048. {
  1049. MessageBox.Show("Cannot add items to this folder.");
  1050. return;
  1051. }
  1052. ContextMenu menu = new ContextMenu();
  1053. var onetoone = new MenuItem() { Header = "Add Individual Files" };
  1054. AddTypes(onetoone, AddOneToOneFiles);
  1055. menu.Items.Add(onetoone);
  1056. var manytoone = new MenuItem() { Header = "Add Sets of Files" };
  1057. AddTypes(manytoone, AddManyToOneFiles);
  1058. menu.Items.Add(manytoone);
  1059. menu.Items.Add(new Separator());
  1060. var manual = new MenuItem() { Header = "Add Document Set Manually" };
  1061. manual.Click += (o, e) => { AddDocumentSet(); };
  1062. menu.Items.Add(manual);
  1063. menu.IsOpen = true;
  1064. }
  1065. private void AddOneToOneFiles(Guid type)
  1066. {
  1067. Guid folderid = FolderIDs?.FirstOrDefault() ?? Guid.Empty;
  1068. if (!SelectFiles(out String[] filenames))
  1069. return;
  1070. Progress.ShowModal("Preparing Upload", (progress) =>
  1071. {
  1072. Dictionary<String, Tuple<Document, JobDocumentSet, JobDocumentSetMileStone, JobDocumentSetMileStoneFile>> map =
  1073. new Dictionary<string, Tuple<Document, JobDocumentSet, JobDocumentSetMileStone, JobDocumentSetMileStoneFile>>();
  1074. foreach (var filename in filenames)
  1075. {
  1076. var data = File.ReadAllBytes(filename);
  1077. Document document = new Document()
  1078. {
  1079. FileName = Path.GetFileName(filename).ToLower(),
  1080. Data = data,
  1081. CRC = CoreUtils.CalculateCRC(data),
  1082. TimeStamp = new FileInfo(filename).LastWriteTime
  1083. };
  1084. JobDocumentSet set = new JobDocumentSet();
  1085. set.Job.ID = JobID;
  1086. set.Folder.ID = folderid;
  1087. set.Code = Path.GetFileNameWithoutExtension(filename).ToUpper();
  1088. set.Description = Path.GetFileNameWithoutExtension(filename).ToUpper();
  1089. set.Discipline.ID = DisciplineID;
  1090. set.Type.ID = TypeID;
  1091. set.Category.ID = CategoryID;
  1092. set.Area.ID = AreaID;
  1093. JobDocumentSetMileStone milestone = new JobDocumentSetMileStone();
  1094. milestone.Type.ID = type;
  1095. milestone.Status = JobDocumentSetMileStoneStatus.InProgress;
  1096. milestone.Due = DateTime.Today;
  1097. JobDocumentSetMileStoneFile file = new JobDocumentSetMileStoneFile();
  1098. map[filename] = new Tuple<Document, JobDocumentSet, JobDocumentSetMileStone, JobDocumentSetMileStoneFile>(
  1099. document,
  1100. set,
  1101. milestone,
  1102. file
  1103. );
  1104. }
  1105. progress.Report("Uploading Files");
  1106. var docs = map.Select(x => x.Value.Item1);
  1107. new Client<Document>().Save(docs, "Uploaded By File Selection");
  1108. progress.Report("Creating Document Sets");
  1109. var sets = map.Select(x => x.Value.Item2);
  1110. new Client<JobDocumentSet>().Save(sets, "Uploaded by File Selection");
  1111. progress.Report("Creating MileStones");
  1112. foreach (var key in map.Keys)
  1113. map[key].Item3.DocumentSet.ID = map[key].Item2.ID;
  1114. var milestones = map.Select(x => x.Value.Item3);
  1115. new Client<JobDocumentSetMileStone>().Save(milestones, "Uploaded by File Selection");
  1116. progress.Report("Linking Documents");
  1117. foreach (var key in map.Keys)
  1118. {
  1119. map[key].Item4.EntityLink.ID = map[key].Item3.ID;
  1120. map[key].Item4.DocumentLink.ID = map[key].Item1.ID;
  1121. }
  1122. var files = map.Select(x => x.Value.Item4);
  1123. new Client<JobDocumentSetMileStoneFile>().Save(files, "Uploaded by File Selection");
  1124. });
  1125. MessageBox.Show(String.Format("{0} Document Sets Created", filenames.Length));
  1126. Refresh();
  1127. }
  1128. private void AddManyToOneFiles(Guid type)
  1129. {
  1130. Guid folderid = FolderIDs?.FirstOrDefault() ?? Guid.Empty;
  1131. if (!SelectFiles(out String[] filenames))
  1132. return;
  1133. JobDocumentSet set = new JobDocumentSet();
  1134. set.Job.ID = JobID;
  1135. set.Folder.ID = folderid;
  1136. set.Discipline.ID = DisciplineID;
  1137. set.Type.ID = TypeID;
  1138. set.Category.ID = CategoryID;
  1139. set.Area.ID = AreaID;
  1140. var grid = new DynamicDataGrid<JobDocumentSet>();
  1141. grid.OnAfterSave += (form, items) =>
  1142. {
  1143. Progress.ShowModal("Creating MileStone", (progress) =>
  1144. {
  1145. JobDocumentSetMileStone milestone = new JobDocumentSetMileStone();
  1146. milestone.DocumentSet.ID = set.ID;
  1147. milestone.Type.ID = type;
  1148. milestone.Status = JobDocumentSetMileStoneStatus.InProgress;
  1149. milestone.Due = DateTime.Today;
  1150. new Client<JobDocumentSetMileStone>().Save(milestone, "Uploaded By File Selection");
  1151. progress.Report("Uploading Files");
  1152. List<Document> documents = new List<Document>();
  1153. foreach (var filename in filenames)
  1154. {
  1155. var data = File.ReadAllBytes(filename);
  1156. Document document = new Document()
  1157. {
  1158. FileName = Path.GetFileName(filename).ToLower(),
  1159. Data = data,
  1160. CRC = CoreUtils.CalculateCRC(data),
  1161. TimeStamp = new FileInfo(filename).LastWriteTime
  1162. };
  1163. documents.Add(document);
  1164. new Client<Document>().Save(documents, "Uploaded by File Selection");
  1165. }
  1166. progress.Report("Creating File Links");
  1167. List<JobDocumentSetMileStoneFile> files = new List<JobDocumentSetMileStoneFile>();
  1168. foreach (var document in documents)
  1169. {
  1170. JobDocumentSetMileStoneFile file = new JobDocumentSetMileStoneFile();
  1171. file.EntityLink.ID = milestone.ID;
  1172. file.DocumentLink.ID = document.ID;
  1173. files.Add(file);
  1174. }
  1175. new Client<JobDocumentSetMileStoneFile>().Save(files, "Uploaded by File Selection");
  1176. });
  1177. };
  1178. if (grid.EditItems(new[] { set }))
  1179. {
  1180. MessageBox.Show(String.Format("{0} files uploaded", filenames.Length));
  1181. Refresh();
  1182. }
  1183. }
  1184. private void AddDocumentSet()
  1185. {
  1186. Guid folderid = FolderIDs?.FirstOrDefault() ?? Guid.Empty;
  1187. JobDocumentSet set = new JobDocumentSet();
  1188. set.Job.ID = JobID;
  1189. set.Folder.ID = folderid;
  1190. set.Discipline.ID = DisciplineID;
  1191. set.Type.ID = TypeID;
  1192. set.Category.ID = CategoryID;
  1193. set.Area.ID = AreaID;
  1194. var grid = new DynamicDataGrid<JobDocumentSet>();
  1195. if (grid.EditItems(new[] { set }))
  1196. Refresh();
  1197. }
  1198. private void Edit_OnClick(object sender, RoutedEventArgs e)
  1199. {
  1200. if (treeGrid.SelectedItem == null)
  1201. {
  1202. MessageBox.Show("Please choose a Document Set first");
  1203. return;
  1204. }
  1205. Guid[] setIDs = treeGrid.SelectedItems.Select(x => (x as DocumentSetNode).ID).ToArray();
  1206. EditDocumentSets(setIDs);
  1207. }
  1208. private void EditDocumentSets(Guid[] setIDs)
  1209. {
  1210. var sets = new Client<JobDocumentSet>().Query(
  1211. new Filter<JobDocumentSet>(x => x.ID).InList(setIDs)
  1212. ).Rows.Select(x => x.ToObject<JobDocumentSet>()).ToArray();
  1213. var grid = new DynamicDataGrid<JobDocumentSet>();
  1214. // grid.OnCustomiseEditor += (form, items, column, editor) =>
  1215. // {
  1216. // if (String.Equals(column.ColumnName, "Discipline.ID"))
  1217. // editor.Editable = DisciplineVisible ? Editable.Enabled : Editable.Hidden;
  1218. // if (String.Equals(column.ColumnName, "Type.ID"))
  1219. // editor.Editable = TypeVisible ? Editable.Enabled : Editable.Hidden;
  1220. // if (String.Equals(column.ColumnName, "Category.ID"))
  1221. // editor.Editable = CategoryVisible ? Editable.Enabled : Editable.Hidden;
  1222. // if (String.Equals(column.ColumnName, "Area.ID"))
  1223. // editor.Editable = AreaVisible ? Editable.Enabled : Editable.Hidden;
  1224. // };
  1225. if (grid.EditItems(sets))
  1226. UpdateNodes(sets);
  1227. }
  1228. private void UpdateNodes(IEnumerable<JobDocumentSet> sets)
  1229. {
  1230. if (_documentsets == null)
  1231. return;
  1232. foreach (var set in sets)
  1233. {
  1234. var node = _documentsets.GetNode(set.ID);
  1235. if (node != null)
  1236. {
  1237. var tags = new List<String>()
  1238. {
  1239. set.Discipline.Description,
  1240. set.Type.Description,
  1241. set.Category.Description,
  1242. set.Area.Description
  1243. }.Where(x=>!String.IsNullOrWhiteSpace(x)).Distinct().ToArray();
  1244. JobDocumentSetDescriptionBlock desc = new JobDocumentSetDescriptionBlock(
  1245. set.ID, set.Code, set.Description, tags);
  1246. node.Description = Serialization.Serialize(desc);
  1247. JobDocumentSetDetailsBlock dets = new JobDocumentSetDetailsBlock()
  1248. {
  1249. ID = set.ID,
  1250. Date = set.Date,
  1251. Size = set.Size,
  1252. Scale = set.Scale,
  1253. Employee = set.Employee.Name
  1254. };
  1255. node.Details = Serialization.Serialize(dets);
  1256. }
  1257. }
  1258. }
  1259. private void HideRejected_OnClick(object sender, RoutedEventArgs e)
  1260. {
  1261. _hidesuperceded = !_hidesuperceded;
  1262. HideSupercededLabel.Content = _hidesuperceded ? "Show All" : "Last Only";
  1263. Refresh();
  1264. }
  1265. private void Delete_OnClick(object sender, RoutedEventArgs e)
  1266. {
  1267. if ((treeGrid.SelectedItems == null) || !treeGrid.SelectedItems.Any())
  1268. {
  1269. MessageBox.Show("Please choose a Document Set first");
  1270. return;
  1271. }
  1272. if (MessageBox.Show(
  1273. "Are you sure you wish to delete the selected Document Sets?",
  1274. "Confirm Delete",
  1275. MessageBoxButton.YesNo
  1276. ) != MessageBoxResult.Yes)
  1277. return;
  1278. List<JobDocumentSet> updates = new List<JobDocumentSet>();
  1279. List<DocumentSetNode> orphans = new List<DocumentSetNode>();
  1280. var items = treeGrid.SelectedItems.Select(x => (DocumentSetNode)x).ToArray();
  1281. foreach (DocumentSetNode item in items)
  1282. {
  1283. var children = item.Children.Where(x => !items.Contains(x));
  1284. if (children.Any())
  1285. orphans.AddRange(children);
  1286. }
  1287. if (orphans.Any())
  1288. {
  1289. var confirm = MessageBox.Show(
  1290. "These Document Sets contain children!\nDo you wish to delete these as well?",
  1291. "Delete Children",
  1292. MessageBoxButton.YesNoCancel
  1293. );
  1294. if (confirm == MessageBoxResult.Cancel)
  1295. return;
  1296. if (confirm == MessageBoxResult.No)
  1297. {
  1298. foreach (var orphan in orphans)
  1299. {
  1300. var update = new JobDocumentSet();
  1301. update.ID = orphan.ID;
  1302. update.Parent.ID = Guid.Empty;
  1303. updates.Add(update);
  1304. }
  1305. return;
  1306. }
  1307. }
  1308. Progress.ShowModal("Deleting Document Set",(progress) =>
  1309. {
  1310. if (updates.Any())
  1311. new Client<JobDocumentSet>().Save(updates, "Parent Document Deleted");
  1312. var deletes = items.Select(x=>new JobDocumentSet() { ID = x.ID }).ToArray();
  1313. new Client<JobDocumentSet>().Delete(deletes, "Deleted By User");
  1314. });
  1315. Refresh();
  1316. }
  1317. #endregion
  1318. private void FlatList_OnClick(object sender, RoutedEventArgs e)
  1319. {
  1320. _flatlist = !_flatlist;
  1321. FlatListLabel.Content = _flatlist ? "Tree View" : "Flat List";
  1322. Refresh();
  1323. }
  1324. private void IncludeRetired_OnClick(object sender, RoutedEventArgs e)
  1325. {
  1326. _includeretired = !_includeretired;
  1327. FlatListLabel.Content = _includeretired ? "Active Only" : "Include Retired";
  1328. Refresh();
  1329. }
  1330. private void TreeGrid_OnSelectionChanged(object? sender, GridSelectionChangedEventArgs e)
  1331. {
  1332. //var treeColumn = treeGrid.Columns[e.CurrentRowColumnIndex.ColumnIndex];
  1333. //var column = treeColumn.MappingName.Replace("Blocks[","").Replace("]","");
  1334. // var column = e.Column.MappingName.Replace("Blocks[","").Replace("]","");
  1335. // var data = (e.Record as DocumentSetNode).Blocks[column];
  1336. // if (String.IsNullOrWhiteSpace(data))
  1337. // return;
  1338. //
  1339. // var block = Serialization.Deserialize<JobDocumentSetMileStoneBlock>(data.ToString());
  1340. // Guid id = block.ID;
  1341. }
  1342. private void TreeGrid_OnCurrentCellActivated(object? sender, CurrentCellActivatedEventArgs e)
  1343. {
  1344. var node = treeGrid.CurrentItem as DocumentSetNode;
  1345. if (node == null)
  1346. return;
  1347. var treeColumn = treeGrid.Columns[e.CurrentRowColumnIndex.ColumnIndex];
  1348. var column = treeColumn.MappingName.Replace("Blocks[","").Replace("]","");
  1349. if (!node.Blocks.ContainsKey(column))
  1350. MileStoneSelected(null);
  1351. else
  1352. {
  1353. var block = Serialization.Deserialize<JobDocumentSetMileStoneBlock>(node.Blocks[column]);
  1354. MileStoneSelected?.Invoke(block);
  1355. }
  1356. }
  1357. private void TreeGrid_OnCellDoubleTapped(object? sender, TreeGridCellDoubleTappedEventArgs e)
  1358. {
  1359. var set = e.Record as DocumentSetNode;
  1360. if (set != null)
  1361. EditDocumentSets(new Guid[] { set.ID });
  1362. }
  1363. }
  1364. }