Odyssey
cutrustdetail.prg
1 <?php
2  /*
3  * Script: cutrustdetail.v2
4  *
5  * Purpose: This script is used to assign values for a credit union using a
6  * trusted vendor. Things such as billpay values and other SSO
7  * The user will need to select a trustedid, which is setup in the
8  * cutrustedmaster table. They are then allowed to enter the options
9  * for each of the fields.
10  *
11  * Parameters:
12  * user_name - This is the cu code coming from the menu
13  *
14  */
15 
16 $monLibrary= dirname(__FILE__) . "/../library";
17 require_once("$monLibrary/cu_top.i");
18 require_once("$monLibrary/ck_hticket.i");
19 require_once("$monLibrary/monitorView.i");
20 require_once("$sharedLibrary/commonJsFunctions.i");
21 
22  if (!CheckPerm($link, $Hu, "cutrustdetail", $_SERVER['REMOTE_ADDR'])) {
23  // ** Permissions failed
24  // ** redirect to new page
25  header("Location: /hcuadm/hcu_noperm.prg");
26  exit;
27  }
28 
29 // Globals
30 $dataFile= "cutrustdetail.data";
31 $dataPath= "/hcuadm/$dataFile";
32 $showSQL= false;
33 
34 dms_import_v2($DATA_PARAMETERS, "TOP_LEVEL", array("user_name" => "string"));
35 $cu= strtoupper($DATA_PARAMETERS["TOP_LEVEL"]["user_name"]);
36 
37 printMonitorPageTop("CU trust detail for $cu", $homecuKendoVersion, $cloudfrontDomainName);
38 ?>
39 
40 <style>
41  .popupForm .grid_12 {
42  margin-bottom: 5px;
43  }
44 
45  .k-grid tbody .k-button {
46  min-width: 20px !important; /* override */
47  }
48 
49  #defaultValueGrid thead {
50  display: none;
51  }
52 
53  #defaultValueGrid tbody tr {
54  height: 50px;
55  }
56 
57  #defaultValueGrid .k-grid-customDelete {
58  position: relative;
59  left: -5px;
60  }
61 
62  .homecu-formStatus {
63  margin: 5px;
64  padding: 5px;
65  }
66 
67  #accordion {
68  border-color: rgb(204, 204, 204);
69  background-color: rgb(238, 238, 238);
70  border-radius: 6px;
71  color: rgb(51, 131, 187) !important;
72  display: block;
73  font-family: 'Trebuchet MS', 'Lucida Grande', Verdana, Arial, sans-serif;
74  font-size: 11px;
75  font-weight: bold;
76  }
77 
78  #deleteDialogWindow .message {
79  text-align: center;
80  }
81 
82  #deleteDialogWindow .k-edit-buttons {
83  text-align: center;
84  }
85 
86  .k-context-menu .k-item, .k-context-menu {
87  width: 250px !important;
88  text-overflow: ellipsis;
89  }
90 
91  .helpCell:hover, .k-grid-help:hover {
92  text-decoration: underline;
93  cursor: help;
94  }
95 </style>
96 
97 <script type="text/javascript">
98 
99 <?php
100 // Include common functions
101 getErrorsAreShownFunction();
102 getContextMenuForDoubleLevelGrid();
103 getAddMinifiedActionStyleFunction();
104 getParseCrudGridFunction();
105 getEscapeHTMLFunction();
106 getShowSQLFunction($showSQL);
107 getSetupValidatorInGridFunction();
108 getUseValidatorInGridFunction();
109 getShowWaitFunctions();
110 ?>
111 
112 <?php
113 /**
114  * Initializes the top level grid. You are able to add unused trusted details which will pull in all the details from the master.
115  */ ?>
116 var dataItemToDelete= null;
117 var detailRecords= [];
118 var trustedIdDetailDDLData= {};
119 function initGrid()
120 {
121 
122  var initialized= false;
123 
124  var grid= $("#grid").kendoGrid({
125  dataSource: {
126  transport: {
127  read: {
128  url: "<?php echo $dataPath; ?>?operation=readTrustedVendors&cu=<?php echo $cu; ?>",
129  dataType: "json",
130  type: "POST"
131  },
132  create: {
133  url: "<?php echo $dataPath; ?>?operation=createTrustedId&cu=<?php echo $cu; ?>",
134  dataType: "json",
135  type: "POST"
136  },
137  parameterMap: function(data, type) {
138  showWaitWindow();
139  if (type == "read")
140  return {};
141  else if (type = "create")
142  return data;
143  }
144  },
145  schema: {
146  model: {
147  id: "trustedId",
148  fields: {
149  trustedId: {type: "string"},
150  hasMasterRecord: {type: "boolean", defaultValue: true},
151  masterDetailLength: {type: "number"}
152  }
153  },
154  parse: function (data)
155  {
156  hideWaitWindow();
157  if (data.operation == "read")
158  {
159  trustedIdDetailDDLData= data.trustedIdDetailDDL;
160  detailRecords= data.detailRecords;
161  $("#grid .k-grid-add").addClass("topLevel");
162  }
163 
164  <?php // Gets the trusted ids from an associate array that are not used. Associate array is necessary because array also contains the valid properties of the trustedId referenced later. ?>
165  var availableTrustedIds= Object.keys(trustedIdDetailDDLData);
166  var gridData= grid.dataSource.data();
167  var trustedIdsInGrid= {};
168  for (var i=0; i!= gridData.length; i++)
169  trustedIdsInGrid[gridData[i].trustedId]= true;
170  availableTrustedIds= $.grep(availableTrustedIds, function (n,i) { return typeof(trustedIdsInGrid[n]) == "undefined"; });
171 
172  if (availableTrustedIds.length == 0)
173  $("#grid .k-grid-add.topLevel").hide();
174  else
175  $("#grid .k-grid-add.topLevel").show();
176  showWarnings(data, "warningNotification");
177  showSQL(data);
178 
179  if (data.operation == "read")
180  {
181  $("#invalidRequestServerSide").val(errorsAreShown(data, "formValidateMainDiv"));
182  return data.topRecords;
183  }
184  else
185  {
186  $("#invalidRequestServerSide").val(errorsAreShown(data, "formValidatePopupDiv"));
187  for(var i=0; i!= data.bottomRecords.length; i++)
188  {
189  detailRecords.push(data.bottomRecords[i]);
190  }
191  return data.record;
192  }
193  }
194  },
195  sort: {field: "trustedId", dir: "asc"}
196  },
197  columns: [
198  {field: "hasMasterRecord", hidden: true},
199  {field: "trustedId", title: "Trusted ID",
200  template: function (dataItem) {
201  if (dataItem.hasMasterRecord)
202  return dataItem.trustedId;
203  else
204  return "*** " + dataItem.trustedId + " *** DO NOT USE: NO MASTER RECORD ***";
205  }
206  }
207  ],
208  sortable: true,
209  filterable: {extra: false},
210  toolbar: [{name: "create", text: ""}],
211  detailTemplate: "<div class=\"detailGrid\"></div>",
212  detailInit: function (eOuter) {
213  initDetailGrid(eOuter, trustedIdDetailDDLData[eOuter.data.trustedId]);
214  },
215  editable: {
216  mode: "popup",
217  template: kendo.template($("#popupTemplate").html()),
218  window: {
219  draggable: false,
220  resizable: false,
221  title: "Add Trusted Id",
222  actions: [],
223  width: 400
224  }
225  },
226  edit: function (eOuter) {
227  setupValidatorInGrid(eOuter, false);
228  var availableTrustedIds= Object.keys(trustedIdDetailDDLData);
229  var gridData= grid.dataSource.data();
230  var trustedIdsInGrid= {};
231  for (var i=0; i!= gridData.length; i++)
232  trustedIdsInGrid[gridData[i].trustedId]= true;
233  availableTrustedIds= $.grep(availableTrustedIds, function (n,i) { return typeof(trustedIdsInGrid[n]) == "undefined"; });
234 
235  var trustedIdDDL= $("#nameDDL").kendoDropDownList({
236  dataSource: {
237  data: availableTrustedIds
238  },
239  filter: "startswith",
240  dataBound: function () {
241  eOuter.model.trustedId= this.value();
242  eOuter.model.masterDetails= kendo.stringify(trustedIdDetailDDLData[this.value()]);
243  },
244  change: function () {
245  eOuter.model.trustedId= this.value();
246  eOuter.model.masterDetails= kendo.stringify(trustedIdDetailDDLData[this.value()]);
247  }
248  }).data("kendoDropDownList");
249  },
250  save: function (eOuter) {
251  useValidatorInGrid(eOuter, false);
252  },
253  dataBound: function () {
254  fullContextMenu.gridDatabound (true, initialized, grid);
255  if (!initialized)
256  {
257  addMinifiedActionStyle("#grid", true);
258  initialized= true;
259  }
260  },
261  cancel: function (eOuter) {
262  fullContextMenu.gridCancel(eOuter, grid, true);
263  grid.cancelRow();
264  eOuter.preventDefault();
265  return false;
266  },
267  detailExpand: function(e) {
268  $(e.detailRow).find(".detailGrid").data("kendoGrid").dataSource.read(); // Doing autoBind false on the detail grid and that is so it shows the new records on a trusted detail create.
269  }
270  }).data("kendoGrid");
271 
272  fullContextMenu= new getFullContextMenu(grid, "contextMenu", "trustedId", "property");
273 
274  fullContextMenu.setOptionFuncs(masterOptionFunc, detailOptionFunc, additionalSelectOptionFunc);
275  fullContextMenu.setDeleteOptions("<?php echo $dataPath; ?>", "removeTrustedId&cu=<?php echo $cu; ?>", "removeTrustedDetail&cu=<?php echo $cu; ?>", function(isMaster, dataItemForDelete) {
276  if (isMaster)
277  {
278  var data= grid.dataSource.data().slice(0);
279  data= $.grep(data, function(n,i) { return n.trustedId != dataItemForDelete.trustedId; });
280  detailRecords= $.grep(detailRecords, function(n,i) { return n.trustedId != dataItemForDelete.trustedId; });
281  $(".detailGrid:visible").each(function () { $(this).data("kendoGrid").dataSource.read(); });
282  grid.dataSource.data(data);
283  }
284  else
285  {
286  detailRecords= $.grep(detailRecords, function(n,i) { return n.trustedId == dataItemForDelete.trustedId && n.property == dataItemForDelete.property;}, true);
287  $(".detailGrid:visible").each(function () { $(this).data("kendoGrid").dataSource.read(); });
288  }
289  }, function(dataItem) {
290  return $.grep(detailRecords, function(n,i) { n.trustedId == dataItem.trustedId; }).length;
291  });
292 
293  fullContextMenu.setLimitFromDatabound(["click"]);
294 }
295 
296 <?php
297 /**
298  * function masterOptionFunc(options, dataItem)
299  * Adds in the options to the context menu on the top level.
300  *
301  * @param array options -- the menu items
302  * @param object dataItem -- the data for the grid line item
303  */
304 ?>
305 function masterOptionFunc(options, dataItem) {
306  options.push({text: "Delete", cssClass: "deleteLi"});
307 
308  if (typeof(trustedIdDetailDDLData[dataItem.trustedId]) != "undefined")
309  {
310  var availableTrustedIds= Object.keys(trustedIdDetailDDLData[dataItem.trustedId]);
311  var gridData= $.grep(detailRecords, function(n,i) { return n.trustedId == dataItem.trustedId; });
312  var trustedIdsInGrid= {};
313  for (var i=0; i!= gridData.length; i++)
314  trustedIdsInGrid[gridData[i].property]= true;
315  availableTrustedIds= $.grep(availableTrustedIds, function (n,i) { return typeof(trustedIdsInGrid[n]) == "undefined"; });
316 
317  if (dataItem.hasMasterRecord && availableTrustedIds.length > 0)
318  {
319  options.push({text: "Add detail", cssClass: "addDetailLi"});
320  options.push({text: "Pull unused master properties", cssClass: "pullLi"});
321  }
322  }
323 
324 }
325 
326 <?php
327 /**
328  * function detailOptionFunc(options, dataItem)
329  * Adds in the options for the context menu on the bottom level.
330  *
331  * @param array options -- the menu items
332  * @param object dataItem -- the data for the grid line item
333  */
334 ?>
335 function detailOptionFunc(options, dataItem) {
336  options.push({text: "Delete", cssClass: "deleteLi"});
337  var data= $.grep(detailRecords, function(n,i) { return n.trustedId == dataItem.trustedId; });
338  data.sort(function(a,b) { return a.displayOrder - b.displayOrder; });
339 
340  if (data.length >= 2) // Will need at least two tasks for move before and move after to make sense.
341  {
342  var moveBeforeChoices= [];
343  var moveAfterChoices= [];
344  this.taskIndex= -1;
345  for (var i=0; i!= data.length; i++)
346  {
347  moveBeforeChoices.push({text: data[i].property, cssClass: "moveBeforeChoiceLi task_"+i});
348  moveAfterChoices.push({text: data[i].property, cssClass: "moveAfterChoiceLi task_"+i});
349  if (data[i].property == dataItem.property)
350  this.taskIndex= i;
351  }
352 
353  if (data.length == 2)
354  {
355  this.taskIndex == 0 ? moveBeforeChoices= [] : moveBeforeChoices.splice(this.taskIndex, 1);
356  this.taskIndex == 1 ? moveAfterChoices= [] : moveAfterChoices.splice(this.taskIndex, 1);
357  }
358  else
359  {
360  moveBeforeChoices.splice(this.taskIndex, 2);
361  this.taskIndex == 0 ? moveAfterChoices.splice(0, 1) : moveAfterChoices.splice(this.taskIndex-1, 2);
362  }
363 
364  if (moveBeforeChoices.length > 0)
365  options.push({text: "Move before...", items: moveBeforeChoices, cssClass: "moveBeforeLi"});
366  if (moveAfterChoices.length > 0)
367  options.push({text: "Move after...", items: moveAfterChoices, cssClass: "moveAfterLi"});
368  }
369 
370  if (dataItem.hasMasterRecord)
371  {
372  options.unshift({text: "Edit", cssClass: "editLi"});
373  }
374 }
375 
376 <?php
377 /**
378  * function additionalSelectOptionFunc(item, tr, grid, dataItem)
379  * Adds in logic when a menu item is clicked
380  *
381  * @param HTMLElement item -- represents the li clicked on the menu.
382  * @param HTMLElement tr -- represents the tr the menu is for.
383  * @param object dataItem -- the data for the tr in the grid.
384  */
385 ?>
386 function additionalSelectOptionFunc(item, tr, grid, dataItem)
387 {
388  if ($(item).hasClass("pullLi"))
389  {
390  var popup= $("#pullDownPopup").data("kendoWindow");
391  var pullDownGrid= $("#pullDownGrid").data("kendoGrid");
392  var popupNoTemplates= $("#noTemplatesPopup").data("kendoWindow");
393  if (popup == null)
394  {
395  popup= $("<div id='pullDownPopup'></div>").kendoWindow({
396  content: {
397  template: kendo.template($("#pullTemplate").html())
398  },
399  modal: true,
400  title: "Pull Down Properties",
401  visible: false,
402  actions: [],
403  draggable: false,
404  resizable: false,
405  width: 400
406  }).data("kendoWindow");
407 
408  popupNoTemplates= $("<div id='noTemplatesPopup'></div>").kendoWindow({
409  content: {
410  template: kendo.template($("#pullInNoTemplates").html())
411  },
412  modal: true,
413  title: "No Templates",
414  visible: false,
415  actions: [],
416  draggable: false,
417  resizable: false,
418  width: 400
419  }).data("kendoWindow");
420 
421  pullDownGrid= $("#pullDownGrid").kendoGrid({
422  dataSource: {
423  data: [],
424  schema: {
425  model: {
426  id: "property",
427  fields: {
428  property: {type: "string"},
429  value: {type: "string"},
430  fieldType: {type: "string"},
431  message: {type: "string"}
432  }
433  }
434  }
435  },
436  columns: [
437  {title: "<input type='checkbox' id='allCheckbox'>", template: "<input type='checkbox' class='rowCheckbox'>", width: "50px"},
438  {field: "property", title: "Property"},
439  ],
440  autoBind: false
441  }).data("kendoGrid");
442 
443  $("#allCheckbox").click(function () {
444  $(".rowCheckbox").prop("checked", $(this).prop("checked"));
445  });
446 
447 
448 
449  $("#pullDownPopup .cancelBtn").click(function () {
450  popup.close();
451  return false;
452  });
453 
454  $("#noTemplatesPopup .okayBtn").click(function () {
455  popupNoTemplates.close();
456  return false;
457  });
458  }
459 
460  $("#pullDownPopup .okayBtn").off("click"); // Make sure that the variables don't stagnate to the first trustedId.
461  $("#pullDownPopup .okayBtn").on("click", function() {
462  var properties= [];
463  $(".rowCheckbox:checked").each(function () {
464  properties.push(pullDownGrid.dataItem($(this).parent().parent()));
465  });
466 
467  showWaitWindow();
468  $.post("<?php echo $dataPath; ?>?operation=pullDownOptions&cu=<?php echo $cu; ?>", {trustedId: dataItem.trustedId, options: kendo.stringify(properties)}, function (data) {
469  hideWaitWindow();
470 
471  showSQL(data);
472  if (!errorsAreShown(data, "formValidatePullDownDiv"))
473  {
474  for (var i=0; i!= data.record.length; i++)
475  detailRecords.push(data.record[i]);
476  $(".detailGrid:visible").each(function () { $(this).data("kendoGrid").dataSource.read(); });
477  popup.close();
478  }
479  });
480  });
481 
482  var gridData= grid.dataSource.data();
483  var inMaster= trustedIdDetailDDLData[dataItem.trustedId];
484  var lookup= {};
485  var data= [];
486  var gridIndex= null;
487 
488  var properties= $.grep(detailRecords, function(n,i) { return n.trustedId == dataItem.trustedId; });
489  for(var j=0; j!= properties.length; j++)
490  lookup[properties[j].property]= true;
491  for (var prop in inMaster)
492  {
493  if (typeof(lookup[prop]) == "undefined")
494  data.push({property: prop, value: inMaster[prop].defaultValue, fieldType: inMaster[prop].fieldType, message: inMaster[prop].message});
495  }
496 
497  if (data.length > 0)
498  {
499  $("#allCheckbox").prop("checked", false);
500  pullDownGrid.dataSource.data(data);
501  popup.open().center();
502  }
503  else
504  {
505  popupNoTemplates.open().center();
506  }
507  }
508  else if ($(item).hasClass("moveAfterChoiceLi") || $(item).hasClass("moveBeforeChoiceLi"))
509  {
510  var classes= $(item).attr("class").split(/\s+/);
511  var moveTaskId= 0;
512  for(var i=0; i!= classes.length; i++)
513  {
514  var parts= classes[i].split(/_/);
515  if (parts[0].trim() == "task")
516  {
517  moveTaskId= Number(parts[1]);
518  break;
519  }
520  }
521 
522  if ($(item).hasClass("moveAfterChoiceLi"))
523  moveTaskId++;
524  $.post("<?php echo $dataPath; ?>?operation=reorderProperties&cu=<?php echo $cu; ?>", {position: moveTaskId, trustedId: dataItem.trustedId, property: dataItem.property}, function (data) {
525  hideWaitWindow();
526  showSQL(data);
527  if (!errorsAreShown(data, "formValidateMainDiv"))
528  {
529  detailRecords= $.grep(detailRecords, function(n,i) { return n.trustedId != dataItem.trustedId; });
530  for(var i=0, length= data.record.length; i!= length; i++)
531  {
532  detailRecords.push(data.record[i]);
533  }
534  grid.dataSource.read();
535  $("#grid").data("kendoGrid").expandRow($(tr).closest(".k-detail-row").prev()); // The outer grid collapses normally but need to still show the grid changed.
536  }
537  });
538  }
539 }
540 
541 <?php
542 /**
543  * function showWarnings(data, warningNotification)
544  * Shows a warning when a trustedId's properties cannot be decoded properly.
545  *
546  * @param object data -- the data from the data call that contains the warning property.
547  * @param string warningNotification -- the id of the warning notification.
548  */
549 ?>
550 function showWarnings(data, warningNotification)
551 {
552  if (typeof(data.warning) != "undefined" && data.warning.length > 0)
553  {
554  var notification= $("#"+warningNotification).data("kendoNotification");
555  if (notification == null)
556  {
557  notification = $("<div id='"+warningNotification+"'></div>").kendoNotification({
558  position: {
559  pinned: true,
560  top: 18,
561  left: 128
562  }
563  }).data("kendoNotification");
564  }
565  var warningLine= "<b>Warnings:</b><br>";
566  for (var i=0; i!= data.warning.length; i++)
567  warningLine+= data.warning[i]+ "<br>";
568  notification.warning(warningLine);
569  }
570 }
571 
572 <?php
573 /**
574  * function helpDataBound(grid, isCancel)
575  * Adds logic to open the help on the grid databound function.
576  *
577  * @param KendoGrid grid -- the bottom level grid
578  * @param boolean isCancel -- need to clear the events on a cancel event.
579  */
580 ?>
581 function helpDataBound(grid, isCancel)
582 {
583  if (isCancel)
584  $(grid.tbody).find(".k-grid-help").off("click mouseenter mouseleave");
585  $(grid.tbody).find(".k-grid-help").on("click", function () {
586  openHelpPopup($(this), grid.dataItem($(this).parent().parent()).message);
587  return false;
588  });
589 
590  $(grid.tbody).find(".k-grid-help").on("mouseenter", function () {
591  $(this).data("timer", setTimeout($.proxy(function() { openHelpPopup($(this), grid.dataItem($(this).parent().parent()).message)}, $(this)), 3000));
592  return false;
593  });
594 
595  $(grid.tbody).find(".k-grid-help").on("mouseleave", function () {
596  clearTimeout($(this).data("timer"));
597  return false;
598  });
599 }
600 
601 <?php
602 /**
603  * function openHelpPopup(td, message)
604  * Opens the help popup
605  *
606  * @param HTMLElement td -- the table cell of the help popup
607  * @param string message -- the message to display in an info popup
608  */
609 ?>
610 function openHelpPopup(td, message)
611 {
612  clearTimeout($(td).data("timer"));
613  if ($(td).data("hasNotification") == true)
614  return;
615 
616  var notification= $("<div></div>").kendoNotification({
617  position: {
618  pinned: false,
619  top: $(td).offset().top - $("body").scrollTop(),
620  left: $(td).offset().left - $("body").scrollLeft()
621  },
622  autoHideAfter: 0,
623  show: function ()
624  {
625  $(td).data("hasNotification", true);
626  $(".k-notification:last").mouseleave(function () {
627  notification.hide();
628  });
629  },
630  hide: function ()
631  {
632  $(td).data("hasNotification", false);
633  }
634  }).data("kendoNotification");
635 
636  if (message == null || message == "")
637  notification.info("No help available");
638  else
639  notification.info(message);
640 }
641 
642 <?php
643 /**
644  * function initDetailGrid(eOuter, fieldTypeDDLData)
645  * Initializes the bottom-level Grid
646  *
647  * @param object eOuter -- contains data for the master record and other things.
648  * @param object fieldTypeDDLData -- contains the master DDL data.
649  */
650 ?>
651 function initDetailGrid(eOuter, fieldTypeDDLData)
652 {
653  var detailGridInitialized= false;
654  var trustedId= eOuter.data.trustedId;
655  var detailGridSelector= eOuter.detailRow.find(".detailGrid");
656 
657  var nextDisplayOrderNum= $.grep(detailRecords, function(n,i) { return n.trustedId == trustedId; }).length;
658 
659  var detailGrid= eOuter.detailRow.find(".detailGrid").kendoGrid({
660  dataSource: {
661  transport: {
662  read: function (options) {
663  options.success(detailRecords);
664  },
665  create: function (options) {
666  showWaitWindow();
667  var parameters= {trustedId: trustedId, property: options.data.property, value: options.data.value, fieldType: options.data.fieldType, message: options.data.message};
668  $.post("<?php echo $dataPath; ?>?operation=addTrustedDetail&cu=<?php echo $cu; ?>", parameters, function(data) {
669  hideWaitWindow();
670  showSQL(data);
671 
672  if (!errorsAreShown(data, "formValidatePopupDetailDiv"))
673  {
674  data.record[0].displayOrder= nextDisplayOrderNum++;
675  options.success(data.record);
676  detailRecords.push(data.record[0]);
677  }
678  });
679  },
680  update: function (options) {
681  showWaitWindow();
682  var parameters= {trustedId: trustedId, property: options.data.property, value: options.data.value, fieldType: options.data.fieldType, message: options.data.message};
683  $.post("<?php echo $dataPath; ?>?operation=updateTrustedDetail&cu=<?php echo $cu; ?>", parameters, function (data) {
684  hideWaitWindow();
685  showSQL(data);
686  if (!errorsAreShown(data, "formValidatePopupDetailDiv"))
687  {
688  options.success(data.record);
689  }
690  });
691  }
692  },
693  schema: {
694  model: {
695  id: "pid",
696  fields: {
697  pid: {type: "number"},
698  property: {type: "string"},
699  value: {type: "string"},
700  fieldType: {type: "string"},
701  hasMasterRecord: {type: "boolean", defaultValue: eOuter.data.hasMasterRecord},
702  message: {type: "string"},
703  trustedId: {type: "string"},
704  displayOrder: {type: "number"}
705  }
706  }
707  },
708  sort: {field: "displayOrder", dir: "asc"},
709  filter: {field: "trustedId", operator: "eq", value: eOuter.data.trustedId}
710  },
711  editable: {
712  mode: "popup",
713  template: kendo.template($("#popupTemplateDetail").html()),
714  window: {
715  draggable: false,
716  resizable: false,
717  actions: [],
718  width: 400,
719  autoFocus: false
720  }
721  },
722  columns: [
723  {field: "hasMasterRecord", hidden: true, attributes: {"class" : "detailHasMasterRecord"}},
724  {command: [{className: "k-grid-help k-info-colored", name: "help", text: "", iconClass: "k-icon k-i-info"}], attributes: {"class" : "helpCell"}, width: "50px"},
725  {field: "property", title: "Property"},
726  {field: "value", title: "Value"}
727  ],
728  sortable: true,
729  filterable: {extra: false},
730  autoBind: false,
731  edit: function (eInner) {
732  setupValidatorInGrid(eInner, true);
733 
734  var fieldType= "string";
735 
736  $("#valueTextInput").blur(function () {
737  eInner.model.dirty= true;
738  eInner.model.value= $(this).val();
739  });
740 
741  $("#valueBooleanInput").click(function () {
742  eInner.model.dirty= true;
743  eInner.model.value= $(this).prop("checked") ? "Y" : "N";
744  });
745 
746  var valueNumericInput= $("#valueNumericInput").kendoNumericTextBox({
747  change: function () {
748  eInner.model.dirty= true;
749  eInner.model.value= this.value();
750  }
751  }).data("kendoNumericTextBox");
752 
753  var index= 0;
754  var inEditCell= false;
755  var valueGrid= $("#valueGrid").kendoGrid({
756  dataSource: {
757  data: [],
758  schema: {
759  model: {
760  id: "index",
761  fields: {
762  index: {type: "number"},
763  value: {type: "string"}
764  }
765  }
766  }
767  },
768  columns: [
769  {field: "index", hidden: true},
770  {command: [{name: "customDelete", text: "", iconClass: "k-icon k-i-delete", click: function(e) {
771  eInner.model.dirty= true;
772  getValueFromGrid(eInner, e, valueGrid, true);
773  return false;
774  }}], width: "45px"},
775  {field: "value"}
776  ],
777  editable: {
778  mode: "incell",
779  confirmation: false
780  },
781  edit: function (eGrid) {
782  if (eGrid.model.isNew())
783  eGrid.model.index= ++index;
784  },
785  save: function (eGrid) {
786  eInner.model.dirty= true;
787  getValueFromGrid(eInner, eGrid, this, false);
788  },
789  dataBound: function () {
790  addMinifiedActionStyle("#valueGrid", true);
791  }
792  }).data("kendoGrid");
793 
794  if (eInner.model.isNew())
795  {
796  $(eInner.container).data("kendoWindow").title("Add Detail");
797  $("#propertyLabel").remove();
798 
799  var addedProperties= {};
800  var data= $.grep(detailRecords, function(n,i) { return n.trustedId == eOuter.data.trustedId; });
801  for (var i=0; i!= data.length; i++)
802  {
803  addedProperties[data[i].property]= true;
804  }
805 
806  var propertyDDL= [];
807  for(var prop in fieldTypeDDLData)
808  {
809  if (typeof(addedProperties[prop]) == "undefined")
810  {
811  var r= fieldTypeDDLData[prop];
812  propertyDDL.push({fieldType: r.fieldType, defaultValue: r.defaultValue, propertyName: prop, message: r.message});
813  }
814  }
815  // Create dropDownList and then select the field type from the first that is defaultly selected.
816  var propertyDDL= $("#propertyDDL").kendoDropDownList({
817  dataSource: {
818  data: propertyDDL,
819  schema: {
820  model: {
821  id: "propertyName",
822  fields: {
823  propertyName: {type: "string"},
824  defaultValue: {type: "string"},
825  fieldType: {type: "string"},
826  message: {type: "string"}
827  }
828  }
829  }
830  },
831  dataTextField: "propertyName",
832  dataValueField: "propertyName",
833  filter: "startswith",
834  dataBound: function (e) {
835  var dataItem= this.dataItem();
836 
837  if (dataItem != null)
838  {
839  eInner.model.property= this.value();
840  eInner.model.fieldType= dataItem.fieldType;
841  eInner.model.message= dataItem.message;
842  fieldTypeChange(eInner, valueGrid, valueNumericInput, dataItem.fieldType, true, dataItem.defaultValue);
843  }
844 
845  },
846  change: function (e) {
847  var dataItem= this.dataItem();
848 
849  if (dataItem != null)
850  {
851  eInner.model.property= this.value();
852  eInner.model.fieldType= dataItem.fieldType;
853  eInner.model.message= dataItem.message;
854  fieldTypeChange(eInner, valueGrid, valueNumericInput, dataItem.fieldType, true, dataItem.defaultValue);
855  }
856 
857  }
858  }).data("kendoDropDownList");
859  }
860  else
861  {
862  $(eInner.container).data("kendoWindow").title("Update Detail");
863  $("#propertyDDL").remove();
864  fieldTypeChange(eInner, valueGrid, valueNumericInput, eInner.model.fieldType, false);
865  }
866  },
867  save: function (eInner) {
868  useValidatorInGrid(eInner, true);
869  },
870  dataBound: function (eInner)
871  {
872  helpDataBound(this, false);
873 
874  $(this.tbody).find("tr .detailHasMasterRecord:contains(true)").each(function () {
875  $(this).parent().find("td:not(.helpCell)").on("click", function () {
876  fullContextMenu.gridForMenu= detailGrid;
877  fullContextMenu.dataItemForMenu= detailGrid.dataItem($(this).parent());
878  fullContextMenu.rowForMenu= $(this).parent();
879  detailGrid.editRow($(this).parent());
880  return false;
881  });
882  });
883  },
884  cancel: function (eInner) {
885  fullContextMenu.gridCancel(eInner, this, false);
886 
887  $(this.tbody).find("tr td:not(.helpCell)").off("click");
888  $(this.tbody).find("tr .detailHasMasterRecord:contains(true)").each(function () {
889  $(this).parent().find("td:not(.helpCell)").on("click", function () {
890  fullContextMenu.gridForMenu= detailGrid;
891  fullContextMenu.dataItemForMenu= detailGrid.dataItem($(this).parent());
892  fullContextMenu.rowForMenu= $(this).parent();
893  detailGrid.editRow($(this).parent());
894  return false;
895  });
896  });
897 
898  helpDataBound(this, true);
899  this.cancelChanges();
900  eInner.preventDefault();
901  return false;
902  }
903  }).data("kendoGrid");
904 
905  fullContextMenu.addToDetailGrid (eOuter.data.trustedId, detailGrid);
906 }
907 
908 <?php
909 /**
910  * function getValueFromGrid(eInner, eGrid, grid, isDeleting)
911  * When type of "list" is selected, this gets the value from the grid.
912  *
913  * @param object eInner -- contains the data and other things for the detail record
914  * @param object eGrid -- the e argument inside of the value grid
915  * @param KendoGrid grid -- the detail grid
916  * @param boolean isDeleting -- if true, then the value is being removed from the grid
917  */
918 ?>
919 function getValueFromGrid(eInner, eGrid, grid, isDeleting)
920 {
921  eInner.model.dirty= true;
922  var data= grid.dataSource.data();
923 
924  if (isDeleting)
925  {
926  var deletingIndex= Number($(eGrid.target).closest("tr").find("td:eq(0)").text());
927  data= $.grep(data, function(n,i) { return n.index != deletingIndex; });
928  grid.dataSource.data(data);
929  }
930  var valueArray= [];
931  for (var i=0; i!= data.length; i++)
932  {
933  if (!isDeleting && data[i].index == eGrid.model.index)
934  valueArray.push(eGrid.values.value);
935  else
936  valueArray.push(data[i].value);
937  }
938  eInner.model.value= kendo.stringify(valueArray);
939 }
940 
941 <?php
942 /**
943  * function fieldTypeChange(eInner, valueGrid, valueNumericInput, newFieldType, updateModel, defaultValue)
944  * This makes sure that the value is entered in a numeric text box if the type is Numeric for example.
945  *
946  * @param object eInner -- the e argument from the detail grid
947  * @param KendoGrid valueGrid -- the value grid
948  * @param kendoNumericTextBox valueNumericInput -- the numeric text box
949  * @param string newFieldType -- What to change the field to
950  * @param boolean updateModel -- if true, then set the new value in the model
951  * @param string defaultValue -- if updating model, then it also means that the defaultValue is being set.
952  */
953 ?>
954 function fieldTypeChange(eInner, valueGrid, valueNumericInput, newFieldType, updateModel, defaultValue)
955 {
956  switch(newFieldType)
957  {
958  case "digits":
959  $("#valueTextDiv").hide();
960  $("#valueBooleanDiv").hide();
961  $("#valueNumericDiv").show();
962  $("#valueListDiv").hide();
963 
964  if (updateModel)
965  {
966  valueNumericInput.value(defaultValue);
967  eInner.model.value= defaultValue;
968  }
969  else
970  {
971  valueNumericInput.value(eInner.model.value);
972  }
973  valueNumericInput.focus();
974  break;
975  case "string":
976  $("#valueTextDiv").show();
977  $("#valueBooleanDiv").hide();
978  $("#valueNumericDiv").hide();
979  $("#valueListDiv").hide();
980 
981  if (updateModel)
982  {
983  $("#valueTextInput").val(defaultValue);
984  eInner.model.value= defaultValue;
985  }
986  else
987  {
988  $("#valueTextInput").val(eInner.model.value);
989  }
990  $("#valueTextInput").focus();
991  break;
992  case "boolean":
993  $("#valueTextDiv").hide();
994  $("#valueBooleanDiv").show();
995  $("#valueNumericDiv").hide();
996  $("#valueListDiv").hide();
997 
998  if (updateModel)
999  {
1000  if (defaultValue == "Y")
1001  {
1002  $("#valueBooleanInput").prop("checked", true);
1003  eInner.model.value= "Y";
1004  }
1005  else
1006  {
1007  $("#valueBooleanInput").prop("checked", false);
1008  eInner.model.value= "N";
1009  }
1010  }
1011  else
1012  {
1013  $("#valueBooleanInput").prop("checked", eInner.model.value == "Y");
1014  }
1015  $("#valueBooleanInput").focus();
1016  break;
1017  case "list":
1018  $("#valueTextDiv").hide();
1019  $("#valueBooleanDiv").hide();
1020  $("#valueNumericDiv").hide();
1021  $("#valueListDiv").show();
1022 
1023  var valueDecoded= [];
1024  if (updateModel)
1025  {
1026  if (defaultValue != null && defaultValue != "")
1027  {
1028  var value= defaultValue.replace(/\\"/g, '"'); // unescape here so JSON is valid.
1029  valueDecoded= JSON && JSON.parse(value) || $.parseJSON(value);
1030  }
1031  }
1032  else
1033  {
1034  if (eInner.model.value != null)
1035  {
1036  var value= eInner.model.value.replace(/\\"/g, '"'); // unescape here so JSON is valid.
1037  valueDecoded= JSON && JSON.parse(value) || $.parseJSON(value);
1038  }
1039  }
1040 
1041  var gridData= [];
1042  for(var i=0; i!= valueDecoded.length; i++)
1043  {
1044  gridData.push({index: i, value: valueDecoded[i]});
1045  }
1046  valueGrid.dataSource.data(gridData);
1047  break;
1048  }
1049 }
1050 
1051 $(document).ready(function () {
1052  initGrid();
1053 });
1054 </script>
1055 
1056 <script id="deleteConfirmTemplate" type="text/x-kendo-template">
1057  <div class="k-edit-form-container">
1058  <div class="container_12">
1059  <div class="grid_12 message">Are you sure that you want to delete?
1060  <span class="removeDetailWarning" style="display:none;">There are <span>1</span> details that will be deleted as well.</span>
1061  </div>
1062  </div>
1063  <div class="k-edit-buttons k-state-default">
1064  <a class="k-button k-button-icontext deleteConfirmContinueBtn" href="\#">
1065  <span class="k-icon k-i-check"></span>
1066  Continue
1067  </a>
1068  <a class="k-button k-button-icontext deleteConfirmCancelBtn" href="\#">
1069  <span class="k-icon k-i-cancel"></span>
1070  Cancel
1071  </a>
1072  </div>
1073  </div>
1074 </script>
1075 <script id="pullTemplate" type="text/x-kendo-template">
1076  <div id="formValidatePullDownDiv" class="homecu-formStatus k-block k-error-colored" style="display:none;"></div>
1077  <div class="k-edit-form-container">
1078  <div class="container_12">
1079  <div id="pullDownGrid"></div>
1080  </div>
1081  <div class="k-edit-buttons k-state-default">
1082  <a class="k-button k-button-icontext okayBtn" href="\#">
1083  <span class="k-icon k-i-check"></span>
1084  Okay
1085  </a>
1086  <a class="k-button k-button-icontext cancelBtn" href="\#">
1087  <span class="k-icon k-i-cancel"></span>
1088  Cancel
1089  </a>
1090  </div>
1091  </div>
1092 </script>
1093 <script id="popupTemplate" type="text/x-kendo-template">
1094  <div id="formValidatePopupDiv" class="homecu-formStatus k-block k-error-colored" style="display:none;"></div>
1095  <form id="popupForm" class="popupForm" data-role="validator" novalidate>
1096  <div class="container_12">
1097  <div class="grid_12">
1098  <div class="grid_4 alpha">
1099  <label>Id:</label>
1100  </div>
1101  <div class="grid_7">
1102  <div id="nameDDL"></div>
1103  </div>
1104  <div class="grid_1 omega">
1105  &nbsp;
1106  </div>
1107  </div>
1108  </div>
1109  </form>
1110 </script>
1111 <script id="popupTemplateDetail" type="text/x-kendo-template">
1112  <div id="formValidatePopupDetailDiv" class="homecu-formStatus k-block k-error-colored" style="display:none;"></div>
1113  <form id="popupFormDetail" class="popupForm" data-role="validator" novalidate>
1114  <div class="container_12">
1115  <div class="grid_12">
1116  <div class="grid_4 alpha">
1117  <label>Id:</label>
1118  </div>
1119  <div class="grid_7">
1120  <div id="propertyLabel">#: property #</div>
1121  <div id="propertyDDL"></div>
1122  </div>
1123  <div class="grid_1 omega">
1124  &nbsp;
1125  </div>
1126  </div>
1127  <div class="grid_12">
1128  <div class="grid_4 alpha">
1129  <label>Value:</label>
1130  </div>
1131  <div class="grid_7">
1132  <div id="valueTextDiv" style="display: none;">
1133  <textarea class="k-input" id="valueTextInput" style="width: 100%; height: 100px" row=15 col=40></textarea>
1134  </div>
1135  <div id="valueBooleanDiv" style="display: none;">
1136  <input type="checkbox" id="valueBooleanInput">
1137  </div>
1138  <div id="valueNumericDiv" style="display:none;">
1139  <input id="valueNumericInput">
1140  </div>
1141  <div id="valueListDiv" style="display: none;">
1142  <div id="valueGrid" style="width: 100%"></div>
1143  </div>
1144  </div>
1145  <div class="grid_1 omega">
1146  &nbsp;
1147  </div>
1148  </div>
1149  </div>
1150  </form>
1151 </script>
1152 <script id="pullInNoTemplates" type="text/x-kendo-template">
1153  <div class="k-edit-form-container">
1154  <div class="container_12">
1155  <div class="grid_12 message">There are no templates to add.</div>
1156  </div>
1157  <div class="k-edit-buttons k-state-default">
1158  <a class="k-button k-button-icontext okayBtn" href="\#">
1159  <span class="k-icon k-i-check"></span>
1160  Okay
1161  </a>
1162  </div>
1163  </div>
1164 </script>
1165 
1166 <?php printMonitorPageMiddle("CU trust detail for $cu", null, true); ?>
1167 
1168  <div id='hideSubmitWait' style='position:relative; left:-2000px;top:-2000px;'>
1169  <div id='homecuSubmitWait' class='k-block' >
1170  <div class='k-loading-image'></div>
1171  </div>
1172  </div>
1173  <input type="hidden" id="invalidRequestClientSide">
1174  <input type="hidden" id="invalidRequestServerSide">
1175  <div class="container_12">
1176  <div class="grid_12">
1177  <div class="grid_7 alpha omega">
1178  <div id="sqlOutput"></div>
1179  </div>
1180  </div>
1181  <div class="grid_12">
1182  <div class="grid_7 alpha omega">
1183  <div id="formValidateMainDiv" class="k-block k-error-colored" style="display:none;"></div>
1184  </div>
1185  </div>
1186  <div class="grid_12">
1187  <div class="grid_7 alpha omega">
1188  <div id="grid" style="width:100%"></div>
1189  </div>
1190  </div>
1191  </div>
1192 
1193 <?php printMonitorPageBottom(); ?>