Odyssey
AppAdm.data
1 <?php
2 
3 require_once(__DIR__ . '/../library/DateConvert.i');
4 
5 /**************************
6 LITERALS
7 ***************************/
8 
9 /**
10  * function GetSearchInData()
11  * Gets the list for the search in dropdownlist.
12  * @return array
13  */
14 function GetSearchInData() {
15  return array(
16  array("value" => "", "text" => "Select..."),
17  array("value" => "email", "text" => "Email Address"),
18  array("value" => "dob", "text" => "Date Of Birth"),
19  array("value" => "ssn", "text" => "Social Security #"),
20  array("value" => "name", "text" => "Last Name"),
21  array("value" => "account", "text" => "Account #"));
22 }
23 
24 /**
25  * function GetApplicantSearchInData()
26  * Gets the options for the search in dropdownlist in the applicant tab.
27  * @return array
28  */
29 function GetApplicantSearchInData() {
30  return array(
31  array("value" => "email", "text" => "Email"),
32  array("value" => "account", "text" => "Account #"),
33  array("value" => "username", "text" => "Username")
34  );
35 }
36 
37 /**
38  * function GetWithStatusData()
39  * Gets the list for the status dropdownlist.
40  * @return array
41  */
42 function GetWithStatusData() {
43  return array(
44  array("value" => "", "text" => "All"),
45  array("value" => "NoStat", "text" => "Submitted"),
46  array("value" => "SubErr", "text" => "Submitted w/Errors"),
47  array("value" => "Approved", "text" => "Approved"),
48  array("value" => "Rejected", "text" => "Rejected"),
49  array("value" => "Pending", "text" => "Pending"),
50  array("value" => "ReqReview", "text" => "Requires Review"));
51 }
52 
53 /**
54  * function GetRangeTypeData()
55  * Gets the list for the range type dropdownlist.
56  * @return array
57  */
58 function GetRangeTypeData() {
59  return array(
60  array("value" => "", "text" => "ALL Applications"),
61  array("value" => "submit", "text" => "Applications Submitted between"),
62  array("value" => "start", "text" => "Applications Started between"),
63  array("value" => "modified", "text" => "Applications Last Modified between"));
64 }
65 
66 /**
67  * function GetSortByData()
68  * Gets the list for the sort by dropdownlist.
69  * @return array
70  */
71 function GetSortByData() {
72  return array(
73  array("value" => "startAD", "text" => "Most recently started first"),
74  array("value" => "startBC", "text" => "Most recently started last"),
75  array("value" => "statusA", "text" => "Status ordered A-Z"),
76  array("value" => "statusZ", "text" => "Status ordered Z-A"),
77  array("value" => "typeA", "text" => "Type ordered A-Z"),
78  array("value" => "typeZ", "text" => "Type ordered Z-A"),
79  array("value" => "nameA", "text" => "Name ordered A-Z"),
80  array("value" => "nameZ", "text" => "Name ordered Z-A"));
81 }
82 
83 /**
84  * function GetActionData()
85  * Gets the list for the action dropdownlist.
86  * @return array
87  */
88 function GetActionData() {
89  return array(
90  array("value" => "", "text" => "Action"),
91  array("value" => "delete", "text" => "Delete"),
92  array("value" => "view", "text" => "View Application"));
93 }
94 
95 /**
96  * function GetFailedLoginAttempts()
97  * Gets how many failed login attempts are allowed.
98  * @return integer
99  */
100 function GetFailedLoginAttempts() {
101  return 5;
102 }
103 
104 /**************************
105 DATA FUNCTIONS
106 **************************/
107 
108 /**
109  * function DeleteLoanApps($dbh, $Cu, $parameters)
110  * Deletes the lnappuserresponse record (and then return the grid without the records.)
111  *
112  * @param $dbh -- the database connection
113  * @param $Cu -- the credit union
114  * @param $parameters -- an array of parameters from the request string.
115  *
116  * @return $status -- "000" if successful; nonzero otherwise.
117  * @return $error -- "" if successful; nonempty otherwise.
118  * @return $info -- If successful: "Loans were deleted successfully."
119  * @return $gridData.total -- how many grid records there are.
120  * @return $gridData.data -- the grid records.
121  */
122 function DeleteLoanApps($dbh, $Cu, $parameters) {
123  try {
124  $isDelete = true;
125  $results = _ValidateReadLoanAppsParameters($parameters, $isDelete);
126  if ($results["status"] !== "000") {
127  throw new exception ($results["error"], 1);
128  }
129  $parameters = $results["data"];
130 
131  $sql = "delete from lnappuserresponse where respid in
132  (select r.respid from lnappuserresponse r
133  inner join lnappuser u on r.userid = u.userid
134  where u.cu = '$Cu' and r.respid in " . $parameters["deleteIn"] . ")";
135  // I like guarantees that no data outside the CU will be deleted.
136 
137  $sth = db_query($sql, $dbh);
138  if (!$sth) {
139  throw new exception ("Delete query failed.", 2);
140  }
141 
142  $returnArray = ReadLoanApps($dbh, $Cu, $parameters, $isDelete);
143  $returnArray["info"] = "Loans were deleted successfully."; // Give a success message.
144  } catch (exception $e) {
145 
146  // Requery the grid regardless. Read should already have worked as this point.
147  $returnArray = ReadLoanApps($dbh, $Cu, $parameters, $isDelete);
148  $returnArray["status"] = $e->getCode();
149  $returnArray["error"] = $e->getMessage();
150  }
151  return $returnArray;
152 }
153 
154 /**
155  * function ReadLoanApps($dbh, $Cu, $parameters, $isDelete)
156  * Reads the grid with the current sort and filter settings.
157  *
158  * @param $dbh -- the database connection
159  * @param $Cu -- the credit union
160  * @param $parameters -- the request parameters
161  * @param $isDelete -- If the call is actually deleting records, bypass validation (already done)
162  *
163  * @return $status -- "000" if successful; nonzero otherwise.
164  * @return $error -- "" if successful; nonempty otherwise.
165  * @return $gridData.total -- how many grid records there are.
166  * @return $gridData.data -- the grid records.
167  */
168 function ReadLoanApps($dbh, $Cu, $parameters, $isDelete) {
169  try {
170  // Prevent double validation if deleting.
171  if (!$isDelete) {
172  $results = _ValidateReadLoanAppsParameters($parameters, false);
173  if ($results["status"] !== "000") {
174  throw new exception ($results["error"], 1);
175  }
176  extract($results["data"]);
177  } else {
178  extract($parameters);
179  }
180 
181 
182  // Timezone needs to be part of the query due to filtering based on the start, submit, or modified date.
183  $tz = GetCreditUnionTimezone($dbh, $Cu);
184 
185  $select = "set time zone '$tz';
186  select case when l.respstatus = '000' then 'Submitted'
187  when l.respstatus in ('020','021','022') then 'Submitted With Errors'
188  when l.respstatus = '027' then 'Approved'
189  when l.respstatus = '028' then 'Rejected'
190  when l.respstatus = '026' then 'Pending'
191  when l.respstatus in ('029','030') then 'Requires Review'
192  else '' end as status,
193  trim(l.resplname) || ', ' || trim(l.respfname) || ' ' || l.respmname as name, m.loantitle, l.respid, a.userid, l.loanid,
194  l.respstatus, l.respamt, l.respstarton, l.respdob, l.respssn, l.respmember, lower(a.email) as email, l.respcoreloanappid";
195  $sql = "from lnappuser a
196  inner join lnappuserresponse l on a.userid = l.userid
197  inner join lnappschemamaster m on l.loanid = m.loanid";
198 
199  $where = array("a.cu = '" . prep_save($Cu, 10) . "'");
200  if ($userId != 0) {
201  $where[] = "a.userid = " . intval($userId);
202  } else if ($lookFor != "") {
203  switch ($searchIn) {
204  case "email":
205  $where[] = "lower(a.email) = '" . strtolower(prep_save($lookFor, 50)) . "'";
206  break;
207  case "dob":
208  $where[] = "l.respdob = '" . prep_save($lookFor, 10) . "'";
209  break;
210  case "ssn":
211  $where[] = "l.respssn = '" . prep_save($lookFor, 11) . "'";
212  break;
213  case "name":
214  $where[] = "strpos(lower(l.resplname), '" . strtolower(prep_save($lookFor, 55)) . "') > 0";
215  break;
216  case "account":
217  $where[] = "l.respmember = '" . prep_save($lookFor, 15) . "'";
218  break;
219  case "loan":
220  $where[] = "l.respcoreloanappid = '" . prep_save($lookFor, 15) . "'";
221  break;
222  }
223  }
224 
225  if ($withStatus != "") {
226  switch ($withStatus) {
227  case 'NoStat':
228  // ** Try to show any loan that does not have any other form of status -- started, but not finished, or a zero..
229  $where[] = "(l.respstatus = '000' or l.respstatus = '0' or l.respstatus is null)";
230  break;
231  case 'SubErr':
232  $where[] = "l.respstatus in ('020','021','022')";
233  break;
234  case 'Approved':
235  $where[] = "l.respstatus = '027'";
236  break;
237  case 'Rejected':
238  $where[] = "l.respstatus = '028'";
239  break;
240  case 'Pending':
241  $where[] = "l.respstatus = '026'";
242  break;
243  case 'ReqReview':
244  $where[] = "l.respstatus in ('029','030')";
245  break;
246  case 'All':
247  default:
248  # show all loanapps regardless of status
249  }
250  }
251 
252  // ** Date Range Filter
253  if ($rangeType != "") {
254  $dateFilterField = "";
255  switch ($rangeType) {
256  case "submit":
257  $dateFilterField = 'l.respsubmiton';
258  break;
259  case "start":
260  $dateFilterField = 'l.respstarton';
261  break;
262  case "modified":
263  $dateFilterField = 'l.respmodifiedon';
264  break;
265  }
266  if ($dateFilterField != "") {
267  if ($fromDate != "") {
268  $where[] = "$dateFilterField >= '" . prep_save($fromDate) . "'";
269  }
270  if ($toDate != "") {
271  $where[] = "$dateFilterField <= '" . prep_save($toDate) . "'";
272  }
273  }
274  }
275 
276  if (count($where) > 0) {
277  $sql .= " where " . implode(" and ", $where);
278  }
279 
280  switch($sortBy) {
281  case "startAD":
282  $sortSQL = "order by l.respstarton desc";
283  break;
284  case "startBC":
285  $sortSQL = "order by l.respstarton asc";
286  break;
287  case "statusA":
288  $sortSQL = "order by 1 asc";
289  break;
290  case "statusZ":
291  $sortSQL = "order by 1 desc";
292  break;
293  case "typeA":
294  $sortSQL = "order by m.loantitle asc";
295  break;
296  case "typeZ":
297  $sortSQL = "order by m.loantitle desc";
298  break;
299  case "nameA":
300  $sortSQL = "order by 2 asc";
301  break;
302  case "nameZ":
303  $sortSQL = "order by 2 desc";
304  break;
305  default:
306  $sortSQL = "";
307  break;
308  }
309 
310  $limitSQL = $limit == 0 && $offset == 0 ? "" : "limit $limit offset $offset";
311 
312  $countSQL = "select count(*) $sql";
313  $mainSQL = "$select $sql $sortSQL $limitSQL";
314 
315  $sth = db_query($countSQL, $dbh);
316  if (!$sth) {
317  throw new exception ("count query failed.", 2);
318  }
319  $results = db_fetch_row($sth, 0);
320  $total = intval($results[0]);
321 
322  $sth = db_query($mainSQL, $dbh);
323  if (!$sth) {
324  throw new exception ("main query failed.", 3);
325  }
326  $results = db_fetch_all($sth);
327  $results = $results === false ? array() : $results;
328 
329  $gridData = array();
330  foreach($results as $record) {
331  $gridData[] = array("loanappid" => $record["respcoreloanappid"], "start" => $record["respstarton"], "account" => $record["respmember"],
332  "status" => $record["status"], "respid" => $record["respid"], "dob" => $record["respdob"], "title" => $record["loantitle"],
333  "ssn" => $record["respssn"], "email" => $record["email"], "name" => $record["name"], "userid" => $record["userid"]);
334  }
335 
336  $returnArray = array("status" => "000", "error" => "", "gridData" => array("total" => $total, "data" => $gridData));
337  } catch (exception $e) {
338  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage(), "gridData" => array("total" => 0, "data" => array()));
339  }
340  return $returnArray;
341 }
342 
343 /**
344  * function _ValidateReadLoanAppsParameters($parameters, $isDelete)
345  * This function validates the parameters for read and delete operations.
346  *
347  * @param $parameters -- the parameters from the request line.
348  * @param $isDelete -- if delete, also need to validate the "loanids" parameter.
349  *
350  * @return $status -- "000" if successful; nonzero otherwise.
351  * @return $error -- "" if successful; nonempty otherwise.
352  * @return $data -- the sanitized version of the parameters.
353  */
354 function _ValidateReadLoanAppsParameters($parameters, $isDelete) {
355  try {
356  if ($isDelete) {
357  $loanids = HCU_array_key_value("loanids", $parameters);
358  $loanids = $loanids === false ? "" : trim($loanids);
359  if ($loanids == "") {
360  throw new exception ("Loan ids are required.", 14);
361  }
362  $loanids = HCU_JsonDecode($loanids);
363  if (count($loanids) == 0) {
364  throw new exception ("Loan ids are required.", 15);
365  }
366  foreach($loanids as $i => $loanid) {
367  if (!ctype_digit(strval($loanid))) {
368  throw new exception ("Loan ids are invalid.", 16);
369  }
370  }
371  $parameters ["deleteIn"] = "(" . implode(", ", $loanids) . ")";
372  }
373 
374  // Search in validation
375  if (HCU_array_key_exists("searchIn", $parameters)) {
376  $searchInData = GetSearchInData();
377  $found = false;
378  foreach($searchInData as $searchInRow) {
379  if ($searchInRow["value"] == $parameters["searchIn"]) {
380  $found = true;
381  break;
382  }
383  }
384  if (!$found) {
385  throw new exception ("Search in is invalid.", 1);
386  }
387  } else {
388  $parameters["searchIn"] = "";
389  }
390 
391  // look for validation
392  if ($parameters["searchIn"] != "" && HCU_array_key_exists("lookFor", $parameters)) {
393  $parameters["lookFor"] = trim($parameters["lookFor"]);
394  } else {
395  $parameters["lookFor"] = "";
396  }
397 
398  switch($parameters["searchIn"]) {
399  case "":
400  break; // Not searching for anything.
401  case "email":
402  if (!validateEmail($parameters["lookFor"])) {
403  throw new exception ("Email is invalid.", 3);
404  }
405  break;
406  case "dob":
407  // DateConvert class, also optional last param $century could be used if
408  // abberations show up (they shouldn't for a birth date search)
409  $parameters["lookFor"] = FilterDateForDatabase($parameters["lookFor"], 'm/d/Y');
410  if (empty($parameters["lookFor"])) {
411  throw new exception ("Date is invalid.", 4);
412  }
413  break;
414  case "ssn":
415  if (!ValidateSSN($parameters["lookFor"])) {
416  throw new exception ("SSN is invalid.", 5);
417  }
418  break;
419  case "account":
420  case "loan":
421  if (!ctype_digit(strval($parameters["lookFor"]))) {
422  throw new exception ("search string needs to be a number.", 6);
423  }
424  break;
425  }
426 
427  // Status validation
428  if (HCU_array_key_exists("withStatus", $parameters)) {
429  $statusData = GetWithStatusData();
430  $found = false;
431  foreach($statusData as $statusRow) {
432  if ($statusRow["value"] == $parameters["withStatus"]) {
433  $found = true;
434  break;
435  }
436  }
437  if (!$found) {
438  throw new exception ("Status is invalid.", 2);
439  }
440  } else {
441  $parameters["withStatus"] = "";
442  }
443 
444  // Range type validation
445  if (HCU_array_key_exists("rangeType", $parameters)) {
446  $rangeTypeData = GetRangeTypeData();
447  $found = false;
448  foreach($rangeTypeData as $rangeTypeRow) {
449  if ($rangeTypeRow["value"] == $parameters["rangeType"]) {
450  $found = true;
451  break;
452  }
453  }
454  if (!$found) {
455  throw new exception ("Range type is invalid.", 7);
456  }
457  } else {
458  $parameters["rangeType"] = "";
459  }
460 
461  // Date validation
462  $parameters["fromDate"] = $parameters["rangeType"] == "" ? false : HCU_array_key_value("fromDate", $parameters);
463  $parameters["fromDate"] = $parameters["fromDate"] === false ? "" : trim($parameters["fromDate"]);
464  $parameters["toDate"] = $parameters["rangeType"] == "" ? false : HCU_array_key_value("toDate", $parameters);
465  $parameters["toDate"] = $parameters["toDate"] === false ? "" : trim($parameters["toDate"]);
466 
467  if ($parameters["fromDate"] == "") {
468  if ($parameters["rangeType"] != "") {
469  throw new exception ("From date is required.", 8);
470  }
471  } else {
472  if (!validateDate($parameters["fromDate"])) {
473  throw new exception ("From date is invalid.", 10);
474  }
475  $parameters["fromDate"] = ModifyDateForDatabase($parameters["fromDate"]);
476  }
477 
478  if ($parameters["toDate"] == "") {
479  if ($parameters["rangeType"] != "") {
480  throw new exception ("To date is required.", 8);
481  }
482  } else {
483  if (!validateDate($parameters["toDate"])) {
484  throw new exception ("To date is invalid.", 12);
485  }
486  $parameters["toDate"] = ModifyDateForDatabase($parameters["toDate"]);
487  }
488 
489  if ($parameters["fromDate"] > $parameters["toDate"]) {
490  throw new exception ("To date cannot be before from date.", 11);
491  }
492 
493  // Sort validation
494  if (HCU_array_key_exists("sortBy", $parameters) && $parameters["sortBy"] != "") {
495  $sortByData = GetSortByData();
496  $found = false;
497  foreach($sortByData as $sortByRow) {
498  if ($sortByRow["value"] == $parameters["sortBy"]) {
499  $found = true;
500  break;
501  }
502  }
503  if (!$found) {
504  throw new exception ("Sort by is invalid.", 13);
505  }
506  } else {
507  $parameters["sortBy"] = "startAD";
508  }
509 
510  $returnArray = array("status" => "000", "error" => "", "data" => $parameters);
511  } catch (exception $e) {
512  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage());
513  }
514  return $returnArray;
515 }
516 
517 /**
518  * Supports multiple date input formats by converting it to the format you need
519  * and allows users to enter date formats that are not so strict. See the
520  * referenced class object for full documentation.
521  * @param string $date
522  * @param string $requested_format DateTime format string (default below)
523  * @param int $century, overrides default IBM 40 year algo - see docs in DateConvert
524  * @return string
525  * @throws Exception
526  */
527 function FilterDateForDatabase($date = '', $requested_format = 'Y-m-d', $century = 0)
528 {
529  $dc = new DateConvert();
530  return $dc->ConvertDateFromFormat($date, $requested_format, $century);
531 }
532 
533 /**
534  * function ModifyDateForDatabase($date)
535  * Changes the date format from MM/dd/yyyy to yyyy-MM-dd which is better for the database.
536  * I made the change so that I can use a common date validation function.
537  *
538  * @param $date -- the old date in MM/dd/yyyy
539  *
540  * @return $date -- the new date in yyyy-MM-dd
541  */
542 function ModifyDateForDatabase($date) {
543  return substr($date, - 4) . "-" . substr($date, 0, 2) . "-" . substr($date, 3, 2);
544 }
545 
546 /**
547  * function ValidateSSN($ssn)
548  * A simple function to validate the syntax of the SSN.
549  * I did not see a function anywhere like validateEmail or validateDate.
550  *
551  * @param $ssn -- the SSN to verify
552  *
553  * @return $passed -- true if the SSN is valid
554  */
555 function ValidateSSN($ssn) {
556  $pattern = '/^\d{3}-\d{2}-\d{4}$/';
557  $result = preg_match($pattern, $ssn);
558  return $result === 1;
559 }
560 
561 /**
562  * function ReadApplicantSearch($dbh, $Cu, $parameters)
563  * This search for the applicant on the applicant tab.
564  *
565  * @param $dbh -- the database connection.
566  * @param $Cu -- the credit union.
567  * @param $parameters -- the parameters.
568  *
569  * @return $status -- "000" if successful; nonzero otherwise.
570  * @return $error -- "" if successful; nonempty otherwise.
571  * @return $gridData.total -- how many grid records there are.
572  * @return $gridData.data -- the grid records.
573  */
574 function ReadApplicantSearch($dbh, $Cu, $parameters) {
575  try {
576  $results = _ValidateApplicantSearch($parameters);
577  if ($results["status"] !== "000") {
578  throw new exception ("Search is invalid.", 1);
579  }
580  extract($parameters);
581 
582  $select = "select ln.userid \"lnId\", ln.email \"email\", coalesce(ln.failedloginattempts, 0) \"failedAttempts\",
583  case when ln.userlogintype = 'H' then 'Home Banking' when ln.userlogintype = 'N' then 'Non-Home Banking Member' else 'Loan App User' end \"loginType\",
584  coalesce(trim(u.user_name), '<None>') \"username\", coalesce(trim(ln.session_account), '<None>') \"accountnumber\"";
585 
586  $sql = "from lnappuser ln
587  left join ${Cu}user u on ln.banking_user_id = u.user_id
588  where ln.cu = '" . prep_save($Cu, 10) . "'";
589 
590  if ($searchIn === "email") {
591  $sql .= " and lower(trim(ln.email)) like '%" . strtolower(prep_save($lookFor, 50)) . "%'";
592  } else if ($searchIn === "account") {
593  $sql .= " and trim(ln.session_account) like '%" . prep_save($lookFor) . "%'";
594  } else if ($searchIn === "username") {
595  $sql .= " and lower(trim(u.user_name)) like '%" . strtolower(prep_save($lookFor)) . "%'";
596  }
597 
598  $sortSQL = "order by ln.userid desc"; // Most recent first?
599  $limitSQL = $limit == 0 && $offset == 0 ? "" : "limit $limit offset $offset";
600 
601  $countSQL = "select count(*) $sql";
602  $mainSQL = "$select $sql $sortSQL $limitSQL";
603 
604  $sth = db_query($countSQL, $dbh);
605  if (!$sth) {
606  throw new exception ("count query failed.", 2);
607  }
608  $results = db_fetch_row($sth, 0);
609  $total = intval($results[0]);
610 
611  $sth = db_query($mainSQL, $dbh);
612  if (!$sth) {
613  throw new exception ("main query failed.", 3);
614  }
615  $gridData = db_fetch_all($sth);
616  $gridData = $gridData === false ? array() : $gridData;
617 
618  $returnArray = array("status" => "000", "error" => "", "gridData" => array("total" => $total, "data" => $gridData));
619  } catch (exception $e) {
620  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage(), "gridData" => array("total" => 0, "data" => array()));
621  }
622  return $returnArray;
623 }
624 
625 /**
626  * function _ValidateApplicantSearch($parameters)
627  * This validates the applicant search data call.
628  *
629  * @param $parameters -- the parameters.
630  *
631  * @return $status -- "000" if successful; nonzero otherwise.
632  * @return $error -- "" if successful; nonempty otherwise.
633  */
634 function _ValidateApplicantSearch($parameters) {
635  try {
636  $searchIn = HCU_array_key_value("searchIn", $parameters);
637  $lookFor = HCU_array_key_value("lookFor", $parameters);
638 
639  if ($searchIn === false || $lookFor === false) {
640  throw new exception ("Search is missing parameters.", 1);
641  }
642  switch($searchIn) {
643  case "email":
644  if (!validateEmail($lookFor)) {
645  throw new exception ("Email is invalid.", 3);
646  }
647  break;
648  case "account":
649  if (!ctype_digit(strval($lookFor))) {
650  throw new exception ("search string needs to be a number.", 4);
651  }
652  break;
653  case "username":
654  break;
655  default:
656  throw new exception ("Search is invalid.", 2);
657  }
658 
659  $returnArray = array("status" => "000", "error" => "");
660  } catch (exception $e) {
661  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage());
662  }
663  return $returnArray;
664 }
665 
666 /**
667  * function ReadApplicant($dbh, $Cu, $parameters)
668  * This reads the applicant (view button on the applicant tab grid.)
669  *
670  * @param $dbh -- the database connection.
671  * @param $Cu -- the credit union.
672  * @param $parameters -- the parameters.
673  *
674  * @return $status -- "000" if successful; nonzero otherwise.
675  * @return $error -- "" if successful; nonempty otherwise.
676  * @return $applicantData -- used for the template for applicant data.
677  * @return $type -- "read" for purpose of determining what to do on return.
678  */
679 function ReadApplicant($dbh, $Cu, $parameters) {
680  try {
681  $userId = HCU_array_key_value("userId", $parameters);
682  if ($userId === false) {
683  throw new exception ("Userid is required.", 1);
684  }
685 
686  $sql = "select ln.userid \"lnId\", ln.email \"email\", coalesce(ln.failedloginattempts, 0) \"failedAttempts\",
687  case when ln.userlogintype = 'H' then 'Home Banking' when ln.userlogintype = 'N' then 'Non-Home Banking Member' else 'Loan App User' end \"loginType\",
688  coalesce(trim(u.user_name), '') \"username\", coalesce(trim(ln.session_account), '') \"accountnumber\",
689  coalesce(trim(ln.confidenceword), '<None>') \"confidence\"
690  from lnappuser ln
691  left join ${Cu}user u on ln.banking_user_id = u.user_id
692  where ln.cu = '" . prep_save($Cu, 10) . "' and ln.userid = " . intval($userId);
693  $sth = db_query($sql, $dbh);
694  if (!$sth) {
695  throw new exception ("query failed.", 5);
696  }
697  if (db_num_rows($sth) <= 0) {
698  throw new exception ("User not found.", 2);
699  }
700  $applicantData = db_fetch_assoc($sth, 0);
701 
702  $applicantData["isLocked"] = $applicantData["failedAttempts"] >= GetFailedLoginAttempts();
703 
704  // don't show password stuff for type "H" because it uses home banking credentials
705  $applicantData["canChangeLogin"] = $applicantData["loginType"] !== "Home Banking";
706 
707  $sql = "select a.user_answer \"answer\", m.quest_text \"question\"
708  from lnappuser_questselect a
709  inner join cuquestmaster m on a.questid = m.quest_id
710  where a.userid = " . intval($userId) . " and m.quest_lang = 'en_US'";
711  $sth = db_query($sql, $dbh);
712  if (!$sth) {
713  throw new exception ("query failed.", 3);
714  }
715  $results = db_fetch_all($sth);
716  $results = $results === false ? array() : $results;
717 
718  $applicantData["securityQuestions"] = $results;
719 
720  $sql = "select count(*) from lnappuserresponse where userid = " . intval($userId);
721  $sth = db_query($sql, $dbh);
722  if (!$sth) {
723  throw new exception ("query failed.", 4);
724  }
725  if (db_num_rows($sth) <= 0) {
726  throw new exception ("query failed.", 6);
727  }
728  list ($numLoanApps) = db_fetch_row($sth, 0);
729 
730  $applicantData["numLoanApps"] = $numLoanApps;
731 
732  $returnArray = array("status" => "000", "error" => "", "applicantData" => $applicantData, "type" => "read");
733  } catch (exception $e) {
734  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage(), "type" => "read");
735  }
736  return $returnArray;
737 }
738 
739 /**
740  * function SaveApplicant($dbh, $Cu, $parameters)
741  * This saves the applicant on the applicant tab.
742  *
743  * @param $dbh -- the database connection.
744  * @param $Cu -- the credit union.
745  * @param $parameters -- the parameters.
746  *
747  * @return $status -- "000" if successful; nonzero otherwise.
748  * @return $error -- "" if successful; nonempty otherwise.
749  * @return $type -- "read" for purpose of determining what to do on return.
750  * @return $info -- the message to display to the screen on success.
751  */
752 function SaveApplicant($dbh, $Cu, $parameters) {
753  try {
754  $userId = HCU_array_key_value("userId", $parameters);
755  if ($userId === false) {
756  throw new exception ("Userid is required.", 4);
757  }
758 
759  $lockSetting = HCU_array_key_value("lockSetting", $parameters);
760  if ($lockSetting === false) {
761  throw new exception ("Lock setting is required.", 3);
762  }
763 
764  $email = HCU_array_key_value("email", $parameters);
765  if ($email === false) {
766  throw new exception ("Email is required.", 5);
767  }
768 
769  $password = HCU_array_key_value("password", $parameters);
770  if ($password === false) {
771  throw new exception ("Password is required.", 6);
772  }
773 
774  $confirm = HCU_array_key_value("confirm", $parameters);
775  if ($confirm === false) {
776  throw new exception ("Confirm is required.", 7);
777  }
778 
779  $sql = "select ln.userid \"lnId\", lower(coalesce(trim(ln.email), '')) \"email\", coalesce(ln.failedloginattempts, 0) \"failedAttempts\"
780  from lnappuser ln
781  where ln.userid = " . intval($userId);
782  $sth = db_query($sql, $dbh);
783  if (!$sth) {
784  throw new exception ("Query failed.", 1);
785  }
786  if (db_num_rows($sth) <= 0) {
787  throw new exception ("User not found.", 2);
788  }
789 
790  list($userId, $originalEmail, $failedAttempts) = db_fetch_row($sth, 0);
791  $saveList = array();
792 
793  switch($lockSetting) {
794  case "noChange":
795  break;
796  case "setLocked":
797  $saveList[] = "failedloginattempts = " . intval(GetFailedLoginAttempts());
798  break;
799  case "setUnlocked":
800  $saveList[] = "failedloginattempts = 0";
801  break;
802  default:
803  throw new exception ("Lock setting is not valid.", 8);
804  break;
805  }
806 
807  $email = trim($email);
808  if ($email == "") {
809  if ($originalEmail == "") {
810  throw new exception ("Please set an email.", 9);
811  }
812  } else if (strtolower($email) !== $originalEmail) {
813  if (!validateEmail($email)) {
814  throw new exception ("Email is not valid.", 11);
815  }
816 
817  $sql = "select count(*) from lnappuser ln where lower(trim(ln.email)) = '" . strtolower(prep_save($email, 50)) . "'";
818  $sth = db_query($sql, $dbh);
819  if (!$sth) {
820  throw new exception ("Select query failed.", 15);
821  }
822  if (db_num_rows($sth) <= 0) {
823  throw new exception ("Select query failed.", 16);
824  }
825  list($dupCount) = db_fetch_row($sth, 0);
826  if ($dupCount > 0) {
827  throw new exception ("Email needs to be unique.", 17);
828  }
829  $saveList[] = "email = '" . prep_save($email, 50) . "'";
830  }
831 
832  if ($password !== "") {
833  if ($confirm !== $password) {
834  throw new exception ("Passwords do not match.", 10);
835  }
836 
837  if (strlen($password) < 6 || strlen($password) > 20) {
838  throw new exception ("Password must be from 6 to 20 characters long.", 12);
839  }
840 
841  if ( !(preg_match("/\d/",$password) && preg_match("/\D/",$password))) {
842  throw new exception ("Password must contain both number and letter characters,", 13);
843  }
844 
845  if (preg_match("/['\"]/", $password)) {
846  throw new exception ("There are invalid characters in password.", 14);
847  }
848 
849  $saveList[] = "pwd = '" . prep_save(password_hash($password, PASSWORD_DEFAULT)) . "'";
850  }
851 
852  if (count($saveList) > 0) {
853 
854  $sql = "update lnappuser
855  set " . implode(", ", $saveList) . "
856  where userid = " . intval($userId) . " and cu = '" . prep_save($Cu, 10) . "'";
857  $sth = db_query($sql, $dbh);
858  if (!$sth) {
859  throw new exception ("Update query failed.", 18);
860  }
861  }
862 
863  $returnArray = array("status" => "000", "error" => "", "info" => "Applicant was updated successfully.", "type" => "update");
864  } catch (exception $e) {
865  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage(), "type" => "update");
866  }
867  return $returnArray;
868 }