Odyssey
userSupportRights.prg
1 <?php
2 /**
3  * @package UserSupport (Subpackage rights)
4  * @author SPB
5  *
6  * This script is run when the user opens up the rights (cubes) card in the user hub. It cannot be run independently of that.
7  */
8 $string= array("filter" => HCUFILTER_INPUT_STRING);
9 HCU_ImportVars($parameters, "a", array("operation" => $string, "payload" => $string, "ft" => $string, "userId" => $string));
10 extract($parameters["a"]);
11 
12 $showSQL= $SYSENV["devmode"];
13 $operation= is_null($operation) ? "" : trim($operation);
14 
15 if ($operation != "")
16 {
17  switch($operation)
18  {
19  case "saveRights":
20  $returnArray= saveUserRights($dbh, $Cu, $userId, $Cn);
21  break;
22  case "deleteRights":
23  $returnArray= deleteRights($dbh, $Cu, $userId, $Cn);
24  break;
25  default: // Won't get here
26  $returnArray= array("error" => array("Operation not specified: '$operation'"), "record" => array(), "sql" => array());
27  }
28 
29  header('Content-type: application/json');
30  if (!$showSQL)
31  unset($returnArray["sql"]);
32  print HCU_JsonEncode($returnArray);
33 }
34 else
35 {
36  try
37  {
38  $userId = HCU_PayloadDecode($Cu, $payload);
39  }
40  catch(exception $e)
41  { ?>
42  <div class='noUserFound'><div>No User Found</div></div>
43  <?php exit;
44  }
45  printPage("$menu_link?ft=$ft", $userId["user_id"], getUserRights($dbh, $Cu, $userId["user_id"], $showSQL));
46 }
47 
48 /**
49  * function getUserRights($dbh, $cuCode, $userId, $showSQL= false)
50  * Gets the user rights
51  *
52  * @param integer $dbh -- the database connection
53  * @param string $cuCode -- the credit union
54  * @param integer $userId -- the user id
55  * @param boolean $showSQL -- turn it off for non devmode.
56  * @return array -- 0 to infinity errors, all SQL, a nonzero code if there is an error. Data is what will show up in the user rights table.
57  */
58 function getUserRights($dbh, $Cu, $userId, $showSQL)
59 {
60  $sqls= array();
61  try
62  {
63  $sql= "select f.feature_code, f.description as fdescription, g.group_name, p.profile_code, least(pr.amount_per_transaction, gr.amount_per_transaction) as max_apt,
64  ur.feature_code as exists, least(pr.amount_per_day, gr.amount_per_day) as max_apd, least(pr.amount_per_month, gr.amount_per_month) as max_apm,
65  least(pr.amount_per_account_per_day, gr.amount_per_account_per_day) as max_apapd, least(pr.count_per_day, gr.count_per_day) as max_cpd, least(pr.count_per_month,
66  gr.count_per_month) as max_cpm, least(pr.count_per_account_per_day, gr.count_per_account_per_day) as max_cpapd, f.limit_type,
67  coalesce(pr.confirm_required, false) or coalesce(gr.confirm_required, false) as bool_max_cr, ur.amount_per_transaction as apt, ur.amount_per_day as apd,
68  ur.amount_per_month as apm, ur.amount_per_account_per_day as apapd, ur.count_per_day as cpd, ur.count_per_month as cpm, ur.count_per_account_per_day as cpapd,
69  ur.deny_create as bool_fcreate, ur.deny_confirm as bool_fconfirm, ur.deny_decline as bool_fdecline,
70  ur.deny_report as bool_freport, coalesce(ur.confirm_required,false) as bool_crequired, ur.deny_access as bool_faccess from
71  (select user_id, group_id from ${Cu}user where user_id= $userId) as u
72  inner join ${Cu}group g on u.group_id = g.group_id
73  inner join cu_profile p on g.profile_id = p.profile_id and p.cu= '$Cu'
74  inner join cu_profilerights pr on p.profile_id = pr.profile_id
75  inner join cu_feature f on pr.feature_code = f.feature_code and f.enabled
76  left join ${Cu}userrights ur on u.user_id= ur.user_id and pr.feature_code = ur.feature_code
77  left join ${Cu}grouprights gr on pr.feature_code = gr.feature_code and g.group_id = gr.group_id";
78  $sqls[]= $sql;
79  $sth= db_query($sql, $dbh);
80  if (!$sth)
81  throw new exception("Select query failed!", 1);
82  $data= array();
83  $unusedFeatures= array();
84  for($i=0; $array= db_fetch_assoc($sth, $i); $i++)
85  {
86  foreach($array as $key => $value)
87  {
88  $value= is_null($value) ? "" : trim($value);
89 
90  if ($key == "exists")
91  $value= $value != "";
92  else if ($key == "bool_crequired" || $key == "bool_max_cr") // Confirm and confirm's higher up value are not inverted.
93  $value= $value == "t";
94  else if (strpos($key, "bool_") !== false)
95  $value= $value != "t"; // Want the opposite. It is deny after all.
96  else if (isset($array["max_${key}"]))
97  {
98  $maxValue= floatval($array["max_${key}"]);
99  $value= floatval($value) >= $maxValue ? "" : $value; // Prevent recording actual values when equal or greater than the limit. Also "Default" is supposed to be the same.
100  }
101  $array[$key]= $value;
102  }
103  $data[]= $array;
104  }
105  $returnArray= array("code" => 0, "error" => array(), "data" => $data, "sql" => $sqls);
106  }
107  catch(exception $e)
108  {
109  $returnArray= array("code" => $e->getCode(), "error" => array($e->getMessage()), "sql" => $sqls);
110  }
111 
112  if (!$showSQL)
113  unset($returnArray["sql"]);
114 
115  return $returnArray;
116 }
117 
118 /**
119  * function saveUserRights($dbh, $Cu, $userId, $Cn)
120  *
121  * This function saves the changes made to the rights grid and propagates them to the database. This expects three json arrays: deletes, updates, and adds. If one or more isn't there,
122  * Then, it isn't a big deal. If all are missing, then nothing is done. The three arrays are validated and if there are no errors, then the changes are made to the database and an audit
123  * record is created.
124  *
125  * @param $dbh -- the database connection
126  * @param $Cu -- the credit union
127  * @param $userId -- the userId to save rights from.
128  * @param $Cn -- the logged in user (For logging the audit record)
129  *
130  * @return array("error" => 0 or 1 errors found, "code" => 0 if successful; nonzero otherwise, "sql" => all $sqls used (except for the audit/update functions))
131  */
132 function saveUserRights($dbh, $Cu, $userId, $Cn)
133 {
134  $sqls= array();
135  $string= array("filter" => HCUFILTER_INPUT_STRING);
136  HCU_ImportVars($parameters, "a", array("deletes" => $string, "updates" => $string, "adds" => $string));
137  extract($parameters["a"]);
138  try
139  {
140  $deletes= is_null($deletes) ? "" : trim($deletes);
141  $updates= is_null($updates) ? "" : trim($updates);
142  $adds= is_null($adds) ? "" : trim($adds);
143 
144  $officialDelete= array();
145  $officialUpdate= array();
146  $officialAdd= array();
147  $notSoOfficialUpdate= array();
148  $notSoOfficialAdd= array();
149  $codes= array();
150  $doSave= false;
151 
152  if ($deletes != "" && $deletes != "[]")
153  {
154  $deletes= HCU_JsonDecode($deletes);
155  if (count($deletes) == 0)
156  throw new exception("Delete array is invalid!", 1);
157  foreach($deletes as $featureCode)
158  {
159  $officialDelete[]= array("_action" => "delete", "user_id" => $userId, "feature_code" => $featureCode);
160  }
161  $doSave= true;
162  }
163 
164  $updateCodes= array();
165  if ($updates != "" && $updates != "[]")
166  {
167  $updates= HCU_JsonDecode($updates);
168  if (count($updates) == 0)
169  throw new exception("Update array is invalid!", 2);
170 
171  foreach($updates as $update)
172  {
173  if (!isset($update["feature_code"]))
174  throw new exception("Update row needs the feature code!", 3);
175  $code= trim($update["feature_code"]);
176  $updateCodes[$code]= true;
177  $code= prep_save($code, 10);
178  $codes[]= "('$code','update')";
179  $update["_action"]= "update";
180  $update["user_id"]= $userId;
181 
182  $notSoOfficialUpdate[]= $update;
183  }
184  $doSave= true;
185  }
186 
187  $addCodes= array();
188  if ($adds != "" && $adds != "[]")
189  {
190 
191  $adds= HCU_JsonDecode($adds);
192  if (count($adds) == 0)
193  throw new exception("Add array is invalid!", 2);
194  foreach($adds as $add)
195  {
196  if (!isset($add["feature_code"]))
197  throw new exception("Add row needs the feature code!", 6);
198  $code= trim($add["feature_code"]);
199  $addCodes[$code]= true;
200  $code= prep_save($code, 10);
201  $codes[]= "('$code', 'add')";
202  $add["_action"]= "create";
203  $add["user_id"]= $userId;
204  $notSoOfficialAdd[]= $add;
205  }
206  $doSave= true;
207  }
208 
209  // Check to see if the feature code is in the right array
210  if (count($codes) > 0)
211  {
212  $sql= "select v.* from ${Cu}userrights r inner join (values " . implode(",", $codes) . ") as v (code, type) on r.feature_code = v.code and r.user_id = $userId";
213  $sqls[]= $sql;
214  $sth= db_query($sql, $dbh);
215  if (!$sth)
216  throw new exception("Check query failed!", 7);
217 
218  $officialUpdate= array();
219  $officialAdd= array();
220 
221  $actualUpdateCodes= array();
222  $actualAddCodes= array();
223 
224  for($i=0; $array= db_fetch_assoc($sth, $i); $i++)
225  {
226  if ($array["type"] == "add")
227  $actualAddCodes[$array["code"]]= true;
228  else
229  $actualUpdateCodes[$array["code"]]= true;
230  }
231 
232  if (count($actualUpdateCodes) == count($updateCodes) && count($actualAddCodes) == 0) // All good. Add codes shouldn't exist. Update codes should exist.
233  {
234  $officialUpdate= $notSoOfficialUpdate;
235  $officialAdd= $notSoOfficialAdd;
236  }
237  else
238  {
239  foreach($notSoOfficialUpdate as $row)
240  {
241  $feature= $row["feature_code"];
242  if (!isset($actualUpdateCodes[$feature]))
243  {
244  $row["_action"]= "create";
245  $officialAdd[]= $row;
246  }
247  else
248  $officialUpdate[]= $row;
249  }
250 
251  foreach($notSoOfficialAdd as $row)
252  {
253  $feature= $row["feature_code"];
254  if (isset($actualAddCodes[$feature]))
255  {
256  $row["_action"]= "update";
257  $officialUpdate[]= $row;
258  }
259  else
260  $officialAdd[]= $row;
261  }
262  }
263  }
264 
265  if ($doSave)
266  {
267  $sql= "select email from cuadminusers where user_name= '$Cn' and cu= '$Cu'";
268  $sqls[]= $sql;
269  $sth = db_query($sql, $dbh);
270  if (!$sth)
271  throw new exception("email query failed!", 9);
272  $email= db_fetch_row($sth)[0];
273 
274  $context= "admin";
275  $script= "userSupportRights.prg";
276  $addr= trim($_SERVER["REMOTE_ADDR"]);
277  $vars= array("cu" => $Cu);
278 
279  if ($deletes != "" && $deletes != "[]")
280  {
281  $official= array("userrights" => $officialDelete);
282  if (DataUserTableUpdate($dbh, $vars, null, $official, $userId, "UR_DELETE", $context, $script, "A", "Rights Delete", $Cn, $email, $addr) === false)
283  throw new exception("Deleting failed!", 10);
284  }
285 
286  if ($updates != "" && $updates != "[]")
287  {
288  $official= array("userrights" => $officialUpdate);
289  if (DataUserTableUpdate($dbh, $vars, null, $official, $userId, "UR_UPDATE", $context, $script, "A", "Rights Update", $Cn, $email, $addr) === false)
290  throw new exception("Updating failed!", 11);
291  }
292 
293  if ($adds != "" && $adds != "[]")
294  {
295  $official= array("userrights" => $officialAdd);
296  if (DataUserTableUpdate($dbh, $vars, null, $official, $userId, "UR_ADD", $context, $script, "A", "Rights Add", $Cn, $email, $addr) === false)
297  throw new exception("Adding failed!", 12);
298  }
299  }
300  }
301  catch(exception $e)
302  {
303  return array("error" => array($e->getMessage()), "code" => $e->getCode(), "sql" => $sqls);
304  }
305  return array("error" => array(), "code" => 0, "sql" => $sqls);
306 }
307 
308 /**
309  * function printPage($self, $userId, $readData)
310  *
311  * Prints all script, templates and HTML necessary to display the rights grid from within the outer tab window.
312  *
313  * @param $self -- The URL of this script
314  * @param $userId -- the userId of the user
315  * @param $readData -- the results of getUserRights. This is to not flicker on load which was caused by the call for the page plus the call for the read.
316  */
317 function printPage($self, $userId, $readData)
318 { ?>
319  <script type="text/javascript">
320  //# sourceURL=rights.js
321 
322  var userSupportContents= {};
323 
324  userSupportContents.labels= [
325  {code: "apt", description: "Amount per transaction", popup: "Transaction", type: "amount"},
326  {code: "apapd", description: "Amount per account per day", popup: "Account / Day", type: "amount"},
327  {code: "apd", description: "Amount per day", popup: "Day", type: "amount"},
328  {code: "apm", description: "Amount per month", popup: "Month", type: "amount"},
329  {code: "cpapd", description: "Count per account per day", popup: "Account / Day", type: "count"},
330  {code: "cpd", description: "Count per day", popup: "Day", type: "count"},
331  {code: "cpm", description: "Count per month", popup: "Month", type: "count"},
332  {code: "bool_faccess", description: "Has access", popup: "Access", type: "boolean", isAccess: true},
333  {code: "bool_fcreate", description: "Create feature", popup: "Create", type: "boolean"},
334  {code: "bool_fconfirm", description: "Needs confirmation", popup: "Confirm", type: "boolean"},
335  {code: "bool_fdecline", description: "Can decline", popup: "Decline", type: "boolean"},
336  {code: "bool_freport", description: "Has Report", popup: "Report", type: "boolean"},
337  {code: "bool_crequired", description: "Confirmation required", popup: "Required", type: "boolean", isConfirmation: true}
338  ];
339  userSupportContents.rightsData= [];
340  userSupportContents.defaultRowTemplate= "";
341 
342  <?php
343  /**
344  * function dirtify(dirty, container)
345  *
346  * This function sets the dirty flag after the cell is changed.
347  *
348  * @param Boolean dirty -- if there are changes or not.
349  * @param HTMLElement container -- this is the cell. (TD tag)
350  */
351  ?>
352  function dirtify(dirty, container)
353  {
354  if (dirty)
355  {
356  $(container).addClass("k-dirty-cell");
357  $(container).prepend("<span class='k-dirty'></span>");
358  }
359  else
360  {
361  $(container).removeClass("k-dirty-cell");
362  $(container).find(".k-dirty").remove();
363  }
364  }
365 
366  <?php
367  /**
368  * function numericEditor(container, options)
369  *
370  * This is a custom editor for numeric fields. This is specifying the limits of the particular feature and if the value goes equal or above the limit, then it is set to null.
371  *
372  * @param HTMLElement container -- this is the cell. (TD tag)
373  * @param {} options -- contains quite a few things. For more info go to the kendo grid API and look at columns.editor.
374  */
375  ?>
376  function numericEditor(container, options, rightsGrid)
377  {
378  if ($(container).hasClass("vsgDisabled"))
379  {
380  rightsGrid.closeCell();
381  return false;
382  }
383  switch(options.model.limit_type)
384  {
385  case "B": break; <?php // "Both" means that DO IT! ?>
386  case "D":
387  if (!$(container).hasClass("bb"))
388  {
389  rightsGrid.closeCell();
390  return;
391  }
392  break;
393  case "Q":
394  if (!$(container).hasClass("cc"))
395  {
396  rightsGrid.closeCell();
397  return;
398  }
399  break;
400  default: <?php // Unknown value. This expects BDQ. ?>
401  rightsGrid.closeCell();
402  return false;
403  break;
404  }
405 
406  var label= null;
407  for(var i=0; i!= userSupportContents.labels.length; i++)
408  {
409  var item= userSupportContents.labels[i];
410  if (item.code == options.field)
411  {
412  label= item;
413  break;
414  }
415  }
416  var max= (options.model["max_"+options.field]+"").trim();
417  max= max == "" ? (label.type == "amount" ? "<?php echo FEATURE_LIMIT_MAX_AMOUNT; ?>" : "<?php echo FEATURE_LIMIT_MAX_COUNT; ?>") : max;
418  max= Math.round(max);
419  var value= options.model[options.field];
420  value= value == null ? null : Math.round(value);
421  var orgValue= options.model["org_"+options.field];
422  var ntb= $("<input>").appendTo(container).kendoNumericTextBox({
423  min: 0,
424  format: label.type == "amount" ? "c0" : "n0",
425  max: max,
426  step: max,
427  value: value,
428  upArrowText: "Set to Default",
429  downArrowText: "Set to 0",
430  change: function() {
431  var value= this.value() >= max ? null : Math.round(this.value());
432 
433  if (orgValue == "")
434  orgValue= null;
435  options.model[options.field]= value;
436  this.value(value);
437  options.model.dirty= orgValue != value;
438 
439  dirtify(options.model.dirty, container);
440  }
441  }).data("kendoNumericTextBox");
442  }
443 
444  <?php
445  /**
446  * function radioClick(value, rightsGrid)
447  *
448  * This is called when one of the radio buttons on the top gets clicked and also on the databound for the grid (booleans are shown by default.)
449  *
450  * @param string value -- the val() of the radio button that is clicked. This will show/hide columns based on the selection.
451  * @param KendoGrid rightsGrid -- the main grid.
452  */
453  ?>
454  function radioClick(value, rightsGrid)
455  {
456  var showBoolean= false, booleanIndex= 2;
457  var showCounts= false, countIndex= 4;
458  var showAmounts= false, amountIndex= 3;
459  switch(value)
460  {
461  case "boolean":
462  showBoolean= true;
463  break;
464  case "count":
465  showCounts= true;
466  break;
467  case "amount":
468  showAmounts= true;
469  break;
470  case "all":
471  showBoolean= true;
472  showAmounts= true;
473  showCounts= true;
474  break;
475  }
476 
477  <?php // There seems to be a bug with hide/show columns after read calls. ?>
478  showBoolean ? $("#userRightsGrid").removeClass("hideAA") : $("#userRightsGrid").addClass("hideAA");
479  showCounts ? $("#userRightsGrid").removeClass("hideCC") : $("#userRightsGrid").addClass("hideCC");
480  showAmounts ? $("#userRightsGrid").removeClass("hideBB") : $("#userRightsGrid").addClass("hideBB");
481 
482  if (typeof(resizingAndWhatnot) == "function")
483  (resizingAndWhatnot)($("#externalTabWindow").data("kendoWindow"));
484  }
485 
486  <?php
487  /**
488  * function clearRecord(item)
489  *
490  * This happens when the user clicks on one of the checkboxes on the second column and updates. That is meant to set everything to "Default" for the row so effectively, it is "cleared."
491  *
492  * @param {} item -- the dataItem in the dataSource to update.
493  */
494  ?>
495  function clearRecord(item)
496  {
497  item.checked= false;
498  item.exists= false;
499  item.apt= null;
500  item.apd= null;
501  item.apm= null;
502  item.apapd= null;
503  item.cpd= null;
504  item.cpm= null;
505  item.cpapd= null;
506  item.bool_faccess= true;
507  item.bool_fcreate= true;
508  item.bool_fconfirm= true;
509  item.bool_fdecline= true;
510  item.bool_freport= true;
511  item.bool_crequired= true;
512  }
513 
514  <?php
515  /**
516  * function createSnapshot(item)
517  *
518  * This sets the original fields to the current value before changes.
519  *
520  * @param {} item -- the dataItem in the dataSource to update.
521  */
522  ?>
523  function createSnapshot(item)
524  {
525  item.org_apt= item.apt;
526  item.org_apd= item.apd;
527  item.org_apm= item.apm;
528  item.org_apapd= item.apapd;
529  item.org_cpd= item.cpd;
530  item.org_cpm= item.cpm;
531  item.org_cpapd= item.cpapd;
532  item.org_bool_faccess= item.bool_faccess;
533  item.org_bool_fcreate= item.bool_fcreate;
534  item.org_bool_fconfirm= item.bool_fconfirm;
535  item.org_bool_fdecline= item.bool_fdecline;
536  item.org_bool_freport= item.bool_freport;
537  item.org_bool_crequired= item.bool_crequired;
538  }
539 
540  <?php
541  /**
542  * function updateGrid(options, rightsGrid)
543  *
544  * This parses the grid and sends the data down to be saved. It is going to iterate and check to see if it is a delete, a update or an add. Then put only the data necessary in those
545  * arrays.
546  *
547  * @param {} options -- all the options: Kendo-defined. For more information, see the kendo dataSource API and look at transport.update function examples.
548  * @param KendoGrid rightsGrid -- the main grid.
549  */
550  ?>
551  function updateGrid(options, rightsGrid)
552  {
553  var deletes= [];
554  var updates= [];
555  var adds= [];
556  var data= options.data.models;
557  for(var i=0; i!= data.length; i++)
558  {
559  var item= data[i];
560  if (item.checked)
561  deletes.push(item.feature_code);
562  else
563  {
564  var record= {feature_code: item.feature_code, amount_per_transaction: item.apt, amount_per_day: item.apd, amount_per_month: item.apm,
565  amount_per_account_per_day: item.apapd, count_per_day: item.cpd, count_per_month: item.cpm, count_per_account_per_day: item.cpapd, deny_report: !item.bool_freport,
566  deny_access: !item.bool_faccess, deny_create: !item.bool_fcreate, deny_confirm: !item.bool_fconfirm, deny_decline: !item.bool_fdecline,
567  confirm_required: item.bool_crequired};
568  if (item.exists)
569  updates.push(record);
570  else
571  adds.push(record);
572  }
573  }
574  if (deletes.length > 0 || updates.length > 0 || adds.length > 0)
575  {
576  var parameters= {userId: "<?php echo $userId; ?>", deletes: kendo.stringify(deletes), updates: kendo.stringify(updates), adds: kendo.stringify(adds)};
577  showWaitWindow();
578  $.post("<?php echo $self; ?>&operation=saveRights", parameters, function(data) {
579  hideWaitWindow();
580  if (data.error.length > 0)
581  {
582  $.homecuValidator.displayMessage(data.error, $.homecuValidator.settings.statusError );
583  options.error(data.error);
584  }
585  else
586  {
587  var data= options.data.models;
588  for(var i=0; i!= data.length; i++)
589  {
590  var item= data[i];
591  item.dirty= false;
592  if (item.checked)
593  clearRecord(item);
594  else
595  item.exists= true;
596  createSnapshot(item);
597  }
598  options.success(data);
599  postPostPostPost();
600  }
601  });
602  }
603  }
604 
605  <?php
606  /**
607  * function openGroupRights()
608  *
609  * This opens the window when the user clicks on the "Open Group's Rights" link. Data is already there from the main read.
610  */
611  ?>
612  function openGroupRights()
613  {
614  var groupRightWindow= $("#groupRightWindow").data("kendoWindow");
615  if (groupRightWindow == null)
616  {
617  groupRightWindow= $("<div id='groupRightWindow'></div>").appendTo("body").kendoWindow({
618  modal: true,
619  title: "Group Rights",
620  visible: false,
621  resizable: false,
622  minWidth: 300,
623  maxWidth: 600,
624  width: "90%",
625  close: function(e) {
626  if (window.activeWindows != null)
627  window.activeWindows.pop();
628  },
629  open: function() {
630  if (window.activeWindows != null)
631  window.activeWindows.push(this);
632  var kWindow= $(this.wrapper);
633  this.center();
634  $(kWindow).css({position: "absolute", top: "10px", overflow: "hidden"});
635  $("#groupRightWindow").css({overflow: "hidden", padding: 0, paddingBottom: 15});
636  }
637  }).data("kendoWindow");
638 
639  groupRightWindow.content(kendo.template($("#groupRightsWindowTemplate").html()));
640 
641  var fields= {feature_code: {type: "string"}, fdescription: {type: "string"}};
642 
643  for (var i=0; i!= userSupportContents.labels.length; i++)
644  {
645  var item= userSupportContents.labels[i];
646  switch(item.type)
647  {
648  case "boolean":
649  if (item.code == "bool_crequired")
650  fields.bool_max_cr= {type: "boolean"};
651  break;
652  case "amount":
653  case "count":
654  fields["max_"+item.code]= {type: "number"};
655  break;
656  }
657  }
658 
659  var groupRightsGrid= $("#groupRightsGrid").kendoGrid({
660  dataSource: {
661  data: userSupportContents.rightsData,
662  schema: {
663  model: {
664  id: "feature_code",
665  fields: fields
666  }
667  }
668  },
669  columns: [
670  {field: "fdescription", title: "Feature", attributes: {"class": "showEllipsis"}, width: "200px"},
671  {title: "Description", template: $("#groupRightsCellTemplate").html()}
672  ],
673  noRecords: {
674  template: "<tr><td colspan='2'>No Records Found</td></tr>"
675  }
676  }).data("kendoGrid");
677 
678  $("#groupRightsGrid").css({border: "none", maxHeight: 500, overflowX: "hidden", overflowY: "auto"});
679  $("#groupRightsGrid:not(:has(.k-grid-norecords) .k-grid-content").css({overflowX: "hidden", overflowY: "auto"});
680  $("#groupRightsGrid:has(.k-grid-norecords) .k-grid-content").css({overflow: "hidden"});
681 
682  $("#groupRightWindow").on("click", ".closeBtn", function() {
683  groupRightWindow.close();
684  return false;
685  });
686 
687  var toolTipProps= homecuTooltip.defaults;
688  toolTipProps.filter= ".showEllipsis:overflown";
689  toolTipProps.content= function(e) {
690  return $(e.target).text().trim();
691  };
692 
693  $("#groupRightsGrid").kendoTooltip(toolTipProps);
694  }
695  groupRightWindow.open();
696  }
697 
698  <?php
699  /**
700  * function init()
701  *
702  * Initializes the main grid and all the click events on the main popup.
703  */
704  ?>
705  function init()
706  {
707  $.homecuValidator.setup({formValidate:'userRightsDiv', formStatusField: 'formValidateDiv'});
708  $("#externalTabWindow").data("preferredHeight", "auto");
709  $("#externalTabWindow").data("preferredWidth", 1100);
710  var columns= [
711  {title: "Feature", columns: [{locked: true, field: "fdescription", title: "&nbsp;", width: 200, attributes: {"class": "showEllipsis"}}]},
712  {title: "Reset", columns: [{locked: true, template: "<input type='checkbox' class='rowCheckbox' # if (!exists) { # disabled style='display:none;' # } #>",
713  attributes: {"class": "checkboxTD"}, headerTemplate: "<input type='checkbox' class='allCheckbox'>", width: 75, sortable: false, filterable: false, field: "checked"}]}
714  ];
715 
716  var fields= {
717  kendoIndex: {type: "number", editable: false},
718  feature_code: {type: "string", editable: false},
719  checked: {type: "boolean", editable: false},
720  fdescription: {type: "string", editable: false},
721  limit_type: {type: "string", editable: false},
722  exists: {type: "boolean", editable: false}
723  };
724 
725  var booleans= [];
726  var confirmation= [];
727  var counts= [];
728  var amounts= [];
729  var rightsGrid= null;
730  var booleanWidth= 75, amountWidth= 150, countWidth= 150;
731  var defaultT= ["<td class='showEllipsis' role='gridcell'>#: fdescription #</td>",
732  "<td class='checkboxTD' role='gridcell'><input type='checkbox' class='rowCheckbox' checked # if (!exists) { # disabled style='display:none;' # } #></td>"];
733  var defaultTB= [];
734  var defaultTA= [];
735  var defaultTC= [];
736  var templateTemplate= kendo.template("# if (code != 'bool_faccess') { # \\# if (limit_type == 'A') { \\# <div class='accessOnly'>&nbsp;</div> \\# } else { \\# # } #"
737  + "<center> \\# if (#: code #) { \\# <div class='restriction allow hcu-all-100'><i class='fa fa-check'></i></div> \\# } else { \\#"
738  + "<div class='restriction ban hcu-all-100'><i class='fa fa-ban'></i></div> \\# } \\# </center> # if (code != 'bool_faccess') { # \\# } \\# # } #");
739  for (var i=0; i!= userSupportContents.labels.length; i++)
740  {
741  var item= userSupportContents.labels[i];
742  switch(item.type)
743  {
744  case "boolean":
745  if (item.isConfirmation)
746  {
747  var thisRow= {field: item.code, title: item.popup, attributes: {"class": "aa"}, headerAttributes: {"class": "aa"},
748  template: "# if (limit_type == 'A') { # <div class='accessOnly'>&nbsp;</div> # } else if (checked) { # Yes # } "
749  + "else if (bool_max_cr) { # <div class='vsgDisabled'>Yes</div> # } else if (bool_crequired) { # "
750  + "<div class='crequired showClickable hcu-all-100'>Yes</div> # } else { # <div class='crequired showClickable hcu-all-100'>No</div> # } #",
751  width: 140};
752  confirmation.push(thisRow);
753  fields[item.code]= {type: "boolean", editable: false};
754  defaultTB.push("# if (limit_type == 'A') { # <td class='accessOnly aa'>&nbsp;</td> # } else { #<td class='aa vsgDisabled k-group-cell' role='gridcell'>Yes</td># } #");
755  }
756  else
757  {
758  var thisRow= {field: item.code, headerTemplate: "<center>"+item.popup+"</center>", attributes: {"class": "aa restrictionTD", "data-name": item.code},
759  headerAttributes: {"class": "aa"}, template: templateTemplate({code: item.code}), width: booleanWidth};
760  booleans.push(thisRow);
761  fields[item.code]= {type: "boolean", editable: false};
762  var defaultThis= item.isAccess ? "<td class='aa vsgDisabled'><center><i class='fa fa-check'></i></center></td>" :
763  "# if (limit_type == 'A') { # <td class='accessOnly aa'>&nbsp;</td> # } else { #<td class='aa vsgDisabled'><center><i class='fa fa-check'></i></center></td> # } #";
764  defaultTB.push(defaultThis);
765  }
766  break;
767  case "amount":
768  amounts.push({field: item.code, title: item.popup, editor: function(container, options) { numericEditor(container, options, rightsGrid); },
769  attributes: {"class": "bb"}, headerAttributes: {"class": "bb"}, template: "# if (limit_type != 'B' && limit_type != 'D') { # <div class='notAmount'>&nbsp;</div> # } "
770  + "else if (checked) { # Default # } else if ("+ item.code+ " == null) { #"
771  + "<div class='showClickable hcu-all-100'>Default</div> # } else { # <div class='showClickable hcu-all-100'>#= kendo.toString ("+ item.code+ ", 'c0') #</div> # } #",
772  width: amountWidth});
773  fields[item.code]= {type: "number"};
774  defaultTA.push("# if (limit_type != 'B' && limit_type != 'D') { # <td class='bb notAmount'>&nbsp;</td> # }"
775  + "else { # <td class='bb vsgDisabled' role='gridcell'>Default</td> # } #");
776  break;
777  case "count":
778  counts.push({field: item.code, title: item.popup, editor: function(container, options) { numericEditor(container, options, rightsGrid); },
779  attributes: {"class": "cc"}, headerAttributes: {"class": "cc"}, template: "# if (limit_type != 'B' && limit_type != 'Q') { # <div class='notCount'>&nbsp;</div> # } "
780  + "else if (checked) { # Default # } else if ("+ item.code+ " == null) { #"
781  + "<div class='showClickable hcu-all-100'>Default</div> # } else { # <div class='showClickable hcu-all-100'>#= kendo.toString ("+ item.code+ ", 'n0') #</div> # } #",
782  width: countWidth});
783  fields[item.code]= {type: "number"};
784  defaultTC.push("# if (limit_type != 'B' && limit_type != 'Q') { # <td class='cc notCount'>&nbsp;</td> # } else {"
785  + "# <td class='cc vsgDisabled' role='gridcell'>Default</td> # } #");
786  break;
787  }
788  }
789 
790  userSupportContents.defaultRowTemplate= defaultT.concat(defaultTB).concat(defaultTA).concat(defaultTC).join(" ");
791 
792  columns.push({headerTemplate: "<center>Restrictions</center>", columns: booleans, headerAttributes: {"class": "topHeader aa"}});
793  columns.push({title: "Confirmation", columns: confirmation, headerAttributes: {"class": "topHeader aa"}});
794  columns.push({title: "Count Per", columns: counts, headerAttributes: {"class": "topHeader cc"}});
795  columns.push({title: "Amount Per", columns: amounts, headerAttributes: {"class": "topHeader bb"}});
796 
797 
798  var data= <?php echo HCU_JsonEncode($readData); ?>;
799  if (data.error.length > 0)
800  $.homecuValidator.displayMessage(data.error, $.homecuValidator.settings.statusError );
801  else
802  {
803  for(var i=0; i!= data.data.length; i++)
804  {
805  data.data[i].kendoIndex= i+1;
806  data.data[i].checked= false;
807  createSnapshot(data.data[i]);
808  }
809  userSupportContents.rightsData= new kendo.data.ObservableArray(data.data);
810 
811  }
812 
813  rightsGrid= $("#userRightsGrid").kendoGrid({
814  dataSource: {
815  transport: {
816  read: function(options) {
817  options.success(userSupportContents.rightsData);
818  },
819  create: function(options) {}, // Won't get here
820  update: function(options) {
821  updateGrid(options, this);
822  }
823  },
824  schema: {
825  model: {
826  id: "kendoIndex",
827  fields: fields
828  }
829  },
830  sort: [{field: "specialAddRow", dir: "asc"}, {field: "fdescription", dir: "asc"}, {field: "feature_code", dir: "asc"}],
831  batch: true
832  },
833  columns: columns,
834  noRecords: {
835  template: "<tr><td colspan='3'>No Records Found</td></tr>"
836  },
837  editable: "incell",
838  autoBind: true,
839  scrollable: false,
840  height: 400
841  }).data("kendoGrid");
842 
843  $("#userRightsGrid:has(.k-grid-norecords) .k-grid-content").css({overflow: "hidden !important"});
844  $("#userRightsGrid").addClass("hideBB hideCC");
845 
846  <?php // This is needed because the colgroups are needed to show/hide based on the radio button selection. Otherwise, there are also alignment issues. ?>
847  $("#userRightsGrid colgroup").each(function(i) {
848  $(this).find("col").each(function(index) {
849  if (index < 2)
850  ;
851  else if (index < 7)
852  $(this).addClass("aa");
853  else if (index < 11)
854  $(this).addClass("bb");
855  else if (index < 14)
856  $(this).addClass("cc");
857  });
858  });
859 
860  $(".userRightsDiv").on("click", ".showGroupRightsBtn", function() {
861  openGroupRights();
862  return false;
863  });
864 
865  $(".userRightsDiv").on("mouseup", ".cancelBtn", function() {
866  potentiallyCancelChanges();
867  return false;
868  });
869 
870  $(".userRightsDiv").on("mouseup", ".updateBtn", function() {
871  $("body").click();
872  var data= rightsGrid.dataSource.data();
873  var doUpdate= false;
874  for(var i=0; i!= data.length; i++)
875  {
876  var item= data[i];
877  if (item.checked)
878  item.dirty= true; <?php // Will need to manually set the dirty flag so it is picked in the model ?>
879  if (item.dirty)
880  doUpdate= true;
881  }
882  doUpdate ? rightsGrid.dataSource.sync() : postPostPostPost();
883  return false;
884  });
885 
886  <?php printCheckboxEvents("#userRightsGrid", "", "", ".allCheckbox", ".rowCheckbox", "doDefaultRow"); ?>
887 
888  $("#userRightsGrid").on("click", ".checkboxTD", function(e) {
889  if (e.target !== this)
890  return;
891  $(this).find("[type='checkbox']").click();
892  });
893 
894  $("#userRightsGrid").on("click", ".crequired.showClickable", function(e) {
895  $("#userRightsGrid .crequiredTD").each(function(ei) {
896  var td= $(this).closest("td");
897  var tr= $(td).closest("tr");
898  var dirty= $(td).hasClass("k-dirty-cell");
899  var dataItem= $("#userRightsGrid").data("kendoGrid").dataItem(tr);
900 
901  $(td).removeClass("crequiredTD");
902  $(td).removeClass("k-edit-cell");
903  $(tr).removeClass("k-grid-edit-row");
904  $(td).html("<div class='crequired showClickable hcu-all-100'>" + (dataItem.bool_crequired ? "Yes" : "No") + "</div>");
905  dirtify(dirty, td);
906  });
907 
908  var isTrue= $(this).text().trim() == "Yes";
909  var td= $(this).closest("td");
910  var tr= $(td).closest("tr");
911  $(td).html("<input class='crrr' type='checkbox' "+ (isTrue ? "checked" : "") + ">");
912  $(td).addClass("crequiredTD");
913  $(td).addClass("k-edit-cell");
914  $(tr).addClass("k-grid-edit-row");
915  return false;
916  });
917 
918  $("#userRightsGrid").on("click", ".crequiredTD", function(e) {
919  if ($(e.target).is("input"))
920  return;
921  var td= $(this).closest("td");
922  var tr= $(td).closest("tr");
923  var dirty= $(td).hasClass("k-dirty-cell");
924  var dataItem= $("#userRightsGrid").data("kendoGrid").dataItem(tr);
925 
926  $(td).removeClass("crequiredTD");
927  $(td).removeClass("k-edit-cell");
928  $(tr).removeClass("k-grid-edit-row");
929  $(td).html("<div class='crequired showClickable hcu-all-100'>" + (dataItem.bool_crequired ? "Yes" : "No") + "</div>");
930  dirtify(dirty, td);
931  return false;
932  });
933 
934  $("#userRightsGrid").on("click", ".crrr", function(e) {
935  var td= $(this).closest("td");
936  var tr= $(td).closest("tr");
937  var dataItem= $("#userRightsGrid").data("kendoGrid").dataItem(tr);
938  dataItem.dirty= true;
939  dataItem.bool_crequired= $(this).is(":checked");
940  dirtify(dataItem.bool_crequired != dataItem.org_bool_crequired, td);
941  });
942 
943  $("body").on("click.userSupportHubNamespace", function(e) {
944  if ($(e.target).hasClass("crrr") || $(e.target).hasClass("crequiredTD") || $(e.target).hasClass("cancelBtn"))
945  return;
946 
947  $("#userRightsGrid .crequiredTD").each(function(ei) {
948  var td= $(this).closest("td");
949  var tr= $(td).closest("tr");
950  var dirty= $(td).hasClass("k-dirty-cell");
951  var dataItem= $("#userRightsGrid").data("kendoGrid").dataItem(tr);
952 
953  $(td).removeClass("crequiredTD");
954  $(td).removeClass("k-edit-cell");
955  $(tr).removeClass("k-grid-edit-row");
956  $(td).html("<div class='crequired showClickable hcu-all-100'>" + (dataItem.bool_crequired ? "Yes" : "No") + "</div>");
957  dirtify(dirty, td);
958  });
959  });
960 
961  $("#userRightsGrid").on("click", ".restriction.allow", function() {
962  var td= $(this).closest("td");
963  var name= $(td).data("name");
964  $(td).html("<center><div class='restriction ban hcu-all-100'><i class='fa fa-ban'></i></div></center>");
965  var tr= $(td).closest("tr");
966  var dataItem= rightsGrid.dataItem(tr);
967  dataItem.dirty= true;
968  dataItem[name]= false;
969  dirtify(dataItem[name] != dataItem["org_"+name], td);
970  });
971 
972  $("#userRightsGrid").on("click", ".restriction.ban", function() {
973  var td= $(this).closest("td");
974  var name= $(td).data("name");
975  $(td).html("<center><div class='restriction allow hcu-all-100'><i class='fa fa-check'></i></div></center>");
976  var tr= $(td).closest("tr");
977  var dataItem= rightsGrid.dataItem(tr);
978  dataItem.dirty= true;
979  dataItem[name]= true;
980 
981  dirtify(dataItem[name] != dataItem["org_"+name], td);
982  });
983 
984  $(".userRightsDiv").on("click", "[name='gridShowOption']", function() {
985  radioClick($(this).val().trim());
986  });
987 
988  <?php printExtendShowOverflown(); ?>
989 
990  var toolTipProps= homecuTooltip.defaults;
991  toolTipProps.filter= ".showEllipsis:overflown";
992  toolTipProps.content= function(e) {
993  return $(e.target).text().trim();
994  };
995 
996  $("#userRightsGrid").kendoTooltip(toolTipProps);
997 
998  $("#userRightsGrid").kendoTooltip(toolTipProps);
999 
1000  toolTipProps= homecuTooltip.defaults;
1001  toolTipProps.filter= ".aa:has(.vsgDisabled)";
1002  toolTipProps.content= "Set at the group or profile level.";
1003 
1004  $("#userRightsGrid").kendoTooltip(toolTipProps);
1005 
1006  toolTipProps= homecuTooltip.defaults;
1007  toolTipProps.filter= "td.vsgDisabled";
1008  toolTipProps.content= "Set to Default.";
1009 
1010  $("#userRightsGrid").kendoTooltip(toolTipProps);
1011 
1012  toolTipProps= homecuTooltip.defaults;
1013  toolTipProps.filter= "td:has(.notAmount)";
1014  toolTipProps.content= "Feature doesn't have amounts.";
1015 
1016  $("#userRightsGrid").kendoTooltip(toolTipProps);
1017 
1018  toolTipProps= homecuTooltip.defaults;
1019  toolTipProps.filter= "td:has(.notCount)";
1020  toolTipProps.content= "Feature doesn't have counts.";
1021 
1022  $("#userRightsGrid").kendoTooltip(toolTipProps);
1023 
1024  toolTipProps= homecuTooltip.defaults;
1025  toolTipProps.filter= "td:has(.accessOnly)";
1026  toolTipProps.content= "Feature is access only.";
1027 
1028  $("#userRightsGrid").kendoTooltip(toolTipProps);
1029 
1030  }
1031 
1032  function doDefaultRow (checked, checkboxInput)
1033  {
1034  var savedRows= $("#userRightsGrid").data("savedRows");
1035  savedRows= savedRows == null ? {} : savedRows;
1036  var tr= $(checkboxInput).closest("tr");
1037  var rightsGrid= $("#userRightsGrid").data("kendoGrid");
1038 
1039  if (checked)
1040  {
1041  savedRows[$(tr).index()]= $(tr).html();
1042  $("#userRightsGrid").data("savedRows", savedRows);
1043  var template= kendo.template(userSupportContents.defaultRowTemplate);
1044  $(tr).removeClass("k-grid-edit-row");
1045  $(tr).html(template(rightsGrid.dataItem($(tr))));
1046  }
1047  else
1048  {
1049  var row= savedRows[$(tr).index()];
1050  if (row != null)
1051  {
1052  $(tr).addClass("k-grid-edit-row");
1053  $(tr).html(row);
1054  }
1055 
1056  }
1057  }
1058 
1059  <?php
1060  /**
1061  * function postPostPostPost()
1062  * This obviously happens after everything else.
1063  */
1064  ?>
1065  function postPostPostPost()
1066  {
1067  $("#externalTabWindow").data("isClosing", true);
1068  $("#externalTabWindow").data("kendoWindow").close();
1069  $("#externalTabWindow").data("isClosing", false);
1070  }
1071 
1072  <?php
1073  /**
1074  * function userSupportDoOnClose()
1075  * Whew! I saved a couple of lines. No, this is needed as it is the hook function from the userSupportHead script.
1076  */
1077  ?>
1078  function userSupportDoOnClose()
1079  {
1080  potentiallyCancelChanges();
1081  $("#externalTabWindow").data("shouldClose", false);
1082  }
1083 
1084  <?php
1085  /**
1086  * function potentiallyCancelChanges()
1087  * This displays the discard changes dialog on close if there are changes made.
1088  */
1089  ?>
1090  function potentiallyCancelChanges()
1091  {
1092  if ($("#userRightsGrid .k-dirty-cell .k-dirty").length == 0 && $("#userRightsGrid .checkboxTD [type='checkbox']:checked").length == 0)
1093  postPostPostPost();
1094  else
1095  {
1096  var discardChangesDialog= $("#discardChangesDialog").data("kendoDialog");
1097  if (discardChangesDialog == null)
1098  {
1099  discardChangesDialog= $("<div id='discardChangesDialog'></div>").appendTo("body").kendoDialog({
1100  title: "Discard Changes",
1101  content: "<p>Changes have been made to this user's rights.</p><p>Do you wish to discard your changes?</p>",
1102  actions: [
1103  {text: "No"},
1104  {text: "Yes", primary: true, action: function() {
1105  postPostPostPost();
1106  }}
1107  ],
1108  visible: false,
1109  open: function() {
1110  if (window.activeWindows != null)
1111  window.activeWindows.push(this);
1112  },
1113  close: function() {
1114  if (window.activeWindows != null)
1115  window.activeWindows.pop();
1116  }
1117  }).data("kendoDialog");
1118  }
1119  discardChangesDialog.open();
1120  }
1121  }
1122 
1123  <?php // Ready is not needed since document is already initiated. ?>
1124  init();
1125  </script>
1126 
1127  <script type="text/x-kendo-template" id="groupRightsWindowTemplate">
1128  <div class="hcu-template container hcu-all-100 hcu-no-padding">
1129  <div id="groupRightsGrid" class="hcu-all-100"></div>
1130  <div class="hcu-edit-buttons k-state-default row hcu-all-100">
1131  <div class="col-xs-12">
1132  <a class="closeBtn k-button k-primary" href="\\#"><i class="fa fa-times"></i>Close</a>
1133  </div>
1134  </div>
1135  </div>
1136  </script>
1137 
1138  <script type="text/x-kendo-template" id="groupRightsCellTemplate">
1139  # var fullDescription= [];
1140  for (var i=0; i!= userSupportContents.labels.length; i++)
1141  {
1142  var item= userSupportContents.labels[i];
1143  var code= item.code == "bool_crequired" ? "bool_max_cr" : "max_"+item.code;
1144  var value= data[code];
1145  if (value != null && (value+"").trim() != "")
1146  {
1147  var fullValue= item.description + ": ";
1148  switch(item.type)
1149  {
1150  case "amount":
1151  fullValue+= kendo.toString(value, "c2");
1152  break;
1153  case "count":
1154  fullValue+= kendo.toString(value, "n0");
1155  break;
1156  case "boolean":
1157  fullValue+= value == "Y" ? "true" : "false";
1158  break;
1159  }
1160  fullDescription.push(fullValue);
1161  }
1162  } #
1163  #= fullDescription.join(", <br>") #
1164  </script>
1165  <script type="text/x-kendo-template" id="defaultRowTemplate">
1166 
1167  </script>
1168 
1169  <div class="container-fluid userRightsDiv hcu-template vsgPrimary" id="userRightsDiv">
1170  <div class="row">
1171  <div id="formValidateDiv" class="col-xs-12 k-block k-error-colored formValidateDiv" style="display:none;"></div>
1172  </div>
1173  <div class="well well-sm">
1174  <div class="row hcuSpacer">
1175  <div class="col-xs-12">
1176  <div class="floatRight">
1177  <a href="#" class="showGroupRightsBtn">Show Group Rights</a>
1178  </div>
1179  </div>
1180  </div>
1181  <div class="row">
1182  <div>
1183  <div class="radio-inline radio-spacer"><label><input type="radio" name="gridShowOption" value="boolean" checked>&nbsp; Restrictions</label></div>
1184  <div class="radio-inline radio-spacer"><label><input type="radio" name="gridShowOption" value="count">&nbsp; Quantity</label></div>
1185  <div class="radio-inline radio-spacer"><label><input type="radio" name="gridShowOption" value="amount">&nbsp; Amount</label></div>
1186  <div class="radio-inline radio-spacer"><label><input type="radio" name="gridShowOption" value="all">&nbsp; All</label></div>
1187  </div>
1188  </div>
1189  </div>
1190  <div class="row">&nbsp;</div>
1191  <div class="row">
1192  <div class="col-xs-12 hcu-secondary">
1193  <div class="small vsgSecondary">Click on field to edit</div>
1194  </div>
1195  </div>
1196  <div class="row">
1197  <div class="col-xs-12">
1198  <div id="userRightsGrid"></div>
1199  </div>
1200  </div>
1201  <div class="hcu-edit-buttons k-state-default">
1202  <a class="cancelBtn" href="#">Cancel</a>
1203  &nbsp;&nbsp;&nbsp;
1204  <a class="updateBtn k-button k-primary" href="#"><i class="fa fa-check"></i>Update</a>
1205  </div>
1206  </div>
1207 <?php }
Definition: User.php:7