Odyssey
addUser.i
1 <?php
2 /**
3  * @package addUser.i -- file is for adding a new user to the system. It is called through a link on the user search dialog or through a card in the user hub.
4  */
5 
6 $dontRunGroupSearch = true;
7 require_once("userSearch.i");
8 require_once("aGroupSupport.i");
9 require_once("$sharedLibrary/hcuTranslate.i"); // ValidateUsername needs to return the same error messages as in banking.
10 
11 $parameters = array("a" => array("operation" => ""));
12 $string = array("filter" => FILTER_DEFAULT);
13 HCU_ImportVars($parameters, "a", array("operation" => $string));
14 
15 switch($parameters["a"]["operation"]) {
16  case "addUser":
17  $returnArray = addUser($dbh, $Cu, $Cn);
18 
19  header('Content-type: application/json');
20  print HCU_JsonEncode($returnArray);
21  exit; // Since this is an included file, whatever is in the script will execute if not exited here.
22  case "validateUsername":
23  $parameters = array("a" => array("username" => ""));
24  HCU_ImportVars($parameters, "a", array("username" => $string));
25  $username = HCU_array_key_exists("username", $parameters["a"]) ? trim($parameters["a"]["username"]) : "";
26  $returnArray = validateUsername($dbh, $Cu, $username);
27 
28  header('Content-type: application/json');
29  print HCU_JsonEncode($returnArray);
30  exit; // Since this is an included file, whatever is in the script will execute if not exited here.
31 }
32 
33 /**
34  * function validateUsername($dbh, $Cu, $username)
35  * This ensures that the username chosen is not already in existence.
36  *
37  * @param integer $dbh -- the database connection
38  * @param string $Cu -- the credit union
39  * @param string $username -- the username to validate
40  *
41  * @return "code" -- zero if no error, nonzero if there is an error, "error" -- the first error encountered or an empty array, "valid" -- true if the username doesn't exist.
42  */
43 function validateUsername($dbh, $Cu, $username) {
44  $MC = new hcu_talk_base("en_US"); // Needed to return same error messages as banking.
45  try {
46 
47  // Treat admin SLIGHTLY differently. Admin SHOULD be able to create an username with a number like what would happen through the user activation process.
48  if (!ctype_digit(strval(trim($username)))) {
49  // * Validate user alias length
50  if ( strlen( $username ) >= 0 && strlen( $username ) < 6 ) {
51  throw new exception ($MC->msg('Username too short', HCU_DISPLAY_AS_JS), 1);
52  }
53 
54  if ( strlen( $username ) > 5 && !check_alias_format( $username ) ) {
55  // * Validate the useralias is correct format
56  throw new exception ($MC->msg('Username appears invalid', HCU_DISPLAY_AS_JS), 2);
57  }
58  }
59 
60  $sql = "select 'FOUND' from ${Cu}user where lower(trim(user_name)) = '" . prep_save(strtolower(trim($username)), 50) . "'";
61  $sth = db_query($sql, $dbh);
62  if (!$sth || db_num_rows($sth) > 0) {
63  throw new exception($MC->msg( "Username not available" , HCU_DISPLAY_AS_JS), 3);
64  }
65 
66  $returnArray = array("status" => "000", "error" => "");
67  } catch(exception $e) {
68  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage());
69  }
70  return $returnArray;
71 }
72 
73 /**
74  * function readForAddUser($dbh, $Cu)
75  * Two things are needed: 1) the profile list for the credit union, 2) the default profile for the credit union and 3) the password rules for the credit union.
76  *
77  * @param integer $dbh -- the database connection
78  * @param string $Cu -- the credit union
79  *
80  * @return "code" -- zero if is no error, nonzero if there is an error, "error" -- the first error encountered or an empty array, "profileDDL" -- the list of profiles for the CU,
81  * "passwordRules" -- the password rules for the CU.
82  */
83 function readForAddUser($dbh, $Cu) {
84  try {
85  $sql = "select profile_id as value, trim(description) as text from cu_profile where cu = '$Cu' order by trim(description)";
86  $sth = db_query($sql, $dbh);
87  if (!$sth) {
88  throw new exception("profile id query failed.", 1);
89  }
90  $ddl = array(array("value" => "", "text" => "(None)"));
91  for($i = 0; $row = db_fetch_assoc($sth, $i); $i++) {
92  $ddl[] = $row;
93  }
94 
95  $sql = "select profile_id from cu_profile p left join cuadmin a on p.profile_code = a.settings::json->>'profile' where a.cu = '$Cu'";
96  $sth = db_query($sql, $dbh);
97  if(!$sth) {
98  throw new Exception("default profile query failed.", 2);
99  }
100  $dftprofile = db_fetch_row($sth, 0)[0];
101 
102  $sql = "select pwdconfig from cuadmin where cu = '$Cu'";
103  $sth = db_query($sql, $dbh);
104  if (!$sth) {
105  throw new exception("pwdconfig query failed.", 4);
106  }
107  $pwdconfig = db_fetch_row($sth, 0)[0];
108  $pwdconfig = HCU_JsonDecode($pwdconfig);
109  if (!is_array($pwdconfig)) {
110  throw new exception("Password array is malformed.", 5);
111  }
112 
113  return array("code" => 0, "error" => array(), "profileDDL" => $ddl, "profileDefault" => $dftprofile, "passwordRules" => $pwdconfig,
114  "usePhonesInsteadOfMFA" => getUsePhonesInsteadOfMFA($dbh, $Cu));
115  } catch(exception $e) {
116  return array("code" => $e->getCode(), "error" => array($e->getMessage()), "profileDDL" => array(), "profileDefault"=>"", "passwordRules" => array(),
117  "usePhonesInsteadOfMFA" => false);
118  }
119 }
120 
121 /**
122  * function readUserSearch($dbh, $Cu)
123  * This read the user search
124  *
125  * @param $dbh -- the database connection
126  * @param $Cu -- the credit union
127  * @param $Cn -- the logged in user
128  *
129  * @return array("code" => 0 if no errors; nonzero otherwise, "error" => list of errors encountered, "sql" => list of SQL executed, "data" => results of search,
130  * "encryption" => if there is one record returned, then encrypt that record and send.)
131  * Next error code: 34
132  */
133 function addUser($dbh, $Cu, $Cn) {
134  $parameters = array("a" => array("username" => "", "email" => "", "password" => "", "phone" => "", "groupId" => "", "profileId" => "", "confirm" => ""));
135  $encryption = array("message" => "", "hash" => "");
136  try {
137  $string = array("filter" => HCUFILTER_INPUT_STRING);
138  HCU_ImportVars($parameters, "a", array("username" => $string, "email" => $string, "password" => $string, "phones" => $string, "groupId" => $string,
139  "profileId" => $string, "confirm" => $string));
140  extract($parameters["a"]);
141 
142  $username = isset($username) ? trim($username) : "";
143  $email = isset($email) ? trim($email) : "";
144  $password = isset($password) ? trim($password) : "";
145  $phones = isset($phones) ? trim($phones) : "";
146  $groupId = isset($groupId) ? trim($groupId) : "";
147  $profileId = isset($profileId) ? trim($profileId) : "";
148  $confirm = isset($confirm) ? trim($confirm) : "";
149 
150  if ($username == "") {
151  throw new exception("Username is required.", 1);
152  }
153  if ($email == "") {
154  throw new exception("Email is required.", 2);
155  }
156  if ($password == "") {
157  throw new exception("Password is required.", 3);
158  }
159  if ($password != $confirm) {
160  throw new exception("Passwords must match.", 31);
161  }
162  if ($profileId == "") {
163  if ($groupId == "") {
164  throw new exception("Group id or profile id needs to be set (not both).", 7);
165  }
166  if (!is_numeric($groupId)) {
167  throw new exception("Group id needs to be numeric.", 10);
168  }
169  } else {
170  if ($groupId != "") {
171  throw new exception("Group id or profile id needs to be set (not both).", 8);
172  }
173  if (!is_numeric($profileId)) {
174  throw new exception("Profile id needs to be numeric.", 11);
175  }
176  }
177 
178  $userValidate = validateUsername($dbh, $Cu, $username);
179  if ($userValidate["status"] !== "000") {
180  throw new exception($userValidate["error"], 28);
181  }
182 
183  $phoneRecord = array();
184  $contactId = 0;
185  if ($phones != "" && $phones != "[]") {
186  $phones = HCU_JsonDecode($phones);
187  if (!is_array($phones)) {
188  throw new exception("Phones are malformed.", 22);
189  }
190  $phoneArray = array("mobile" => array());
191  foreach($phones as $phone) {
192  $value = trim($phone);
193  if ($value != "") {
194  if (!is_numeric($value) || $value <= 0 || strlen($value) != 10) {
195  throw new exception("Phones are malformed.", 25);
196  }
197  $value = substr($value, 0, 3) . "-" . substr($value, 3, 3) . "-" . substr($value, 6);
198  $phoneArray["mobile"][] = $value;
199  }
200  }
201 
202  $sql = "select nextval('${Cu}usercontact_contact_id_seq'::regclass)";
203  $sth = db_query($sql, $dbh);
204 
205  if (!$sth) {
206  throw new exception("Nextval query failed.", 26);
207  }
208  $contactId = intval(db_fetch_row($sth, 0)[0]);
209 
210  $phoneRecord = array("_action" => "create", "contact_id" => $contactId, "phones" => HCU_JsonEncode($phoneArray));
211  }
212 
213  if (!validateEmail($email)) {
214  throw new exception("Email is not valid.", 5);
215  }
216 
217  $userRecord = array("_action" => "create", "user_name" => $username, "email" => $email);
218  $groupRecord = array();
219 
220  if ($groupId != "") {
221  // Validate group id
222  $sql = "select 'FOUND' from ${Cu}group where group_id = " . intval($groupId);
223  $sth = db_query($sql, $dbh);
224  if (!$sth) {
225  throw new exception("Validate group query failed.", 15);
226  }
227 
228  $userRecord["group_id"] = $groupId;
229  $userRecord["is_group_primary"] = false;
230  $openAccessControl = false;
231  } else {
232  // Validate profile id
233  $sql = "select profile_id, cu from cu_profile where profile_id = " . intval($profileId);
234  $sth = db_query($sql, $dbh);
235  if (!$sth) {
236  throw new exception("Validate query failed.", 12);
237  }
238  if (db_num_rows($sth) == 0) {
239  throw new exception("Profile id not found.", 13);
240  }
241  if (strtolower(trim($Cu)) != strtolower(trim(db_fetch_row($sth, 0)[1]))) {
242  throw new exception("Profile id belongs to another CU.", 14);
243  }
244 
245  $sql = "select nextval('${Cu}group_group_id_seq'::regclass)";
246  $sth = db_query($sql, $dbh);
247 
248  if (!$sth) {
249  throw new exception("Nextval query failed.", 9);
250  }
251  $groupId = intval(db_fetch_row($sth, 0)[0]);
252  $userRecord["group_id"] = $groupId;
253  $userRecord["is_group_primary"] = true;
254 
255  $groupRecord = array("_action" => "create", "group_id" => $groupId, "group_name" => $username, "profile_id" => $profileId, "contact" => $contactId);
256  $openAccessControl = true;
257  }
258 
259  // You don't validate password for admin EXCEPT for being greater than or equal to four characters long.
260  if (strlen($password) < 4) {
261  throw new exception("Password needs to be four characters or greater.", 34);
262  }
263 
264  $hash = password_hash($password, PASSWORD_DEFAULT);
265  $userRecord["passwd"] = "$hash";
266 
267  $envVars = array("cu" => $Cu);
268 
269  $context = "admin";
270  $script = "addUser.i";
271 
272  $sql = "select email from cuadminusers where user_name = '$Cn' and cu = '$Cu'";
273  $sth = db_query($sql, $dbh);
274  if (!$sth) {
275  throw new exception("email query failed.", 16);
276  }
277  $email = db_fetch_row($sth)[0];
278 
279  $sql = "select coalesce(ca.retrylimit,5) as retry, coalesce(ca.gracelimit,5) as grace from cuadmin ca where ca.cu = '$Cu'";
280  $sth = db_query($sql, $dbh);
281  if (!$sth) {
282  throw new exception("retry query failed.", 30);
283  }
284  $row = db_fetch_assoc($sth, 0);
285 
286  $userRecord["failedremain"] = intval($row["retry"]);
287  $userRecord["forceremain"] = intval($row["grace"]);
288  $userRecord["userflags"] = 2;
289 
290  $sql = "select nextval('" . strtolower(trim($Cu)) . "user_id_seq'::regclass)";
291  $sth = db_query($sql, $dbh);
292  if (!$sth) {
293  throw new exception("Nextval query failed.", 19);
294  }
295  $userId = intval(db_fetch_row($sth, 0)[0]);
296  $userRecord["user_id"] = $userId;
297  $userRecord["contact"] = $contactId;
298 
299  if (!db_work ($dbh, HOMECU_WORK_BEGIN)) {
300  throw new exception("begin query failed.", 32);
301  }
302 
303  if (count($phoneRecord) > 0) {
304  $phoneRecord = array("usercontact" => array($phoneRecord));
305  if (DataUserTableUpdate($dbh, $envVars, null, $phoneRecord, $userId, "P_ADD", $context, $script, "A", "Phone Add", $Cn, $email, trim($_SERVER["REMOTE_ADDR"])) === false) {
306  throw new exception("Add contact failed.", 127);
307  }
308  }
309 
310  if (count($groupRecord) > 0) {
311  $groupRecord = array("group" => array($groupRecord));
312  if (DataUserTableUpdate($dbh, $envVars, null, $groupRecord, $userId, "G_ADD", $context, $script, "A", "Group Add", $Cn, $email, trim($_SERVER["REMOTE_ADDR"])) === false) {
313  throw new exception("Add group failed.", 117);
314  }
315  }
316 
317  $userRecord = array("user" => array($userRecord));
318  if (DataUserTableUpdate($dbh, $envVars, null, $userRecord, $userId, "U_ADD", $context, $script, "A", "User Add", $Cn, $email, trim($_SERVER["REMOTE_ADDR"])) === false) {
319  throw new exception("Add user failed.", 118);
320  }
321 
322  if (!db_work($dbh, HOMECU_WORK_COMMIT)) {
323  throw new exception("commit work failed.", 133);
324  }
325 
326  $sql = "select u.user_id, u.user_name, u.email, u.name, array_to_string(ss.cellnumbers, ',') as cellnumber, g.group_name, g.group_id, p.profile_code, p.profile_id,
327  p.description as profile_desc
328  from ${Cu}user u
329  left join (select uc.user_id, array_agg(distinct s.cellnumber::text) as cellnumbers from ${Cu}useraccounts uc
330  left join cusms s on trim(uc.accountnumber) = trim(s.accountnumber) and s.cu = '${Cu}' group by uc.user_id
331  ) ss on u.user_id = ss.user_id
332  left join ${Cu}group g on u.group_id = g.group_id
333  left join cu_profile p on g.profile_id = p.profile_id where u.user_id = $userId";
334  $sth = db_query($sql, $dbh);
335  if (!$sth) {
336  throw new exception("Query query failed.", 20);
337  }
338  if (db_num_rows($sth) == 0) {
339  throw new exception("Query query didn't return anything.", 21);
340  }
341  $thisUser = db_fetch_assoc($sth, 0);
342  $encryption = encryptUser($Cu, $thisUser, false);
343  $counts = getUserHubCounts($dbh, $Cu, intval($thisUser["user_id"]));
344 
345  $groupEncryption = encryptGroup($dbh, $Cu, $thisUser);
346 
347  return array("code" => 0, "error" => array(), "encryption" => $encryption, "data" => array($thisUser), "counts" => $counts, "groupEncryption" => $groupEncryption,
348  "openAccessControl" => $openAccessControl);
349  } catch(exception $e) {
350  if ($e->getCode() >= 100) {
351  db_work($dbh, HOMECU_WORK_ROLLBACK); // Got greater problems if this fails.
352  }
353 
354  return array("code" => $e->getCode(), "error" => array($e->getMessage()), "data" => array());
355  }
356 }
357 
358 /**
359  * function printDoAddUser($self, $callbackName, $formName, $itIsKnownKhaleesi, $knownGroupId, $closeDialog)
360  * Function to handle packaging for the data call to add a user.
361  *
362  * @param $self -- refers to the location of this script: main.prg?ft=15 I believe.
363  * @param $callbackName -- the callback function on the user hub side to call.
364  * @param $formName -- the name of the form to validate.
365  * @param $itIsKnownKhaleesi -- true if the groupId is known.
366  * @param $knownGroupId -- the groupId if known.
367  * @param $closeDialog -- if there is a dialog to close.
368  */
369 function printDoAddUser($self, $callbackName, $formName, $itIsKnownKhaleesi, $knownGroupId, $closeDialog, $usePhonesInsteadOfMFA) { ?>
370  $.homecuValidator.doValidateUsername = false;
371  if ($.homecuValidator.validate()) {
372  var parameters = {username: $("#<?php echo $formName; ?> [name='username']").val().trim(), email: $("#<?php echo $formName; ?> [name='email']").val().trim(),
373  password: $("#<?php echo $formName; ?> [name='passwordi']").val().trim(), confirm: $("#<?php echo $formName; ?> [name='confirmi']").val().trim()};
374 
375  <?php if ($usePhonesInsteadOfMFA) { ?>
376  var phoneData = $("#<?php echo $formName; ?> #phoneGrid").data("kendoGrid").dataSource.data();
377  var phoneArray = [];
378  for(var i = 0; i != phoneData.length; i++) {
379  if (!phoneData[i].isAdd) {
380  phoneArray.push(phoneData[i].value.trim().replace(/\D+/g, ""));
381  }
382  }
383  parameters.phones = kendo.stringify(phoneArray);
384  <?php } ?>
385 
386  <?php if ($itIsKnownKhaleesi) { ?>
387  parameters.groupId = <?php echo intval($knownGroupId); ?>;
388  <?php } else { ?>
389  switch($("#<?php echo $formName; ?> [name='group']:checked").data("val"))
390  {
391  case "addToGroup": parameters.groupId = $("#userAddDialog [name='groupPopup']").data("id").trim(); break;
392  case "newGroup": parameters.profileId = profileDDL.value(); break;
393  }
394  <?php } ?>
395 
396  showWaitWindow();
397  $.post("<?php echo $self; ?>&operation=addUser", parameters, function(data) {
398  hideWaitWindow();
399  if (data.error.length > 0) {
400  $.homecuValidator.displayMessage(data.error, $.homecuValidator.settings.statusError );
401  } else {
402  <?php if ($closeDialog) { ?>
403  if (typeof(<?php echo $callbackName; ?>) == "function") {
404  <?php echo $callbackName; ?>("successfulOneRecord", data.encryption, data.data[0], data.counts, data.groupEncryption, data.openAccessControl);
405  }
406 
407 
408  $("#userAddDialog").data("invalid", false);
409  $("#userAddDialog").data("kendoDialog").close();
410  <?php } else { ?>
411  window.location.href = "main.prg?ft=22&payload=" + encodeURIComponent(encodeURI(data.encryption));
412  <?php } ?>
413  }
414  });
415  }
416  $.homecuValidator.doValidateUsername = true;
417 <?php }
418 
419 /**
420  * function printAddUserInit($selectGroupOrProfile, $profileDDL, $formName)
421  * Function to set up all the add user components.
422  *
423  * @param $selectGroupOrProfile -- If true, then group or profile selection is included in the package.
424  * @param $profileDDL -- the dropdownlist data for the profile.
425  * @param $formName -- the name of the form.
426  */
427 function printAddUserInit($selectGroupOrProfile, $profileDDL, $profileDefault, $formName, $usePhonesInsteadOfMFA)
428 { ?>
429 
430  <?php if ($selectGroupOrProfile) { ?>
431  $("#userAddDialog [name='groupPopup']").click(function() {
432 
433  <?php // close group search module if loaded ?>
434  if (groupSearch != null) {
435  groupSearch.Close();
436  }
437 
438  <?php // load group search module ?>
439  $("#groupSearchModule").empty();
440  $("#groupSearchModule").load("main.prg?ft=102102", function(response) {
441  groupSearch = new GroupSearch();
442  groupSearch.Data([]);
443  groupSearch.Init(groupCallback, this);
444  groupSearch.Open(window.activeWindows);
445  });
446  });
447 
448  profileDDL = $("#<?php echo $formName; ?> #profileDDL").kendoDropDownList({
449  dataTextField: "text",
450  dataValueField: "value",
451  dataSource: {
452  data: <?php echo HCU_JsonEncode($profileDDL); ?>
453  },
454  filter: "startswith",
455  value: <?php echo HCU_JsonEncode($profileDefault); ?>
456  }).data("kendoDropDownList");
457  <?php } // End if ($selectGroupOrProfile) { ?>
458 
459  <?php if ($usePhonesInsteadOfMFA)
460  printInitPhoneGrid($formName);
461 }
462 
463 function printInitPhoneGrid($formName, $data = "")
464 { ?>
465  var data = [{kendoId: 1, value: 0, isAdd: true}];
466  var kendoId = 2;
467  <?php if ($data != "") { ?>
468  var predata = <?php echo $data; ?>;
469  <?php // Bad data got into the phones column so have to check if the data is an array and if the contents are strings like expected. If not, then it will show up as empty. #1232. ?>
470  if (Array.isArray(predata)) {
471  for (var i = 0, iLength = predata.length; i <= iLength; i++) {
472  if (typeof(predata[i]) == "string") {
473  value = predata[i].replace(/\D+/g, "");
474  value = "(" + value.substring(0,3) + ") " + value.substring(3,6) + "-" + value.substring(6);
475  data.push({kendoId: kendoId++, value: value, isAdd: false});
476  }
477  }
478  }
479 
480  <?php } ?>
481  var inPhoneSave = false;
482  var phoneGrid = $("#<?php echo $formName; ?> #phoneGrid").kendoGrid({
483  dataSource: {
484  data: data,
485  schema: {
486  model: {
487  id: "kendoId",
488  fields: {
489  kendoId: {type: "number"},
490  value: {type: "string"},
491  isAdd: {type: "boolean"}
492  }
493  }
494  },
495  sort: [{field: "isAdd", dir: "asc"}, {field: "kendoId", dir: "asc"}]
496  },
497  editable: {
498  confirmation: false
499  },
500  height: 100,
501  columns: [
502  {template: '<span class="fa fa-minus-circle"></span>', width: 30},
503  {field: "value", template: "<div class='validateable'># if (value == null) { # &nbsp; # } else { # #: value # # } #</div>", editor: function(container, options) {
504  var mtb = $("<input class='validatePhone' name='" + options.field + "'>").appendTo(container).kendoMaskedTextBox({
505  mask: "(000) 000-0000"
506  }).data("kendoMaskedTextBox");
507  }}],
508  rowTemplate: "# if (isAdd) { # <tr class='addRow'><td class='addBtn'><span class='fa fa-plus-circle'></span></td><td>Add phone</td></tr> # } else { #"
509  + " <tr data-uid='#: uid #'><td class='removeBtn'><span class='fa fa-minus-circle'></span></td>"
510  + "<td class='phoneValueTD'><div class='validateable'># if (value == null) { # &nbsp; # } else { # #: value # # } #</div></td></tr> # } #",
511  save: function(e) {
512  $("#<?php echo $formName; ?> #phoneGrid").data("phonesChanged", true);
513  window.setTimeout(function() { $("#<?php echo $formName; ?> [name='validatePhonesHidden']").blur(); <?php /* validate grid */ ?>}, 100);
514  }
515  }).data("kendoGrid");
516 
517  $("#<?php echo $formName; ?> #phoneGrid").data("phonesChanged", false);
518 
519  $("#<?php echo $formName; ?> #phoneGrid").css({height: "auto"});
520  $("#<?php echo $formName; ?> #phoneGrid .k-grid-header").remove();
521 
522  $("#<?php echo $formName; ?> #phoneGrid").on("mousedown", ".removeBtn", function() {
523  $("#<?php echo $formName; ?> #phoneGrid").data("phonesChanged", true);
524  $("#<?php echo $formName; ?> #phoneGrid .validatePhone:visible").blur();
525  window.setTimeout(function() { $("#<?php echo $formName; ?> [name='validatePhonesHidden']").blur(); <?php /* validate grid */ ?>}, 100);
526  phoneGrid.saveRow();
527  phoneGrid.removeRow($(this).closest("tr"));
528  return false;
529  });
530 
531  $("#<?php echo $formName; ?> #phoneGrid").on("mousedown", ".addRow", function() {
532  $("#<?php echo $formName; ?> #phoneGrid").data("phonesChanged", true);
533  $("#<?php echo $formName; ?> #phoneGrid .validatePhone:visible").blur();
534  window.setTimeout(function() { $("#<?php echo $formName; ?> [name='validatePhonesHidden']").blur(); <?php /* validate grid */ ?>}, 100);
535  phoneGrid.saveRow();
536  phoneGrid.addRow();
537  return false;
538  });
539 
540  var toolTipProps = homecuTooltip.defaults;
541  toolTipProps.filter = ".validatePhone";
542  toolTipProps.content = "Mobile Number";
543 
544  $("#<?php echo $formName; ?> #phoneGrid").kendoTooltip(toolTipProps);
545 
546 <?php }
547 
548 /**
549  * function printAddUserValidationSetup($self, $passwordRules, $formValidate, $formStatusField, $doValidateGroup)
550  * Function sets up the homecuValidator.
551  *
552  * @param $self -- the location of this script: main.prg?ft=15
553  * @param $passwordRules -- the password rules from the cuadmin record for the credit union.
554  * @param $formValidate -- the form to validate.
555  * @param $formStatusField -- the DIV to use for statuses.
556  * @param $doValidateGroup -- boolean to include the group validation. (Doesn't happen for the add user card because that can only add a user to the current group.)
557  */
558 function printAddUserValidationSetup($self, $passwordRules, $formValidate, $formStatusField, $doValidateGroup, $usePhonesInsteadOfMFA) {
559  // Setting up the homecuValidator doesn't clear out previous custom rules.
560  ?>
561  $.homecuValidator.settings.homecuCustomRules.validateGroup = undefined;
562 
563  $.homecuValidator.setup({formValidate:'<?php echo $formValidate; ?>', formStatusField: '<?php echo $formStatusField; ?>', homecuCustomRules: {
564  confirmA: function(input) {
565  if ($(input).is("#<?php echo $formValidate; ?> [name='confirmi']"))
566  {
567  var password = $("#<?php echo $formValidate; ?> [name='passwordi']").val().trim();
568  if (password == "") {
569  return true;
570  }
571  if (password != $(input).val().trim()) {
572  $(input).attr("data-confirmA-msg", "Passwords must match.");
573  return false;
574  }
575  }
576  return true;
577  },
578  validateUsername1: function(input) {
579  if ($(input).is("#<?php echo $formValidate; ?> [name='username']")) {
580  var theHidden = $("#<?php echo $formValidate; ?> [name='usernameHidden']");
581  if ($(input).val().trim() == "") {
582  $(input).attr("data-validateUsername1-msg", "Username is required.");
583  $(theHidden).attr("data-validateUsername2-msg", "");
584  $("#<?php echo $formValidate; ?> .k-invalid-msg[data-for='usernameHidden']").empty();
585  return false;
586  } else if ($.homecuValidator.doValidateUsername) {
587  $(theHidden).attr("data-validateUsername2-msg", "");
588  $.post("<?php echo $self; ?>&operation=validateUsername", {username: $(input).val().trim()}, function(data) {
589  if (data.status === "000") {
590  $(input).removeClass("k-invalid");
591  } else {
592  $(theHidden).attr("data-validateUsername2-msg", data.error);
593  $(input).addClass("k-invalid");
594  }
595  $(theHidden).blur();
596  });
597  }
598  }
599  return true;
600  },
601  validateUsername2: function(input) {
602  if ($(input).is("#<?php echo $formValidate; ?> [name='usernameHidden']")) {
603  if ($(input).attr("data-validateUsername2-msg") != null && $(input).attr("data-validateUsername2-msg") != "") {
604  $("#<?php echo $formValidate; ?> [name='username']").addClass("k-invalid");
605  $.homecuValidator.showErrorMessage = true;
606  return false;
607  }
608  }
609  return true;
610  }
611  <?php if ($usePhonesInsteadOfMFA) {
612  echo ","; printValidatePhones($formValidate);
613  } ?>
614  , validatepassword: function(input) {
615  if (!input.is("[name='passwordi']")) {
616  return true;
617  }
618  var password = $(input).val();
619  if (password.length < 4) {
620  $(input).attr("data-validatepassword-msg", "Password must be four or more characters.");
621  return false;
622  }
623  var invalidMatches = password.match(/['"\s]/g);
624  if (invalidMatches != null && invalidMatches.length > 0) {
625  $(input).attr("data-validatepassword-msg", "Password has illegal characters.");
626  return false;
627  }
628  return true;
629  }
630  <?php if ($doValidateGroup) { ?>,
631  validateGroup: function(input) {
632  if ($(input).is("[name='validateGroup']")) {
633  switch($("#<?php echo $formValidate; ?> [name='group']:checked").data("val")) {
634  case "addToGroup":
635  if ($("#<?php echo $formValidate; ?> [name='groupPopup']").text().trim() == "(none selected)") {
636  $(input).attr("data-validateGroup-msg", "Group must be selected.");
637  return false;
638  }
639  break;
640  case "newGroup":
641  if ($("#<?php echo $formValidate; ?> #profileDDL").data("kendoDropDownList").value() == "") {
642  $(input).attr("data-validateGroup-msg", "Profile must be selected.");
643  return false;
644  }
645  break;
646  default:
647  $(input).attr("data-validateGroup-msg", "Please specify new group or add to group.");
648  return false;
649  }
650  }
651  return true;
652  }
653  <?php } ?>
654  }});
655  $.homecuValidator.doValidateUsername = true;
656  $.homecuValidator.passwordRules = <?php echo HCU_JsonEncode($passwordRules); ?>;
657 <?php }
658 
659 function printValidatePhones($formValidate) { ?>
660  validatePhones: function(input) {
661  if ($(input).is("[name='validatePhonesHidden']")) {
662  var valid = true;
663 
664  $("#<?php echo $formValidate; ?> #phoneGrid .phoneValueTD").removeClass("k-invalid");
665  $("#<?php echo $formValidate; ?> #phoneGrid .phoneValueTD .validateable").removeClass("k-invalid");
666  var numPhones = 0, phoneMap = {};
667  $("#<?php echo $formValidate; ?> #phoneGrid .phoneValueTD .validateable").each(function() {
668  if ($(this).text().trim() != "") {
669  numPhones++;
670  var value = $(this).text().trim();
671 
672  if (value.match(/^\(\d{3}\) \d{3}-\d{4}$/) == null) {
673  valid = false;
674  $(input).attr("data-validatePhones-msg", "One or more phones are invalid.");
675  } else if (numPhones > 5) {
676  valid = false;
677  $(input).attr("data-validatePhones-msg", "You cannot have over 5 phone numbers.");
678  } else if (phoneMap[value] != null) {
679  valid = false;
680  $(input).attr("data-validatePhones-msg", "You have duplicate phone numbers.");
681  }
682 
683  phoneMap[value] = true;
684  if (!valid) {
685  $(this).addClass("k-invalid");
686  $(this).closest(".phoneValueTD").remove("k-invalid");
687  }
688  }
689  });
690  if (!valid) {
691  return false;
692  }
693  }
694  return true;
695  }
696 <?php }
697 
698 /**
699  * function printAddNewUser($self, $callbackName, $dbh, $Cu)
700  * This prints the user search open function and whatnot
701  *
702  * @param string $self -- main.prg?ft=$ft
703  * @param string $callbackName -- the name to the callback function (when the user is created)
704  * @param integer $dbh -- the database connection
705  * @param string $Cu -- the credit union
706  */
707 function printAddNewUser($self, $callbackName, $dbh, $Cu) {
708  $record = readForAddUser($dbh, $Cu);
709  $formName = "addNewUserForm";
710  $formValidateDiv = "userNewValidation";
711 
712  /**
713  * function openAddNewUser(fromSearchUser)
714  * This function opens the add user dialog.
715  *
716  * @param boolean fromSearchUser -- if coming from the user search, then on cancel, return to this.
717  */
718  ?>
719  function openAddNewUser(fromSearchUser, autoFill) {
720  var userAddDialog = $("#userAddDialog").data("kendoDialog");
721  var groupSearch = null;
722  var profileDDL = null;
723  if (userAddDialog == null) {
724  var phoneGridIndex = 2;
725  var content = '<div class="userSearchTop container hcu-all-100"><div class="row">'
726  + '<div id="<?php echo $formValidateDiv; ?>"></div></div></div><br>'
727  + '<div class="userSearchContents container hcu-all-100"><form id="<?php echo $formName; ?>">'
728  + '<div class="row hcuSpacer"><div class="col-xs-4 hcu-no-padding"><label>Username*</label></div>'
729  + '<div class="col-xs-8 hcu-no-padding"><input name="username" type="text" maxlength="50" class="hcu-all-100 k-input k-textbox">'
730  + '<span data-for="usernameHidden" class="k-invalid-msg"></span></div></div>'
731  <?php dialogPrintInputLine("Email*", "", "email", 255, " required data-required-msg=\"Email is required.\"", "email", false); ?>
732  + '<div class="row hcuSpacer"><div class="col-xs-4 hcu-no-padding"><label>Password*</label></div><div class="col-xs-8 hcu-no-padding">'
733  + '<input name="passwordi" type="password" maxlength="255" class="hcu-all-100 k-input k-textbox"></div></div>'
734  <?php dialogPrintInputLine("Confirm*", "", "confirmi", 255, false, "password", false); ?>
735  + '<div class="row hcuSpacer"><div class="radio"><label class="col-xs-4 hcu-no-padding"><input type="radio" name="group" data-val="addToGroup" checked>Add To Group</label></div>'
736  + '<div class="col-xs-8 hcu-no-padding"><a href="#" name="groupPopup" data-id="">(none selected)</a></div></div>'
737  + '<div class="row hcuSpacer"><div class="radio"><label class="col-xs-4 hcu-no-padding"><input type="radio" name="group" data-val="newGroup">Add To Profile</label></div>'
738  + '<div class="col-xs-8 hcu-no-padding"><div id="profileDDL" class="hcu-all-100"></div></div></div>'
739  <?php if ($record["usePhonesInsteadOfMFA"]) { ?>
740  + '<div class="row hcuSpacer"><span class="h3">Phones</span></div>'
741  + '<div class="row hcuSpacer"><div id="phoneGrid"></div></div>'
742  <?php } ?>
743  + '<input type="hidden" name="usernameHidden"><input type="hidden" name="validatePhonesHidden"><input type="hidden" name="validatePhoneTypesHidden">'
744  + '<input type="hidden" name="validateGroup">'
745  + '</form></div>';
746  userAddDialog = $("<div id='userAddDialog' class='searchDialog'></div>").appendTo("body").kendoDialog({
747  title: "New User",
748  content: content,
749  actions: [{text: "Cancel"},
750  {text: "Add User", primary: true, action: function() {
751  var fromSearchUser = $("#userAddDialog").data("fromSearchUser");
752  <?php printDoAddUser($self, $callbackName, $formName, false, null, true, $record["usePhonesInsteadOfMFA"]); ?>
753  return false;
754  }}],
755  open: function() {
756  if (window.activeWindows != null) {
757  window.activeWindows.push(this);
758  }
759  $("#userAddDialog").data("invalid", true);
760  },
761  show: function() {
762  <?php printAddUserValidationSetup($self, $record["passwordRules"], $formName, $formValidateDiv, true, $record["usePhonesInsteadOfMFA"]); ?>
763  <?php if ($record["code"] != 0) { ?>
764  $.homecuValidator.displayMessage(<?php echo HCU_JsonEncode($record["error"]); ?>, $.homecuValidator.settings.statusError );
765  <?php } ?>
766  },
767  close: function() {
768  if (window.activeWindows != null) {
769  window.activeWindows.pop();
770  }
771  var fromSearchUser = $("#userAddDialog").data("fromSearchUser");
772  var invalid = $("#userAddDialog").data("invalid");
773  if (fromSearchUser && invalid) {
774  $("#userSearchDialog").data("kendoDialog").open();
775  }
776  },
777  minWidth: 300,
778  maxWidth: 450,
779  width: "90%",
780  visible: false,
781  modal: true
782  }).data("kendoDialog");
783 
784  <?php printAddUserInit(true, $record["profileDDL"], $record['profileDefault'], $formName, $record["usePhonesInsteadOfMFA"]); ?>
785  }
786  else <?php // Clear all inputs ?>
787  {
788  $("#<?php echo $formName; ?> [name='username']").val(null);
789  $("#<?php echo $formName; ?> [name='email']").val(null);
790  $("#<?php echo $formName; ?> [name='passwordi']").val(null);
791  $("#<?php echo $formName; ?> [name='confirmi']").val(null);
792  $("#<?php echo $formName; ?> [name='group']:eq(0)").click();
793  <?php if ($record["usePhonesInsteadOfMFA"]) { ?>
794  $("#<?php echo $formName; ?> #phoneGrid").data("kendoGrid").dataSource.data([{kendoId: 1, type: "", value: 0, isAdd: true}]);
795  <?php } ?>
796  $("#<?php echo $formName; ?> [name='groupPopup']").attr("data-id", "").text("(none selected)");
797  $("#<?php echo $formName; ?> #profileDDL").data("kendoDropDownList").value("");
798  $("#<?php echo $formName; ?> .k-invalid").removeClass("k-invalid");
799  }
800 
801  if (autoFill != null) {
802  $("#<?php echo $formName; ?> [name='username']").val(autoFill);
803  }
804 
805  if (fromSearchUser) {
806  $("#userSearchDialog").data("kendoDialog").close();
807  }
808  userAddDialog.open();
809  $("#userAddDialog").data("fromSearchUser", fromSearchUser);
810  }
811 
812  <?php
813  /**
814  * function groupCallback(unused, groupInfo)
815  * Run this function on return of the group search dialog.
816  *
817  * @param null unused -- there are two parameters in the groupSearch return function and I don't care about the first one.
818  * @param {} groupInfo -- the info that I need.
819  */
820  ?>
821  function groupCallback(apparentlyItActuallyIsUsed, groupInfo) {
822  if (apparentlyItActuallyIsUsed == "updateGroupInfo") {
823  $("#<?php echo $formName; ?> [name='groupPopup']").data("id", groupInfo.group.g_id);
824  $("#<?php echo $formName; ?> [name='groupPopup']").text(groupInfo.group.g_name);
825  }
826  }
827 <?php }