Subversion Repositories PEEPS

Rev

Rev 3 | Rev 11 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
package PEEPS;
2
## PEEPS support functions...
3
 
4
use strict;
5
use Exporter 'import';
6
use CGI qw/param header start_html url/;
7
use CGI::Cookie;
8
use DBI;
9
use WebDB;
10
 
11
$SIG{__WARN__} = sub { warn sprintf("[%s] ", scalar localtime), @_ };
12
$SIG{__DIE__}  = sub { die  sprintf("[%s] ", scalar localtime), @_ };
13
 
14
our @EXPORT = qw( $ORCUSER $SYSTEM_EMAIL getRCDBH getAccessLevels authDB max authenticate canView getLeagueAffiliation getLeagueName getLeagues getShiftRef getShiftDepartment getClassID getDepartments convertDepartments convertTime getSchedule getRCid getSetting getUser getUserEmail getUserDerbyName getYears printRCHeader changeShift modShiftTime signUpCount signUpEligible findConflict changeLeadShift sendNewUserEMail sendUserMFAEMail logit orglogit isLeagueAdmin isPersonCovered isLeagueCovered isWFTDAMember remainingPolicyDays remainingOrgPolicyDays getPolicyByID getCoverageByID);
15
 
16
my $dbh = WebDB::connect ("peeps");
17
sub getRCDBH {
18
  return $dbh;
19
}
20
our $ORCUSER;
21
our $SYSTEM_EMAIL = 'rollercon.vorc@gmail.com';
22
use constant {
23
    NOONE     => 0,
24
    USER      => 1,
25
    VOLUNTEER => 1,
26
    LEAD      => 2,
27
    MANAGER   => 3,
28
    DIRECTOR  => 4,
29
    SYSADMIN  => 5,
30
    ADMIN     => 5
31
  };
32
 
33
sub getAccessLevels {
34
  my %AccessLevels = (
35
    -1 => "Locked",
36
 
37
#    1 => "Volunteer",
38
    1 => "User",
39
    2 => "Lead",
40
    3 => "Manager",
41
    4 => "Director",
42
    5 => "SysAdmin"
43
  );
44
  return \%AccessLevels;
45
}
46
 
47
sub authDB {
48
  my $src = shift;
49
  my $id = shift;
50
  my $pass = shift;
51
  my $level = shift;
52
  my $activationcode = shift // "";
53
  my $authentication = shift // "";
54
  my ($result, $authMatch, $sessionid);
55
 
56
  use CGI::Session;
57
 
58
  my $IDHASH;
59
 
60
  if ($src eq "form") {
61
    # check the username and password against the DB and return sessionid (if one exists) if valid.
62
    ($authMatch, $sessionid) = $dbh->selectrow_array ("select 1, sessionid from authentication where username = ? and password = password(?)", undef, $id, $pass);
63
    if ($authMatch) {
64
 
65
      my $session = CGI::Session->new ("driver:mysql", $sessionid ? $sessionid : undef, { DataSource => WebDB::SessionDSN });
66
      $session->param  ('is_logged_in', 1);
67
      $session->expire ('is_logged_in', '+30m');
68
      $session->flush;
69
      $sessionid = $session->id;
70
      $dbh->do ("update authentication set sessionid = ? where username = ?", undef, $sessionid, $id);
71
 
72
      %{$IDHASH} = (%{$dbh->selectrow_hashref ("select * from authentication where username = ?", undef, $id)},
73
                    %{$dbh->selectrow_hashref ("select * from person where id = (select person_id from authentication where username = ?)", undef, $id)});
74
    } else {
75
      $result->{ERRMSG} = "Incorrect Password!";
76
    }
77
  } else {
78
    # check the sessionid against the DB to make sure it's the same user.
79
    ($authMatch, $sessionid) = $dbh->selectrow_array ("select 1, sessionid from authentication where username = ? and sessionid = ?", undef, $id, $pass);
80
    if ($authMatch) {
81
      # the sessionid matches their DB entry, but we need to see if it's expired.
82
      my $session = CGI::Session->load ("driver:mysql", $sessionid, { DataSource => WebDB::SessionDSN });
83
      $sessionid = $session->id;
84
      if ($session->is_empty) {
85
        $result->{ERRMSG} = "Session Not Found!";
86
        $authMatch = 0;
87
      } elsif ($session->is_expired) {
88
        $result->{ERRMSG} = "Session Expired!";
89
        $authMatch = 0;
90
      } elsif (!$session->param  ('is_logged_in')) {
91
        $result->{ERRMSG} = "Session Timed Out (>30 minutes idle)!";
92
        $authMatch = 0;
93
      } else {
94
        $session->expire ('is_logged_in', '+30m');
95
        $session->flush;
96
      }
97
      %{$IDHASH} = (%{$dbh->selectrow_hashref ("select * from authentication where username = ?", undef, $id)},
98
                    %{$dbh->selectrow_hashref ("select * from person where id = (select person_id from authentication where username = ?)", undef, $id)});
99
    } else {
100
      $result->{ERRMSG} = "SECURITY ALERT: Bogus Session!";
101
    }
102
  }
103
 
104
  if ($authMatch) {
105
    # good login, but have we seen this browser before?
106
    my $query = new CGI;
107
    my $PEEPSMFACOOKIE = $query->cookie('PEEPS_MFA_UUID');
108
    my ($MFACHECK) = $dbh->selectrow_array ("select 1 from MFA where person_id = (select person_id from authentication where username = ?) and MFA_UUID = ?", undef, $id, $PEEPSMFACOOKIE);
109
 
110
    if (!$MFACHECK) {
111
      $result->{ERRMSG} = "MFA Check Required.";
112
    }
113
  }
114
 
115
 
116
 
117
 
118
#  my $tempDepartments = convertDepartments ($IDHASH->{department});
119
#  my $MAXACCESS = scalar keys %{ $tempDepartments } ? max ($IDHASH->{'access'}, values %{ $tempDepartments } ) : $IDHASH->{'access'};
120
  my $MAXACCESS = 1;
121
  my ($failed_attempts) = $dbh->selectrow_array ("select count(*) from log where person_id = ? and event = ? and timestampdiff(MINUTE, timestamp, now()) < ? and timestamp > (select timestamp from log where person_id = ? and event = ? order by timestamp desc limit 1)", undef, $IDHASH->{person_id}, "Incorrect Password", 30, $IDHASH->{person_id}, "Logged In");
122
 
123
  if (!$IDHASH->{'person_id'}) {
124
    $result->{ERRMSG} = "Username not found!";
125
    $result->{cookie_string} = '';
126
    $result->{person_id} = '';
127
    logit(0, "Account not found: $id");
128
    $result->{authenticated} = 'false';
129
    return $result;
130
  } elsif ($failed_attempts >= 3) {
131
    $result->{ERRMSG} = "Too Many Failed Login Attempts!<br>(Please wait 30 minutes before trying again.)";
132
    $result->{cookie_string} = '';
133
    $result->{person_id} = $IDHASH->{'person_id'};
134
    logit($IDHASH->{'person_id'}, "User Login Timeout");
135
    $result->{authenticated} = 'false';
136
    return $result;
137
  } elsif (!$authMatch) {
138
    $result->{cookie_string} = '';
139
    $result->{person_id} = $IDHASH->{'person_id'};
140
    logit($IDHASH->{'person_id'}, $result->{ERRMSG});
141
    ($failed_attempts) = $dbh->selectrow_array ("select count(*) from log where person_id = ? and event = ? and timestampdiff(MINUTE, timestamp, now()) < ? and timestamp > (select timestamp from log where person_id = ? and event = ? order by timestamp desc limit 1)", undef, $IDHASH->{person_id}, "Incorrect Password", 30, $IDHASH->{person_id}, "Logged In");
142
    if ($failed_attempts >=3) {
143
      $result->{ERRMSG} .= "<br>Too Many Failed Login Attempts!<br>(Please wait 30 minutes before trying again.)";
144
      logit($IDHASH->{'person_id'}, "Excessive Login Failures, 30 Minute Timeout");
145
    }
146
    $result->{authenticated} = 'false';
147
    return $result;
148
  } elsif ($IDHASH->{'locked'}) {
149
    $result->{ERRMSG} = "Account Locked!";
150
    $result->{cookie_string} = '';
151
    $result->{person_id} = $IDHASH->{'person_id'};
152
    logit($IDHASH->{'person_id'}, "Login attempted for Locked account.");
153
    $result->{authenticated} = 'false';
154
    return $result;
155
  } elsif ($IDHASH->{'activation'} ne "active") {
156
    # It's an inactive account...
157
    if ($activationcode eq "resend") {
158
      # warn "Resending activation code...";
159
      sendNewUserEMail ("New User", $IDHASH);
160
      $result->{ERRMSG} = "Activation code resent. Please check your email.";
161
      $result->{cookie_string} = "${id}&${sessionid}&0";
162
      $result->{person_id} = $IDHASH->{'person_id'};
163
      logit($IDHASH->{'person_id'}, "Activation code resent.");
164
      $result->{authenticated} = 'inactive';
165
      return $result;
166
    } elsif ($activationcode) {
167
      # They submitted an activation code
168
      if ($activationcode eq $IDHASH->{'activation'}) {
169
        # ...and it was good.
170
        $dbh->do ("update authentication set activation = 'active', locked = 0, last_login = now() where person_id = ? and activation = ?", undef, $IDHASH->{'person_id'}, $activationcode);
171
        logit($IDHASH->{'person_id'}, "Activated their account and logged In");
172
        # sendNewUserEMail ("Activate", $IDHASH);
173
        $IDHASH->{'access'} = 1;
174
        $IDHASH->{'activation'} = "active";
175
        $MAXACCESS = max ($MAXACCESS, 1);
176
      } else {
177
        # ...but it wasn't good.
178
        $result->{ERRMSG} = "Activation failed, invalid code submitted.";
179
        $result->{cookie_string} = "${id}&${sessionid}&0";;
180
        $result->{person_id} = $IDHASH->{'person_id'};
181
        logit($IDHASH->{'person_id'}, "Activation failed, invalid code submitted.");
182
        $result->{authenticated} = 'inactive';
183
        return $result;
184
      }
185
    } else {
186
      # No activation code was submitted.
187
      $result->{ERRMSG} = "Inactive account! Please check your email for activation link/code." unless $result->{ERRMSG};
188
      $result->{cookie_string} = "${id}&${sessionid}&0";
189
      $result->{person_id} = $IDHASH->{'person_id'};
190
      logit($IDHASH->{'person_id'}, "Login attempted without activation code.");
191
      $result->{authenticated} = 'inactive';
192
      return $result;
193
    }
194
  } elsif ($result->{ERRMSG} eq "MFA Check Required.") {
195
 
196
    # Need to check MFA...
197
    if ($authentication eq "resend") {
198
      sendUserMFAEMail ($IDHASH);
199
      $result->{ERRMSG} .= "<br>Activation code resent. Please check your email.";
200
      $result->{cookie_string} = "${id}&${sessionid}&0";
201
      $result->{person_id} = $IDHASH->{'person_id'};
202
      logit($IDHASH->{'person_id'}, "MFA code resent.");
203
      $result->{authenticated} = 'needsMFA';
204
      return $result;
205
    } elsif ($authentication) {
206
      # They submitted an authentication code
207
      if ($authentication =~ /^\d{6}$/ and $authentication eq $IDHASH->{'mfa'}) {
208
        # check to see how old it is...
209
        my ($code_age) = $dbh->selectrow_array ("select timestampdiff(MINUTE, mfa_timestamp, now()) from authentication where person_id = ? and mfa = ?", undef, $IDHASH->{'person_id'}, $IDHASH->{'mfa'}) // 99;
210
        if ($code_age > 10) {
211
          # ...but it was too old.
212
          $result->{ERRMSG} = "MFA Authentication failed, code is too old. Resending...";
213
          sendUserMFAEMail ($IDHASH);
214
          $result->{cookie_string} = "${id}&${sessionid}&0";
215
          $result->{person_id} = $IDHASH->{'person_id'};
216
          logit($IDHASH->{'person_id'}, "MFA Authentication failed, code is too old.");
217
          $result->{authenticated} = 'needsMFA';
218
          return $result;
219
        } else {
220
          # ...and it was good.
221
 
222
          use UUID::Tiny qw(create_UUID_as_string UUID_V4);
223
          $result->{MFA_UUID} = create_UUID_as_string(UUID_V4);
224
          $dbh->do ("insert into MFA (person_id, MFA_UUID) values (?, ?)", undef, $IDHASH->{'person_id'}, $result->{MFA_UUID});
225
 
226
          logit($IDHASH->{'person_id'}, "Authenticated with an MFA code.");
227
          $IDHASH->{'access'} = 1;
228
          $MAXACCESS = max ($MAXACCESS, 1);
229
          $result->{cookie_string} = "${id}&${sessionid}&".$MAXACCESS;
230
          $result->{authenticated} = 'confirmedMFA';
231
          return $result;
232
        }
233
      } else {
234
        # ...but it wasn't good.
235
        $result->{ERRMSG} = "MFA Authentication failed, invalid code submitted.";
236
        $result->{cookie_string} = "${id}&${sessionid}&0";
237
        $result->{person_id} = $IDHASH->{'person_id'};
238
        logit($IDHASH->{'person_id'}, "MFA Authentication failed, invalid code submitted.");
239
        $result->{authenticated} = 'needsMFA';
240
        return $result;
241
      }
242
    } else {
243
      # No activation code was submitted.
244
      sendUserMFAEMail ($IDHASH);
245
      $result->{ERRMSG} .= " Please check your email for activation code." unless $result->{ERRMSG};
246
      $result->{cookie_string} = "${id}&${sessionid}&0";
247
      $result->{person_id} = $IDHASH->{'person_id'};
248
      logit($IDHASH->{'person_id'}, "Login attempted from unrecognized location, MFA needed.");
249
      $result->{authenticated} = 'needsMFA';
250
      return $result;
251
    }
252
 
253
 
254
 
255
  }
256
 
257
  if ($MAXACCESS < $level) {
258
    if (getSetting ("MAINTENANCE")) {
259
      $result->{ERRMSG} = "MAINTENANCE MODE: Logins are temporarily disabled.";
260
    } else {
261
      $result->{ERRMSG} = "Your account either needs to be activated, or doesn't have access to this page!";
262
      logit($IDHASH->{'person_id'}, "Insufficient Privileges");
263
    }
264
    $result->{cookie_string} = "${id}&${sessionid}&$IDHASH->{'access'}";
265
    $result->{person_id} = $IDHASH->{'person_id'};
266
    $result->{authenticated} = 'false';
267
  } else {
268
    $result->{ERRMSG} = '';
269
#    $IDHASH->{department} = convertDepartments ($IDHASH->{department});
270
#    $IDHASH->{'access'} = max ($IDHASH->{'access'}, values %{$IDHASH->{department}});
271
    ($IDHASH->{'SYSADMIN'}) = $dbh->selectrow_array ("select 1 from role where person_id = ? and member_org_id = (select id from organization where league_name = ?) and role = ?", undef, $IDHASH->{person_id}, "WFTDA Leadership", "System Admin");
272
    $IDHASH->{'access'} = $IDHASH->{'SYSADMIN'} ? 5 : 1;
273
    $result->{cookie_string} = "${id}&${sessionid}&$IDHASH->{'access'}";
274
    $result->{person_id} = $IDHASH->{'person_id'};
275
    logit($IDHASH->{'person_id'}, "Logged In") if $src eq "form";
276
    $dbh->do ("update authentication set last_login = now() where person_id = ?", undef, $IDHASH->{'person_id'}) if $src eq "form";
277
    $result->{authenticated} = 'true';
278
 
279
    $ORCUSER = $IDHASH;
280
  }
281
  return $result;
282
}
283
 
284
sub max {
285
    my ($max, $next, @vars) = @_;
286
    return $max if not $next;
287
    return max( $max > $next ? $max : $next, @vars );
288
}
289
 
290
 
291
sub authenticate {                  # Verifies the user has logged in or puts up a log in screen
292
  my $MAINTMODE = getSetting ("MAINTENANCE");
293
  my $MINLEVEL = $MAINTMODE ? $MAINTMODE : shift // 1;
294
 
295
  my ($ERRMSG, $authenticated, %FORM);
296
  my $sth = $dbh->prepare("select * from authentication where username = '?'");
297
 
298
  my $query = new CGI;
299
# Check to see if the user has already logged in (there should be cookies with their authentication)?
300
  my $PEEPSAUTH = $query->cookie('PEEPSAUTH');
301
  $FORM{'ID'} = WebDB::trim $query->param('userid') || '';
302
  $FORM{'PASS'} = WebDB::trim $query->param('pass') || '';
303
  $FORM{'SUB'} = $query->param('login') || '';
304
  $FORM{'activate'} = WebDB::trim $query->param('activate') // '';
305
  $FORM{'authenticate'} = WebDB::trim $query->param('authenticate') // '';
306
  $FORM{'saveMFA'} = WebDB::trim $query->param('saveMFA') // '';
307
 
308
  if ($PEEPSAUTH) {
309
    # We have an authenication cookie.  Double-check it
310
    my ($PEEPSID, $SESSID, $LVL) = split /&/, $PEEPSAUTH;
311
    $authenticated = authDB('cookie', $PEEPSID, $SESSID, $MINLEVEL, $FORM{'activate'}, $FORM{'authenticate'});
312
  } elsif ($FORM{'SUB'}) {
313
    # a log in form was submited
314
    if ($FORM{'SUB'} eq "Submit") {
315
      $authenticated = authDB('form', $FORM{'ID'}, $FORM{'PASS'}, $MINLEVEL, $FORM{'activate'}, $FORM{'authenticate'});
316
    } elsif ($FORM{'SUB'} eq "New User") {
317
      # Print the new user form and exit
318
    }
319
  } else {
320
    $authenticated->{authenticated} = 'false';
321
  }
322
 
323
  if ($authenticated->{authenticated} eq 'true') {
324
    use CGI::Session;
325
    my $session = CGI::Session->new ("driver:mysql", $ORCUSER->{sessionid}, { DataSource => WebDB::SessionDSN });
326
#    $session->expire ('~logged_in', '30m');
327
#    $session->flush;
328
    my $sessionid = $session->id ();
329
 
330
    # Limit how long users are allowed to stay logged in at once.
331
    #  [there's no reason to limit PEEPS session length]
332
    # my ($session_length) = $dbh->selectrow_array ("select timestampdiff(MINUTE, last_login, now()) from authentication where person_id = ?", undef, $ORCUSER->{person_id});
333
    # if ($session_length > getSetting ("MAX_SESSION_MINUTES")) {
334
    #   $ENV{'QUERY_STRING'} = "LOGOUT";
335
    #   $authenticated->{ERRMSG} = "Maximum session time exceeded.<br>";
336
    # }
337
 
338
    if ($ENV{'QUERY_STRING'} eq "LOGOUT") {
339
      # warn "logging $ORCUSER->{derby_name} out...";
340
      $authenticated->{ERRMSG} .= "Logged Out.<br>";
341
      $authenticated->{cookie_string} = "";
342
      #$session->clear ("is_logged_in");
343
      #$session->flush;
344
      $authenticated->{authenticated} = 'false';
345
      $ENV{REQUEST_URI} =~ s/LOGOUT//;
346
      logit ($ORCUSER->{person_id}, "Logged Out");
347
      $dbh->do ("update authentication set last_active = now(), sessionid = null where person_id = ?", undef, $ORCUSER->{person_id});
348
      $dbh->do ("delete from sessions where sessions.id = ?", undef, $sessionid);
349
      $ORCUSER = "";
350
    } else {
351
      $dbh->do ("update authentication set last_active = now(), sessionid = ? where person_id = ?", undef, $sessionid, $ORCUSER->{person_id});
352
      return $authenticated->{cookie_string};
353
    }
354
  } elsif ($authenticated->{authenticated} eq "confirmedMFA") {
355
    # Set the MFA cookie and redirect the user to where they were going.
356
 
357
    my $PEEPSAUTH_cookie = CGI::Cookie->new(-name=>'PEEPSAUTH',-value=>$authenticated->{cookie_string});
358
    my $PEEPSMFA_cookie = $FORM{saveMFA} ?
359
      CGI::Cookie->new(-name=>'PEEPS_MFA_UUID',-value=>$authenticated->{MFA_UUID},-expires=>'+5y') :
360
      CGI::Cookie->new(-name=>'PEEPS_MFA_UUID',-value=>$authenticated->{MFA_UUID});
361
 
4 - 362
    $ENV{HTTPS} = 'ON' if $ENV{SERVER_NAME} =~ /^peeps/;
2 - 363
    my $destination = url ();
364
    print header(-cookie=>[$PEEPSAUTH_cookie, $PEEPSMFA_cookie]);
365
    printRCHeader("MFA Confirmed", $destination);
366
    print<<goforth;
367
      Your MFA Code has been confirmed.  You may continue on your way.<br><br>
368
 
369
      <a href="$destination">Continue.</a>
370
goforth
371
    exit;
372
  }
373
 
374
 
375
# If we get here, the user has failed authentication; throw up the log-in screen and die.
376
 
377
  my $PEEPSAUTH_cookie = CGI::Cookie->new(-name=>'PEEPSAUTH',-value=>$authenticated->{cookie_string});
378
 
379
  if ($authenticated->{ERRMSG}) {
380
    $authenticated->{ERRMSG} = "<TR><TD colspan=2 align=center><font color=red><b>".$authenticated->{ERRMSG}."</b></font>&nbsp</TD></TR>";
381
  } else {
382
    $authenticated->{ERRMSG} = "";
383
  }
384
 
385
  print header(-cookie=>$PEEPSAUTH_cookie);
386
 
387
  printRCHeader("Please Sign In");
388
  print<<authpage;
389
  <form action="$ENV{REQUEST_URI}" method=POST name=Req id=Req>
390
    <TR><TD colspan=2 align=center><b><font size=+2>Please Sign In</font>
391
    <TABLE>
392
    </TD></TR>
393
    <TR><TD colspan=2>&nbsp</TD></TR>
394
    $authenticated->{ERRMSG}
395
authpage
396
 
397
  if ($ENV{'QUERY_STRING'} eq "LOGOUT") {
398
    print "<TR><TD colspan=2>&nbsp</TD></TR>";
399
    print "<TR><TD colspan=2><button onClick=\"location.href='';\">Log In</button></TD></TR>";
400
    print "</TABLE></BODY></HTML>";
401
    exit;
402
  }
403
 
404
  if ($authenticated->{authenticated} eq "inactive") {
405
 
406
    print<<activationpage;
407
      <TR><TD colspan=2 align=center>&nbsp;</TD></TR>
408
      <TR><TD align=right><B>Activation Code:</TD><TD><INPUT type=text id=activate name=activate></TD></TR>
409
      <TR><TD></TD><TD><INPUT type=submit name=login value=Submit></TD></TR>
410
      <TR><TD colspan=2 align=center>&nbsp;</TD></TR>
411
      <TR><TD colspan=2 align=center><A HREF='' onClick='document.getElementById("activate").value="resend"; Req.submit(); return false;'>[Resend your activation email]</A></TD></TR>
412
      <TR><TD colspan=2 align=center><A HREF='' onClick="location.href='?LOGOUT';">[Log Out]</A></TD></TR>
413
      </TABLE></FORM>
414
activationpage
415
 
416
  } elsif ($authenticated->{authenticated} eq "needsMFA") {
417
 
418
    print<<MFApage;
419
      <TR><TD colspan=2 align=center>&nbsp;</TD></TR>
420
      <TR><TD align=right><B>Email Authentication Code:</TD><TD><INPUT type=text id=authenticate name=authenticate></TD></TR>
421
      <TR><TD></TD><TD><INPUT type=submit name=login value=Submit></TD></TR>
422
      <TR><TD colspan=2 align=center>&nbsp;</TD></TR>
423
      <TR><TD colspan=2 align=center>Save this browser: <label class="switch"><input name="saveMFA" type="checkbox" value="1"><span class="slider round"></span></label></TD></TR>
424
      <TR><TD colspan=2 align=center><A HREF='' onClick='document.getElementById("authenticate").value="resend"; Req.submit(); return false;'>[Send new authentication code]</A></TD></TR>
425
      <TR><TD colspan=2 align=center><A HREF='' onClick="location.href='?LOGOUT';">[Log Out]</A></TD></TR>
426
      </TABLE></FORM>
427
MFApage
428
 
429
  } else {
430
 
431
    print<<authpage2;
432
      <TR><TD colspan=2>&nbsp</TD></TR>
433
      <TR>
434
        <TD align=right><B>Username:</TD><TD><INPUT type=text id=login name=userid></TD>
435
      </TR>
436
      <TR>
437
        <TD align=right><B>Password:</TD><TD><INPUT type=password name=pass></TD>
438
      </TR>
439
      <TR><TD></TD><TD><input type=hidden name=activate id=activate value=$FORM{'activate'}><input type=hidden name=authenticate id=authenticate value=$FORM{'authenticate'}><INPUT type=submit name=login value=Submit></TD></TR>
440
      <TR><TD colspan=2 align=center><A HREF="view_user?submit=New%20User">[register as a new user]</A></TD></TR>
441
      <TR><TD colspan=2 align=center><A HREF="recoverAccount">[recover your account]</A></TD></TR>
442
    </TABLE>
443
    </FORM>
444
 
445
    <SCRIPT language="JavaScript">
446
    <!--
447
    document.getElementById("login").focus();
448
 
449
    function Login () {
450
      document.getElementById('Req').action = "$ENV{SCRIPT_NAME}";
451
      document.getElementById('Req').submit.click();
452
      return true;
453
    }
454
 
455
    //-->
456
    </SCRIPT>
457
 
458
authpage2
459
  }
460
 
461
#foreach (keys %ENV) {
462
# print "$_: $ENV{$_}<br>";
463
#}
464
# &JScript;
465
  exit;
466
}
467
 
468
sub canView {
469
  my $A = shift // "";
470
  my $B = shift // "";
471
  # Is A a lead or higher of one of B's Depts? (or they're looking at themselves)
472
  # parameters should be a Hashref to the users' details
473
 
474
  return 1 if $A->{person_id} == $B->{person_id}; # It's the same person.
475
 
476
  my ($admin_check) = $dbh->selectrow_array ("select 1 from role where person_id = ? and member_org_id = ? and role = ?", undef, $A->{person_id}, 4276, "System Admin"); # 4276 is "WFTDA Leadership"
477
 
478
  return 1 if $admin_check or $A->{access} > 4; # viewer and target are the same person or it's a SysAdmin.
479
 
480
  my ($league_admin) = $dbh->selectrow_array ("select * from role where person_id = ? and member_org_id in (select distinct member_org_id from role where person_id = ?) and role = ?", undef, $A->{person_id}, $B->{person_id}, "League Admin");
481
 
482
  return 1 if $league_admin;
483
 
484
 
485
  return 0;
486
}
487
 
488
sub getShiftDepartment {
489
  my $shiftID = shift // "";
490
  my $dept;
491
 
492
  if ($shiftID =~ /^\d+$/) {
493
    ($dept) = $dbh->selectrow_array ("select dept from shift where id = ?", undef, $shiftID);
494
  } else {
495
    my ($id, $role) = split /-/, $shiftID;
496
    if ($role =~ /^CLA/) {
497
      $dept = "CLA";
498
    } else {
499
      ($dept) = $dbh->selectrow_array ("select distinct department from staff_template where role like ?", undef, $role.'%');
500
    }
501
  }
502
#  } elsif ($shiftID =~ /^\d+-ANN/) {
503
#    $dept = "ANN";
504
#  } else {
505
#    $dept = "OFF";
506
#  }
507
 
508
  return $dept;
509
}
510
 
511
sub getClassID {
512
  my $shift = shift // "";
513
  return unless $shift =~ /^\d+$/;
514
 
515
  my $shiftref = getShiftRef ($shift);
516
  my ($classid) = $dbh->selectrow_array ("select id from class where date = ? and start_time = ? and location = ?", undef, $shiftref->{date}, $shiftref->{start_time}, $shiftref->{location});
517
  return $classid unless !$classid;
518
 
519
  warn "ERROR: No class.id found for shift $shiftref->{id}";
520
  return "";
521
}
522
 
523
sub getShiftRef {
524
  my $shiftID = shift // "";
525
  return unless $shiftID =~ /^\d+$/;
526
 
527
  my ($shiftref) = $dbh->selectrow_hashref ("select * from shift where id = ?", undef, $shiftID);
528
  return $shiftref unless $shiftref->{id} != $shiftID;
529
 
530
  warn "ERROR: Couldn't find shift with ID [$shiftID]";
531
  return "";
532
}
533
 
534
sub getLeagueAffiliation {
535
  my $PEEPSid = shift // "";
536
 
537
  my $results;
538
 
539
  my $sth = $dbh->prepare ("select member_org_id, league_name, role, type from role join organization on member_org_id = organization.id left join person on person_id = person.id where person_id = ?");
540
  if ($PEEPSid =~ /^\d+$/) {
541
    $sth->execute ($PEEPSid);
542
    while (my ($orgid, $orgname, $role, $orgtype) = $sth->fetchrow_array ()) {
543
      push (@{$results->{$orgid}}, $role);
544
    }
545
  }
546
  return $results;
547
}
548
 
549
sub getLeagueName {
550
  my $id = shift // "";
551
  my ($name) = $dbh->selectrow_array ("select league_name from organization where id = ?", undef, $id);
552
  return $name;
553
}
554
 
555
sub getLeagues {
556
  my $exclude_id = shift // 0;
557
  return $dbh->selectall_arrayref ("select id, concat_ws(' - ', league_name, type, status) as league from organization where status in ('Active', 'Voluntary Inactive')  and id not in (select member_org_id from role where person_id = ?) order by league_name", undef, $exclude_id);
558
}
559
 
560
sub getDepartments {
561
  my $RCid = shift // "";
562
  # If we get an RCid, return the list of departments and levels for that user.
563
  #   Otherwise (no parameter), return the list of departments with their display names.
564
 
565
  if ($RCid) {
566
    my $sth = $dbh->prepare("select department from official where RCid = ?");
567
    $sth->execute($RCid);
568
    my ($dlist) = $sth->fetchrow;
569
    return convertDepartments ($dlist);
570
  } else {
571
    my %HASH;
572
    my $sth = $dbh->prepare("select TLA, name from department");
573
    $sth->execute();
574
    while (my ($tla, $name) = $sth->fetchrow) {
575
      $HASH{$tla} = $name;
576
    }
577
    return \%HASH;
578
  }
579
 
580
}
581
 
582
sub convertDepartments {
583
  # For the department membership, converts the DB string back and forth to a hashref...
584
  my $input = shift // "";
585
  my $output;
586
 
587
  if (ref $input eq "HASH") {
588
    $output = join ":", map { $_."-".$input->{$_} } sort keys %{$input};
589
  } else {
590
    foreach (split /:/, $input) {
591
      my ($tla, $level) = split /-/;
592
      $output->{$tla} = $level;
593
    }
594
    $output = {} unless ref $output eq "HASH";
595
  }
596
 
597
  return $output;
598
}
599
 
600
sub getPolicyByID {
601
  my $pid = shift // "";
602
 
603
  (warn "ERROR: No PolicyID passed to getPolicyByID()" and return) unless $pid =~ /^\d+$/;
604
 
605
  my $policy = $dbh->selectrow_hashref ("select * from policy where id = ?", undef, $pid);
606
 
607
  (warn "ERROR: No Policy found with id: $pid" and return) unless $policy->{id} =~ /^\d+$/;
608
 
609
  return $policy;
610
}
611
 
612
sub getCoverageByID {
613
  my $pid = shift // "";
614
  my $person = shift // "";
615
 
616
  (warn "ERROR: No PolicyID passed to getCoverageByID()" and return) unless $pid    =~ /^\d+$/;
617
  (warn "ERROR: No PersonID passed to getCoverageByID()" and return) unless $person =~ /^\d+$/;
618
 
619
  my $policy = $dbh->selectrow_hashref ("select * from coverage where id = ? and person_id = ?", undef, $pid, $person);
620
 
621
  (warn "ERROR: No Coverage found with id: $pid and person_id: $person" and return) unless $policy->{id} =~ /^\d+$/;
622
 
623
  return $policy;
624
}
625
 
626
sub convertTime {
627
  my $time = shift || return;
628
 
629
  if ($time =~ / - /) {
630
    return join " - ", map { convertTime ($_) } split / - /, $time;
631
  }
632
 
633
  $time =~ s/^(\d{1,2}:\d{2}):\d{2}$/$1/;
634
  $time =~ s/^0//;
635
 
636
  if ($ORCUSER->{timeformat} eq "24hr") {
637
    if ($time =~ /^\d{1,2}:\d{2}$/) { return $time; }
638
  } else {
639
    my ($hr, $min) = split /:/, $time;
640
    my $ampm = " am";
641
    if ($hr >= 12) {
642
      $hr -= 12 unless $hr == 12;
643
      $ampm = " pm";
644
    } elsif ($hr == 0) {
645
      $hr = 12;
646
    }
647
    return $hr.":".$min.$ampm;
648
  }
649
}
650
 
651
sub getSchedule {
652
  my $RCid = shift // return "ERROR: No RCid provided to getSchedule";
653
  my $filter = shift // "";
654
  my $output = shift // "";
655
  my $year = 1900 + (localtime)[5];
656
 
657
  my @whereclause;
658
  if ($filter eq "all") {
659
    push @whereclause, "year(date) >= year(now())";
660
  } elsif ($filter eq "prior") {
661
    push @whereclause, "year(date) < year(now())";
662
  } else {
663
    push @whereclause, "date >= date(now())";
664
  }
665
#  if ($RCid ne $ORCUSER->{RCid}) {
666
#    push @whereclause, "dept != 'PER'";
667
#  }
668
 
669
  use DateTime;
670
  my $dt = DateTime->today (time_zone => 'America/Los_Angeles');
671
  $dt =~ s/T00\:00\:00$//;
672
  my $now = DateTime->now (time_zone => 'America/Los_Angeles');
673
 
674
 
675
  use HTML::Tiny;
676
  my $h = HTML::Tiny->new( mode => 'html' );
677
 
678
  my $where = scalar @whereclause ? "where ".join " and ", @whereclause : "";
679
  my @shifts;
680
  my $sth = $dbh->prepare("select * from (select id, date, dayofweek, track as location, time, role, teams, signup, 'OFF' as dept, volhours from v_shift_officiating where RCid = ? union
681
                                          select id, date, dayofweek, track as location, time, role, teams, signup, 'ANN' as dept, volhours from v_shift_announcer where RCid = ? union
682
                                          select id, date, dayofweek, location, time, role, '' as teams, type as signup, dept, volhours from v_shift where RCid = ? union
683
                                          select id, date, dayofweek, location, time, role, name as teams, 'mvpclass' as signup, 'CLA' as dept, 0 as volhours from v_class_signup_new where RCid = ?) temp
684
                           $where order by date, time");
685
  $sth->execute($RCid, $RCid, $RCid, $RCid);
686
  my $hours = 0;
687
  while (my $s = $sth->fetchrow_hashref) {
688
    my ($yyyy, $mm, $dd) = split /\-/, $s->{date};
689
    my $cutoff = DateTime->new(
690
        year => $yyyy,
691
        month => $mm,
692
        day => $dd,
693
        hour => 5,
694
        minute => 0,
695
        second => 0,
696
        time_zone => 'America/Los_Angeles'
697
    );
698
 
699
 
700
    if (!$s->{teams} or $s->{dept} eq "CLA") {
701
      # it's a time-based shift
702
      if ($s->{dept} eq "PER") {
703
        if ($RCid eq $ORCUSER->{RCid}) {
704
          # DROP
705
          $s->{buttons} = $h->button ({ onClick=>"event.stopPropagation(); if (confirm('Really? You want to delete this personal time?')==true) { location.href='personal_time?choice=Delete&id=$s->{id}'; return false; }" }, "DEL")."&nbsp;".$h->button ({ onClick=>"event.stopPropagation(); location.href='personal_time?choice=Update&id=$s->{id}'" }, "EDIT");
706
        } else {
707
          $s->{location} = "";
708
          $s->{role} = "";
709
        }
710
      } elsif (($RCid == $ORCUSER->{RCid} and $s->{signup} !~ /^selected/ and $now < $cutoff) or ($ORCUSER->{department}->{$s->{dept}} >= 2 or $ORCUSER->{access} >= 5)) {
711
        # DROP
712
        my ($shiftORclass, $linkargs) = ("shift", "");
713
        if ($s->{dept} eq "CLA") {
714
          $shiftORclass = "class";
715
          $linkargs = "&role=$s->{role}";
716
          $s->{role} = $s->{teams};
717
          $s->{teams} = "";
718
        }
719
        $s->{buttons} = $h->button ({ onClick=>"if (confirm('Really? You want to drop this $shiftORclass?')==true) { window.open('make_shift_change?change=del&RCid=$RCid&id=$s->{id}$linkargs','Confirm Class Change','resizable,height=260,width=370'); return false; }" }, "DROP");
720
        if ($ORCUSER->{department}->{$s->{dept}} >= 2 or $ORCUSER->{access} >= 5) {
721
          # NO SHOW
722
          $s->{buttons} .= "&nbsp;".$h->button ({ onClick=>"if (confirm('Really? They were a no show?')==true) { window.open('make_shift_change?noshow=true&change=del&RCid=$RCid&id=$s->{id}$linkargs','Confirm Shift Change','resizable,height=260,width=370'); return false; }" }, "NO SHOW");
723
        }
724
 
725
      }
726
#     $hours += $s->{volhours} unless $s->{dept} eq "PER" or $s->{dept} eq "CLA";
727
 
728
    } elsif (($RCid == $ORCUSER->{RCid} and $s->{signup} !~ /^selected/ and $now < $cutoff) or ($ORCUSER->{department}->{$s->{dept}} >= 2 or $ORCUSER->{access} >= 5)) {
729
      # it's a game shift
730
      #DROP
731
      $s->{buttons} = $h->button ({ onClick=>"if (confirm('Really? You want to drop this shift?')==true) { window.open('make_shift_change?change=del&RCid=$RCid&id=$s->{id}&role=$s->{role}','Confirm Shift Change','resizable,height=260,width=370'); return false; }" }, "DROP");
732
      if ($ORCUSER->{department}->{$s->{dept}} >= 2 or $ORCUSER->{access} >= 5) {
733
        # NO SHOW
734
        $s->{buttons} .= "&nbsp;".$h->button ({ onClick=>"if (confirm('Really? They were a no show?')==true) { window.open('make_shift_change?noshow=true&change=del&RCid=$RCid&id=$s->{id}&role=$s->{role}','Confirm Shift Change','resizable,height=260,width=370'); return false; }" }, "NO SHOW");
735
      }
736
#      $hours += $s->{volhours};
737
    }
738
    $s->{role} =~ s/\-\d+$//;
739
 
740
#   push @shifts, $h->li ({ class=> $s->{date} eq $dt ? "nowrap highlighted" : "nowrap shaded" }, join '&nbsp;&nbsp;', $s->{date}, $s->{dayofweek}, $s->{time}, $s->{location}, getDepartments()->{$s->{dept}}, $s->{role}, $s->{teams}, $s->{buttons});
741
#   push @shifts, $h->li ({ class=> $s->{date} eq $dt ? "highlighted" : "shaded" }, join '&nbsp;&nbsp;', $s->{date}, $s->{dayofweek}, $s->{time}, $s->{location}, getDepartments()->{$s->{dept}}, $s->{role}, $s->{teams}, $s->{buttons});
742
    $s->{time} = convertTime $s->{time};
743
    if ($s->{dept} eq "PER") {
744
      push @shifts, $h->li ({ onClick => "location.replace('personal_time?id=$s->{id}');", class=> $s->{date} eq $dt ? "highlighted" : "shaded" }, $h->div ({ class=>"lisp0" }, [ $h->div ({ class=>"liLeft" }, join '&nbsp;&nbsp;', ($s->{date}, $s->{dayofweek}, $s->{time}, $s->{location}, $s->{dept} eq "CLA" ? "MVP Class:" : getDepartments()->{$s->{dept}}, $s->{role}, $s->{teams})), $h->div ({ class=>"liRight" }, $s->{buttons}) ]));
745
    } else {
746
      push @shifts, $h->li ({ class=> $s->{date} eq $dt ? "highlighted" : "shaded" }, $h->div ({ class=>"lisp0" }, [ $h->div ({ class=>"liLeft" }, join '&nbsp;&nbsp;', ($s->{date}, $s->{dayofweek}, $s->{time}, $s->{location}, $s->{dept} eq "CLA" ? "MVP Class:" : getDepartments()->{$s->{dept}}, $s->{role}, $s->{teams})), $h->div ({ class=>"liRight" }, $s->{buttons}) ]));
747
    }
748
    $hours += $s->{volhours} unless $s->{dept} eq "PER" or $s->{dept} eq "CLA";
749
  }
750
 
751
  if ($output eq "hours") {
752
    return $hours;
753
  }
754
 
755
  if (scalar @shifts) {
756
    return $h->ul ([ @shifts, $h->h5 ("Currently showing $hours hours of Volunteer Time.") ]);
757
  } elsif ($filter eq "prior") {
758
    return $h->p ({ class=>"hint" }, "[nothing to see here]");
759
  } else {
760
    return $h->p ({ class=>"hint" }, "[nothing scheduled at the moment]");
761
  }
762
}
763
 
764
sub getRCid {
765
  my $derbyname = shift;
766
  ($derbyname) = $dbh->selectrow_array ("select RCid from official where derby_name = ?", undef, $derbyname);
767
  return $derbyname;
768
}
769
 
770
sub getSetting {
771
  my $k = shift;
772
 
773
  my ($value) = $dbh->selectrow_array ("select setting.value from setting where setting.key = ?", undef, $k);
774
  return defined $value ? $value : undef;
775
}
776
 
777
sub getUser {
778
  my $ID = shift;
779
 
780
  my $sth;
781
  if ($ID =~ /^\d+$/) {
782
    $sth = $dbh->prepare("select * from person where id = ?");
783
  } elsif ($ID =~ /@/) {
784
    $sth = $dbh->prepare("select * from person where email = ?");
785
  } else {
786
    $sth = $dbh->prepare("select * from person where id = (select person_id from authentication where username = ?)");
787
  }
788
  $sth->execute($ID);
789
  my $user = $sth->fetchrow_hashref;
790
 
791
  my $auth = $dbh->selectrow_hashref ("select * from authentication where person_id = ?", undef, $user->{id});
792
 
793
  map { $user->{$_} = "" unless $user->{$_} } keys %{$user};
794
  map { $user->{$_} = $auth->{$_} ? $auth->{$_} : "" } keys %{$auth};
795
  $user->{person_id} = $user->{id};
796
  return $user->{id} ? $user : "";
797
}
798
 
799
sub getUserEmail {
800
  my $RCid = shift;
801
  my $sth = $dbh->prepare("select email from official where RCid = ?");
802
  $sth->execute($RCid);
803
  my ($email) = $sth->fetchrow_array();
804
  return $email;
805
}
806
 
807
sub getUserDerbyName {
808
  my $RCid = shift;
809
  my $sth = $dbh->prepare("select derby_name from official where RCid = ?");
810
  $sth->execute($RCid);
811
  my ($dname) = $sth->fetchrow_array();
812
  return $dname;
813
}
814
 
815
sub getYears {
816
  my $sth = $dbh->prepare("select distinct year from (select distinct year(date) as year from shift union select distinct year(date) as year from game union select distinct year(date) as year from class union select year(now()) as year) years order by year");
817
# my $sth = $dbh->prepare("select distinct year(date) from v_shift_admin_view");
818
  $sth->execute();
819
  my @years;
820
  while (my ($y) =$sth->fetchrow_array()) { push @years, $y; }
821
  return \@years;
822
}
823
 
824
sub printRCHeader {
825
  my $PAGE_TITLE = shift;
826
  my $redirect = shift // "";
827
# use CGI qw/start_html/;
828
  use HTML::Tiny;
829
  my $h = HTML::Tiny->new( mode => 'html' );
4 - 830
  $ENV{HTTPS} = 'ON' if $ENV{SERVER_NAME} =~ /^peeps/;
2 - 831
 
832
#  my $logout = $h->a ({ href=>"index", onClick=>"document.cookie = 'PEEPSAUTH=; expires=Thu, 01 Jan 1970 00:00:01 GMT; path=/';return true;" }, "[Log Out]");
833
  my $referrer = param ("referrer") ? param ("referrer") : $ENV{HTTP_REFERER};
834
  my $logout = (!$referrer or $referrer eq url) ? "" : $h->button ({ onClick=>"window.location.href='$referrer';" }, "Back")."&nbsp;";
835
  $logout .= url =~ /\/(index)?$/ ? "" : $h->button ({ onClick=>"window.location.href='/';" }, "Home")."&nbsp;";
836
#  $logout .= $h->button ({ onClick=>"document.cookie = 'PEEPSAUTH=; expires=Thu, 01 Jan 1970 00:00:01 GMT; path=/'; location.href='/';" }, "Log Out");
837
  $logout .= $h->button ({ onClick=>"location.href='?LOGOUT';" }, "Log Out");
838
 
839
  my $loggedinas = $ORCUSER ? "Currently logged in as: ".$h->a ({ href=>"/view_user?submit=View&person_id=$ORCUSER->{person_id}" }, $ORCUSER->{derby_name}).$h->br.$logout : "";
840
 
841
#  print start_html (-title=>"vORC - $PAGE_TITLE", -style => {'src' => "/style.css"} );
842
 
843
  my $ANALYTICS = <<MATOMO;
844
  var _mtm = window._mtm = window._mtm || [];
845
  _mtm.push({'mtm.startTime': (new Date().getTime()), 'event': 'mtm.Start'});
846
  (function() {
847
    var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
848
    g.async=true; g.src='https://analytics.whump.org/js/container_to4NCtvM.js'; s.parentNode.insertBefore(g,s);
849
  })();
850
MATOMO
851
 
852
  print $h->open ("html");
853
  print $h->head ([$h->title ("PEEPS - $PAGE_TITLE"),
854
                   $h->link  ({ rel  => "stylesheet",
855
                                type => "text/css",
856
                                href => "/style.css" }),
857
                   $redirect ? $h->meta ({ 'http-equiv'=>"refresh", content=>"0; URL=".$redirect }) : "",
858
#                   $h->script ($ANALYTICS)
859
                  ]);
860
  print $h->open ("body");
861
#  print $h->img ({referrerpolicy=>"no-referrer-when-downgrade", src=>"https://analytics.whump.org/matomo.php?idsite=2&amp;rec=1", style=>"border:0", alt=>""});
862
#<html><head><title>Officials' RollerCon Schedule Manager - $PAGE_TITLE</title>
863
#<link rel="stylesheet" type="text/css" href="/style.css">
864
#</head>
865
#<body text="#000000" bgcolor="#FFFFFF" link="#0000EE" vlink="#551A8B" alink="#FF0000">
866
  print $h->div ({ class=>"sp0" }, [ $h->div ({ class=>"spLeft" },  $h->a ({ href=>"/" }, $h->img ({ src=>"/images/wftda-insurance-logo.svg", width=>"250", height=>"75" }))),
867
                                     $h->div ({ class=>"spRight" }, [ $h->h1 (["PEEPS $PAGE_TITLE", $h->br]),
868
                                     $loggedinas,
869
                                     ])
870
                                   ]);
871
#print<<rcheader;
872
#  <TABLE>
873
# <TR class="nostripe">
874
#   <TD align=right><img SRC="/logo.jpg"></TD>
875
#   <TD align=center valign=middle><b><font size=+3>Officials' RollerCon<br>Schedule Manager<br>$PAGE_TITLE</FONT></b>
876
# <p align=right><font size=-2>$loggedinas <a href='index' onClick="document.cookie = 'PEEPSAUTH=; expires=Thu, 01 Jan 1970 00:00:01 GMT; path=/';return true;">[Log Out]</a></font></TD>
877
# </TR>
878
 
879
#rcheader
880
}
881
 
882
sub changeShift {
883
  my ($change, $shift_id, $role, $user_id) = @_;
884
  if ($shift_id =~ /(am|pm)/) {
885
    my ($td, $st, $tl) = split /\|/, $shift_id;
886
    my ($hr, $min, $ampm) = split /:|\s/, $st;
887
    if ($ampm eq "pm") { $hr += 12; }
888
    elsif ($ampm eq "am" and $hr == 12) { $hr = "00" }
889
 
890
    $st = $hr.":".$min;
891
    $shift_id = join "|", ($td, $st, $tl);
892
  } else {
893
    $shift_id =~ s/(\d+:\d+):00/$1/;
894
  }
895
#warn join " - ", $change, $shift_id, $role, $user_id;
896
  my $leadership_change = 0;
897
# my $department = getShiftDepartment ($role ? $shift_id."-".$role : $shift_id);
898
  my $department;
899
  if ($shift_id =~ /^\d+$/) {
900
    $department = getShiftDepartment ($role ? $shift_id."-".$role : $shift_id);
901
  } else {
902
    $department = "CLA";
903
    if ($change eq "del") {
904
      ($shift_id, $role) = $dbh->selectrow_array ("select id, role from v_class_signup_new where date = ? and start_time = ? and location = ?", undef, split /\|/, $shift_id);
905
    } else {
906
      if ($change eq "override") {
907
        ($shift_id, $role) = $dbh->selectrow_array ("select id, concat('CLA-', max(cast(substring_index(role, '-', -1) as UNSIGNED)) +1) as role from v_class_signup_new where date = ? and start_time = ? and location = ?", undef, split /\|/, $shift_id) unless $change ne "override";
908
      } else {
909
        ($shift_id, $role) = $dbh->selectrow_array ("select id, concat('CLA-', max(cast(substring_index(role, '-', -1) as UNSIGNED)) +1) as role, count(role), capacity from v_class_signup_new where date = ? and start_time = ? and location = ? having capacity > count(role)", undef, split /\|/, $shift_id);
910
      }
911
    }
912
    $role = "CLA-1" unless $role; # If no one has signed up for the class yet, the SQL above doesn't retrieve the first available
913
  }
914
# my $game_based = $role ? "game" : "shift";
915
  my $game_based = $role =~ /^CLA-/ ? "class" : $role ? "game" : "shift";
916
  my $sth;
917
 
918
  if ($change eq "add" or $change eq "override") {
919
    my $taken;
920
    if ($department eq "CLA") {
921
      ($taken) = $shift_id ? 0 : 1;
922
    } elsif ($game_based eq "game") {
923
      ($taken) = $dbh->selectrow_array ("select count(*) from assignment where Gid = ? and role = ?", undef, $shift_id, $role);
924
    } else {
925
      ($taken) = $dbh->selectrow_array ('select count(*) from shift where id = ? and (isnull(assignee_id) = 0 or assignee_id <> "")', undef, $shift_id);
926
    }
927
    if ($taken) {
928
      return ($department eq "CLA") ? "<br>Denied! This class is already full ($shift_id).<br>\n" : "<br>Denied! This shift is already taken ($shift_id).<br>\n";
929
    }
930
  }
931
 
932
  if (lc ($user_id) ne lc ($ORCUSER->{RCid})) { # they're changing someone else's schedule...
933
    if (($department eq "CLA" and $ORCUSER->{department}->{MVP} >= 2) or $ORCUSER->{department}->{$department} >= 2 or $ORCUSER->{access} >= 5 or $ORCUSER->{department}->{VCI} >= 2) {
934
      # the user making the change is either a lead in the dept, a sysadmin, or a VCI lead
935
      logit ($ORCUSER->{RCid}, "$ORCUSER->{derby_name} changed someone else's schedule. ($change, $shift_id, $role, $user_id)");
936
      logit ($user_id, "Schedule was changed by $ORCUSER->{derby_name}. ($change, $shift_id, $role, $user_id)");
937
      $leadership_change = 1;
938
    } else {
939
      logit ($ORCUSER->{RCid}, "Unauthorized attempt to change someone else's schedule. ($change, $shift_id, $role, $user_id)");
940
      return "<br>Denied! You are not authorized to change someone else's schedule in this department ($department).<br>\n";
941
    }
942
  } elsif ($ORCUSER->{department}->{$department} >= 3 or $ORCUSER->{access} >= 5) {
943
    # Managers can sign up for as many shifts within their own department as they like...
944
    $leadership_change = 1;
945
  }
946
 
947
  if ($change eq "add") {
948
    if ($department eq "CLA" and !getUser($user_id)->{MVPid}) {
949
      return "<br>Denied! User ($user_id) does not have an MVP Pass!<br>\n";
950
    } elsif ($department ne "CLA" and getUser($user_id)->{department} and convertDepartments(getUser($user_id)->{department})->{$department} < 1) {
951
      return "<br>Denied! User ($user_id) is not a member of Department ($department)!<br>\n" unless $department eq "CMP";
952
    } elsif ($department eq "EMT" and getUser($user_id)->{emt_verified} == 0) {
953
      return "<br>Denied! User ($user_id) has not had their EMT status verified!<br>\n";
954
    }
955
  }
956
 
957
  my $conflict = findConflict ($user_id, $shift_id, $game_based);
958
  if ($change eq "add" and $conflict) {
959
    return "<br>Denied! There is a conflict ($conflict) with that shift's time!<br>\n";
960
  }
961
 
962
  my $game_type;
963
  if ($department ne "CLA") {
964
    ($game_type) = $dbh->selectrow_array ("select type from ".$game_based." where id = ?", undef, $shift_id);
965
 
966
    if ($game_type =~ /^selected/ and !$leadership_change) {
967
      return "<br>Denied! Only leadership can make changes to 'selected staffing' shifts!<br>\n" unless $department eq "CMP";
968
    }
969
 
970
    if ($change eq "add" and $game_type eq "lead" and convertDepartments(getUser($user_id)->{department})->{$department} < 2 and $ORCUSER->{access} < 3) {
971
      return "<br>Denied! Shift reserved for leadership staff!<br>\n";
972
    }
973
  } else {
974
    $game_type = "class";
975
  }
976
 
977
 
978
#   my $MAXSHIFTS = getSetting ("MAX_SHIFT_SIGNUP_PER_DAY");
979
  my $MAXSHIFTS = getSetting ("MAX_SHIFT_SIGNUP_PER_DAY_".$department);
980
  $MAXSHIFTS = getSetting ("MAX_SHIFT_SIGNUP_PER_DAY") unless defined $MAXSHIFTS;
981
  if ($game_type eq "lead" and $department eq "OFF") { $MAXSHIFTS = 99; }
982
 
983
  my $daily_count;
984
  if ($department eq "CLA") {
985
    # MVP Class Sign-up
986
    $MAXSHIFTS = getSetting ("MAX_CLASS_SIGNUP");
987
    ($daily_count) = $dbh->selectrow_array ("select count(*) from v_class_signup_new where RCid = ? and year(date) = year(now())", undef, $user_id);
988
#   ($daily_count) = $dbh->selectrow_array ("select count(*) from v_shift where RCid = ? and dept = 'CLA'", undef, $user_id);
989
    if ($change eq "add" and $daily_count >= $MAXSHIFTS and !$leadership_change) {
990
      return "<br>Denied! You may only sign up for $MAXSHIFTS Classes!<br>\n";
991
    }
992
  } else {
993
    $daily_count = signUpCount ('get', $user_id, $department);
994
    if ($change eq "add" and $daily_count >= $MAXSHIFTS and !$leadership_change) {
995
      return "<br>Denied! You may only sign up for $MAXSHIFTS $game_type shifts in one day!<br>\n";
996
    }
997
    if ($change eq "add" and $game_based eq "game" and ($department eq "OFF" or $department eq "ANN") and $game_type eq "full length" and !$leadership_change) {
998
      my $dept_table = $department eq 'OFF' ? "v_shift_officiating" : "v_shift_announcer";
999
      my ($full_length_count) = $dbh->selectrow_array ("select count(*) from $dept_table where RCid = ? and gtype = 'full length' and year(date) = year(now())", undef, $user_id);
1000
      my $full_length_max = getSetting("MAX_FULL_LENGTH_SIGNUP_".$department);
1001
      if ($full_length_count >= $full_length_max) {
1002
        my $errormsg = "<br>Denied! You may only sign up to ".($department eq 'OFF' ? "officiate" : "announce")." $full_length_max $game_type game(s) (total)!<br>\n";
1003
        return $errormsg;
1004
      }
1005
    }
1006
  }
1007
 
1008
  my @DBARGS;
1009
  if ($game_based eq "game" or $game_based eq "class") {
1010
    if ($change eq "add" or $change eq "override") {
1011
      $sth = $dbh->prepare("insert into assignment (Gid, role, RCid) values (?, ?, ?)");
1012
    } elsif ($change eq "del") {
1013
      $sth = $dbh->prepare("delete from assignment where Gid = ? and role = ? and RCid= ?");
1014
    }
1015
    @DBARGS = ($shift_id, $role, $user_id);
1016
  } else {
1017
    if ($change eq "add" or $change eq "override") {
1018
      $sth = $dbh->prepare("update shift set assignee_id = ? where id = ? and isnull(assignee_id) = 1");
1019
      @DBARGS = ($user_id, $shift_id);
1020
    } elsif ($change eq "del") {
1021
      $sth = $dbh->prepare("update shift set assignee_id = null where id = ?");
1022
      @DBARGS = ($shift_id);
1023
    }
1024
  }
1025
 
1026
  my $wb_act_code;
1027
  if ($change eq "del" and $department eq "CLA") {
1028
    ($wb_act_code) = $dbh->selectrow_array ("select wb_ticket_act from assignment where Gid = ? and RCid = ? and role like ?", undef, $DBARGS[0], $DBARGS[2], 'CLA-%');
1029
  }
1030
 
1031
  print "<br>attempting to make DB changes...<br>";
1032
  if ($sth->execute (@DBARGS)) {
1033
    $daily_count = signUpCount ($change, $user_id, $department) unless $leadership_change;
1034
    logit ($user_id, "Shift ".ucfirst($change).": $shift_id -> $role");
1035
    logit ($ORCUSER->{RCid}, "OVERRIDE: Shift ".ucfirst($change).": $shift_id -> $role") if $change eq "override";
1036
    if ($department eq "CLA") {
1037
      print "Success!...<br>You've signed up for $daily_count class(es) (you're currently allowed to sign up for $MAXSHIFTS).<br>\n";
1038
      updateWRSTBND ($change, $wb_act_code, $DBARGS[0], $DBARGS[2]);
1039
    } else {
1040
      print "Success!...<br>You've signed up for $daily_count shifts today (you're currently allowed to sign up for $MAXSHIFTS per day).<br>\n";
1041
    }
1042
    return;
1043
  } else {
1044
    if ($department eq "CLA") {
1045
      return "<br><b>You did not get the class</b>, most likely because it filled up while you were looking.<br>\nERROR: ", $sth->errstr();
1046
    } else {
1047
      return "<br><b>You did not get the shift</b>, most likely because someone else took it while you were looking.<br>\nERROR: ", $sth->errstr();
1048
    }
1049
  }
1050
}
1051
 
1052
sub updateWRSTBND {
1053
  my ($change, $wb_act_code, $shift_id, $user_id) = @_;
1054
  use REST::Client;
1055
  use JSON;
1056
  my $headers = { Authorization => getSetting ("WRSTBND_API_KEY") };
1057
  my $client = REST::Client->new();
1058
  $client->setHost('https://core.wrstbnd.io');
1059
 
1060
  my ($accountid) = $dbh->selectrow_array ("select wrstbnd_accountid from RCid_ticket_link left join ticket on MVPid = id where RCid = ? and year = year(now())", undef, $user_id);
1061
 
1062
  if ($change eq "add" or $change eq "override") {
1063
    my ($classid) = $dbh->selectrow_array ("select wrstbnd_id from class where id = ?", undef, $shift_id);
1064
 
1065
    my $body = {
1066
      "eventId"      => "event_893C6u5olU",
1067
      "activeStatus" => "active",
1068
      "ticketTypeId" => $classid
1069
    };
1070
    my $json_body = encode_json $body;
1071
 
1072
    $client->POST(
1073
      '/rest/core/v1/ticket',
1074
      $json_body,
1075
      $headers
1076
    );
1077
    my $response = from_json($client->responseContent());
1078
 
1079
    my $activationCode = $response->{activationCode};
1080
 
1081
    my $api_key = getSetting ("WRSTBND_API_KEY");
1082
    my @add_response = `/bin/curl --location --request POST 'https://core.wrstbnd.io/rest/core/v1/assign' --header 'Authorization: $api_key' --form accountid=$accountid --form ticketactcode=$activationCode --output /dev/null --silent --write-out '%{http_code}\n'`;
1083
    my $add_response = $add_response[$#add_response];
1084
    chomp $add_response;
1085
 
1086
    $dbh->do ("update assignment set wb_ticket_act = ? where Gid = ? and RCid = ? and role like ?", undef, $activationCode, $shift_id, $user_id, 'CLA-%') unless $add_response ne "200";
1087
 
1088
    return;
1089
  } elsif ($change eq "del") {
1090
    my $activationCode = $wb_act_code;
1091
    my $api_key = getSetting ("WRSTBND_API_KEY");
1092
    my $del_response = `/bin/curl --location --request DELETE 'https://core.wrstbnd.io/rest/core/v1/assign' --header 'Authorization: $api_key' --form accountid=$accountid --form ticketactcode=$activationCode --output /dev/null --silent --write-out '%{http_code}\n'`;
1093
  }
1094
 
1095
}
1096
 
1097
sub modShiftTime {
1098
  my ($shift_id, $user_id, $diff) = @_;
1099
  my $ORCUSER = getUser (1);
1100
 
1101
  use Scalar::Util qw(looks_like_number);
1102
  if (!looks_like_number ($diff)) {
1103
    print "<br>ERROR! The time adjustment ($diff) doesn't look like a number.<br>\n";
1104
    return;
1105
  }
1106
 
1107
  my ($validate_assignee) = $dbh->selectrow_array ("select count(*) from v_shift where id = ? and RCid = ?", undef, $shift_id, $user_id);
1108
  if (!$validate_assignee) {
1109
    print "<br>ERROR! This shift is assigned to someone else.<br>\n";
1110
    return;
1111
  }
1112
 
1113
  my $department = getShiftDepartment ($shift_id);
1114
  if (convertDepartments ($ORCUSER->{department})->{$department} < 2 and $ORCUSER->{access} < 5) {
1115
    print "<br>ERROR! You're not authorized to modify this shift's time.<br>\n";
1116
    logit ($ORCUSER->{RCid}, "Unauthorized attempt to modify shift time. ($department, $shift_id)");
1117
    return;
1118
  }
1119
 
1120
  my $rows_changed;
1121
  print "<br>attempting to make DB changes...<br>";
1122
  if ($diff == 0) {
1123
    $rows_changed = $dbh->do ("update shift set mod_time = null where id = ? and assignee_id = ?", undef, $shift_id, $user_id);
1124
  } else {
1125
    $rows_changed = $dbh->do ("update shift set mod_time = ? where id = ? and assignee_id = ?", undef, $diff, $shift_id, $user_id);
1126
  }
1127
 
1128
 
1129
  if (!$rows_changed or $dbh->errstr) {
1130
    print "ERROR: Nothing got updated".$dbh->errstr;
1131
    logit (0, "ERROR modifying a shift time ($diff, $shift_id, $user_id):".$dbh->errstr);
1132
  } else {
1133
    print "SUCCESS: Shift $shift_id succesfully modified by $diff hour(s)";
1134
    logit ($ORCUSER->{RCid}, "SUCCESS: Shift $shift_id succesfully modified by $diff hour(s)");
1135
 
1136
  }
1137
  return;
1138
}
1139
 
1140
sub signUpCount {
1141
  my $action = shift;
1142
  my $id = shift;
1143
  my $dept = shift // "";
1144
 
1145
  if ($id eq $ORCUSER->{RCid}) {
1146
    if ($action eq 'add') {
1147
      if (signUpCount ('get', $id, $dept)) {
1148
        $dbh->do("update sign_up_count set sign_ups = sign_ups + 1 where date = curdate() and RCid = ? and department = ?", undef, $id, $dept);
1149
      } else {
1150
        $dbh->do("replace into sign_up_count (date, RCid, department, sign_ups) values (curdate(), ?, ?, 1)", undef, $id, $dept);
1151
      }
1152
    } elsif ($action eq 'del') {
1153
      if (signUpCount ('get', $id, $dept)) {
1154
        $dbh->do("update sign_up_count set sign_ups = sign_ups - 1 where date = curdate() and RCid = ? and department = ?", undef, $id, $dept);
1155
      }
1156
    }
1157
  }
1158
 
1159
  my ($R) = $dbh->selectrow_array ("select sign_ups from sign_up_count where RCid = ? and department = ? and date = curdate()", undef, $id, $dept);
1160
 
1161
  return $R ? $R : '0';
1162
}
1163
 
1164
sub signUpEligible {
1165
  my $user = shift;
1166
  my $t = shift;
1167
  my $shifttype = shift // "game";
1168
  my $dept = $t->{dept} // "";
1169
  my $DEPTHASH = getDepartments ();
1170
  if ($dept and !exists $DEPTHASH->{$dept}) {
1171
    my %reverso = reverse %{$DEPTHASH};
1172
    $dept = $reverso{$dept};
1173
  }
1174
 
1175
  my $limit = getSetting ("MAX_SHIFT_SIGNUP_PER_DAY_".$dept);
1176
  $limit = getSetting ("MAX_SHIFT_SIGNUP_PER_DAY") unless defined $limit;
1177
 
1178
  if (lc $t->{type} eq "lead" and $dept eq "OFF") { $limit = 99; }
1179
 
1180
  return 0 unless $limit > 0;
1181
 
1182
  my $limitkey = $dept ? "sign_ups_today_".$dept : "sign_ups_today";
1183
 
1184
  if ($shifttype eq "class") {
1185
    my $classid = $t->{id};
1186
    $t->{start_time} =~ s/^(\d+:\d+):00$/$1/;
1187
    ($t->{id}) = $dbh->selectrow_array ("select id from v_class_new where date = ? and location = ? and start_time = ?", undef, $t->{date}, $t->{location}, $t->{start_time});
1188
    $t->{dept} = "CLA";
1189
    $dept = "CLA";
1190
    $t->{type} = "open";
1191
  }
1192
 
1193
  if (findConflict ($user->{RCid}, $t->{id}, $shifttype)) { return 0; }
1194
 
1195
  if (!exists $user->{$limitkey}) {
1196
    $user->{$limitkey} = signUpCount('get', $user->{RCid}, $dept);
1197
  }
1198
 
1199
  if ($shifttype eq "game") {
1200
#    if ($t->{gtype} !~ /^selected/ and $t->{gtype} ne "short track" and $user->{$limitkey} < $limit) {
1201
    if ($t->{gtype} eq "full length" and ($dept eq "OFF" or $dept eq "ANN")) {
1202
      my $table = $dept eq "OFF" ? "v_shift_officiating" : "v_shift_announcer";
1203
      my ($full_length_count) = $dbh->selectrow_array ("select count(*) from $table where RCid = ? and gtype = 'full length' and year(date) = year(now())", undef, $user->{RCid});
1204
      if ($full_length_count >= getSetting ("MAX_FULL_LENGTH_SIGNUP_".$dept)) {
1205
        return 0;
1206
      }
1207
    }
1208
    if (lc $t->{signup} ne "selected" and $user->{$limitkey} < $limit) {
1209
      return 1;
1210
    } else {
1211
      return 0;
1212
    }
1213
  } else {
1214
    if ($dept eq "CLA") {
1215
      # MVP Class Sign-up
1216
      return 0 unless $user->{MVPid};
1217
      my $class_limit = getSetting ("MAX_CLASS_SIGNUP");
1218
      my ($class_count) = $dbh->selectrow_array ("select count(*) from v_class_signup_new where RCid = ? and year(date) = year(now())", undef, $user->{RCid});
1219
      return 0 unless $class_count < $class_limit;
1220
    } else {
1221
      if ($user->{department}->{$dept} < 1) { return 0; }
1222
    }
1223
    if (lc $t->{type} eq "lead" and $user->{department}->{$dept} < 2) { return 0; }
1224
    if (lc $t->{type} eq "manager" and $user->{department}->{$dept} < 3) { return 0; }
1225
    if ($dept eq "EMT" and $user->{emt_verified} == 0) { return 0; }
1226
    if (lc $t->{type} !~ /^selected/ and $user->{$limitkey} < $limit) {
1227
      return 1;
1228
    } else {
1229
      return 0;
1230
    }
1231
  }
1232
}
1233
 
1234
sub findConflict {
1235
  my $rcid = shift;
1236
  my $gid = shift;
1237
  my $type = shift // "";
1238
  my ($date, $start, $end, $existing, $conflicts);
1239
 
1240
  if ($type eq "game") {
1241
  # Are they already signed up for this game? (It's faster to check the two views one at a time...)
1242
#    ($conflicts) = $dbh->selectrow_array ("select count(*) from v_shift_officiating where substring_index(id, '-', 1) = ? and RCid = ?", undef, $gid, $rcid);
1243
    ($conflicts) = $dbh->selectrow_array ("select count(*) from v_shift_officiating where id = ? and RCid = ?", undef, $gid, $rcid);
1244
    if ($conflicts) { return "OFF-".$gid; } # no need to keep looking...
1245
    ($conflicts) = $dbh->selectrow_array ("select count(*) from v_shift_announcer where id = ? and RCid = ?", undef, $gid, $rcid);
1246
    if ($conflicts) { return "ANN-".$gid; } # no need to keep looking...
1247
 
1248
    ($date, $start, $end) = $dbh->selectrow_array ("select distinct date, time, end_time from game where id = ?", undef, $gid);
1249
  } elsif ($type eq "class")  {
1250
    ($conflicts) = $dbh->selectrow_array ("select count(*) from v_class_signup_new where id = ? and RCid = ?", undef, $gid, $rcid);
1251
    if ($conflicts) { return "CLA:".$gid; } # no need to keep looking...
1252
 
1253
    ($date, $start, $end) = $dbh->selectrow_array ("select distinct date, start_time, end_time from v_class_new where id = ?", undef, $gid);
1254
 
1255
  } elsif ($type eq "personal")  {
1256
    ($date, $start, $end, $existing) = @{ $gid };
1257
  } else {
1258
    ($date, $start, $end) = $dbh->selectrow_array ("select distinct date, start_time, end_time from shift where id = ?", undef, $gid);
1259
  }
1260
 
1261
  # Are they signed up for any games that would conflict with this one?
1262
#  my $sth = $dbh->prepare("select count(*) from v_shift_admin_view where id in (select id from game where date = (select date from game where id = ?) and ((time <= (select time from game where id = ?) and end_time > (select time from game where id = ?)) or (time > (select time from game where id = ?) and time < (select end_time from game where id = ?)))) and RCid = ?");
1263
#  my $sth = $dbh->prepare("select count(*) from v_shift_all where id in (select id from v_shift_all where date = (select date from v_shift_all where id = ?) and ((start_time <= (select start_time from v_shift_all where id = ?) and end_time > (select start_time from v_shift_all where id = ?)) or (start_time > (select start_time from v_shift_all where id = ?) and start_time < (select end_time from v_shift_all where id = ?)))) and RCid = ?");
1264
 
1265
  ($conflicts) = $dbh->selectrow_array ("select * from (
1266
    select concat(dept, '-', id) as conflict from v_shift          where date = ? and ((start_time <= ? and end_time > ?) or (start_time > ? and start_time < ?)) and RCid = ? union
1267
    select concat('CLA:', id) as conflict from v_class_signup_new  where date = ? and ((start_time <= ? and end_time > ?) or (start_time > ? and start_time < ?)) and RCid = ? union
1268
    select concat('ANN-', id) as conflict from v_shift_announcer   where date = ? and ((start_time <= ? and end_time > ?) or (start_time > ? and start_time < ?)) and RCid = ? union
1269
    select concat('OFF-', id) as conflict from v_shift_officiating where date = ? and ((start_time <= ? and end_time > ?) or (start_time > ? and start_time < ?)) and RCid = ? ) alltables
1270
    where conflict <> ?",
1271
    undef, $date, $start, $start, $start, $end, $rcid, $date, $start, $start, $start, $end, $rcid, $date, $start, $start, $start, $end, $rcid, $date, $start, $start, $start, $end, $rcid, "PER-".$existing
1272
  );
1273
 
1274
  return $conflicts;
1275
}
1276
 
1277
sub changeLeadShift {
1278
  my ($change, $lshift, $user_id) = @_;
1279
  my $ERRMSG;
1280
 
1281
  my $sth = $dbh->prepare("update lead_shift set assignee_id = ? where id = ?");
1282
 
1283
  print "<br>attempting to make DB changes...<br>";
1284
  if ($change eq "add") {
1285
    $sth->execute($user_id, $lshift)
1286
      or $ERRMSG = "ERROR: Can't execute SQL statement: ".$sth->errstr()."\n";
1287
  } elsif ($change eq "del") {
1288
    $sth->execute('', $lshift)
1289
      or $ERRMSG = "ERROR: Can't execute SQL statement: ".$sth->errstr()."\n";
1290
  }
1291
  if ($ERRMSG) {
1292
    print $ERRMSG;
1293
  } else {
1294
    logit($user_id, "Lead Shift ".ucfirst($change).": $lshift");
1295
    print "Success.<br>";
1296
  }
1297
}
1298
 
1299
sub logit {
1300
  my $RCid = shift;
1301
  my $msg = shift;
1302
  my $sth = $dbh->prepare("insert into log (person_id, ip_address, event) values (?, ?, ?)");
1303
  $sth->execute($RCid, $ENV{REMOTE_ADDR}, $msg);
1304
}
1305
 
1306
sub orglogit {
1307
  my $RCid = shift;
1308
  my $org = shift;
1309
  my $msg = shift;
1310
  $dbh->do ("insert into organization_log (person_id, organization_id, ip_address, event) values (?, ?, ?, ?)", undef, $RCid, $org, $ENV{REMOTE_ADDR}, $msg);
1311
}
1312
 
1313
sub sendUserMFAEMail {
1314
  my $user = shift // return "ERROR [sendUserMFAEMail]: No user data sent to function.";
1315
  use PEEPSMailer;
1316
  use HTML::Tiny;
1317
  my $h = HTML::Tiny->new( mode => 'html' );
1318
 
1319
  return "ERROR [sendUserMFAEMail]: No email address found for user" unless $user->{email};
1320
 
1321
  my $subject = 'WFTDI PEEPS - MFA Verification Code';
1322
  my $body;
1323
  $body = $h->p ("Greetings,");
1324
  $body .= $h->p ("It appears you are trying to log into PEEPS from somewhere new. Here's a code to enter:");
1325
 
1326
  my $random_six_digit_number = 100000 + int(rand(900000));
1327
  my $string_number = sprintf ("%06d", $random_six_digit_number);
1328
  $dbh->do ("update authentication set mfa = ?, mfa_timestamp = now() where person_id = ?", undef, $string_number, $user->{person_id});
1329
 
4 - 1330
  $ENV{HTTPS} = 'ON' if $ENV{SERVER_NAME} =~ /^peeps/;
2 - 1331
  $body .= $h->p ($string_number);
1332
  $body .= $h->p ("Or click ".$h->a ({ href => url ()."?authenticate=".$string_number }, "this link"));
1333
  $body .= $h->br, $h->p ("--PEEPS Automated Emailer");
1334
 
1335
  EmailUser ($user->{email}, $subject, $body);
1336
}
1337
 
1338
sub sendNewUserEMail {
1339
  my $context = shift;
1340
  my $data = shift;
1341
  use PEEPSMailer;
1342
  use HTML::Tiny;
1343
  my $h = HTML::Tiny->new( mode => 'html' );
1344
  my $depts = getDepartments (); # HashRef of the department TLAs -> Display Names...
1345
  my $AccessLevel = getAccessLevels;
1346
 
1347
  my $email = $data->{email};
1348
  my $subject = 'WFTDI PEEPS - New User';
1349
  my $body;
1350
  if ($context eq "New User") {
1351
    $subject .= " Request";
4 - 1352
    $ENV{HTTPS} = 'ON' if $ENV{SERVER_NAME} =~ /^peeps/;
2 - 1353
    my $activationlink = url ()."?activate=".$data->{activation};
1354
    $body = $h->p ("Greetings,");
1355
    $body .= $h->p ("It appears as though you've registered a new account in WFTDI's PEEPS system with the following information:");
1356
    $body .= $h->table ([
1357
      $h->tr ([$h->td ("&nbsp;&nbsp;", "Derby Name:",    $data->{derby_name})]),
1358
      $h->tr ([$h->td ("&nbsp;&nbsp;", "Civil Name:",    join (" ", $data->{name_first}, $data->{name_middle}, $data->{name_last}))]),
1359
#      $h->tr ([$h->td ("&nbsp;&nbsp;", "Pronouns:",      $data->{pronouns})]),
1360
#      $h->tr ([$h->td ("&nbsp;&nbsp;", "TShirt Size:",   $data->{tshirt})]),
1361
      $h->tr ([$h->td ("&nbsp;&nbsp;", "Email Address:", $data->{email})]),
1362
#      $h->tr ([$h->td ("&nbsp;&nbsp;", "Phone:",         $data->{phone})])
1363
    ]);
1364
    $body .= $h->p ("To validate that you've entered a real (and correct) email address (and that you're not a spam-bot), please click the following link:",
1365
      $h->a ({ HREF=>$activationlink }, "Activate my PEEPS Account!"), $h->br,
1366
      "Or you can copy/paste this into the 'Activation Code' box: ".$data->{activation}, $h->br,
1367
      "Once activated, you'll be able to log in.",
1368
      "If you didn't make this request, well, you're still the only one who received this email, and you now have an account request.  You should probably let us know that someone is messing with you.",
1369
      $h->br,
1370
      "--PEEPS Automated Emailer");
1371
  } elsif ($context eq "Activate") {
1372
    $subject .= " Activated!";
1373
    $body = "Greetings again,
1374
 
1375
Your PEEPS account has been actived.
1376
 
1377
--PEEPS Automated Emailer
1378
";
1379
  } else {
1380
    return;
1381
  }
1382
  # send the message
1383
  EmailUser ($email, $subject, $body);
1384
 
1385
}
1386
 
1387
sub isPersonCovered {
1388
  my $pid = shift // "";
1389
  my $date = shift // "";
1390
 
1391
  return "" unless $pid =~ /^\d+$/;
1392
  return "" unless !$date or $date =~ /^\d{4}-\d{2}-\d{2}$/;
1393
 
1394
  my $policy_id;
1395
  if ($date) {
1396
    ($policy_id) = $dbh->selectrow_array ("select id from coverage where person_id = ? and datediff(start, ?) <= 1 and datediff(end, ?) >= 0 and isnull(coverage.terminated) = 1", undef, $pid, $date, $date);
1397
  } else {
1398
    ($policy_id) = $dbh->selectrow_array ("select id from coverage where person_id = ? and datediff(start, now()) < 1 and datediff(end, now()) >= 0 and isnull(coverage.terminated) = 1", undef, $pid);
1399
  }
1400
 
1401
  return $policy_id;
1402
}
1403
 
1404
sub isLeagueCovered {
1405
  my $pid = shift // "";
1406
  my $date = shift // "";
1407
  my $type = shift // "WFTDA General Liability Insurance";
1408
 
1409
  return "" unless $pid =~ /^\d+$/;
1410
  return "" unless !$date or $date =~ /^\d{4}-\d{2}-\d{2}$/;
1411
 
1412
  my $policy_id;
1413
  if ($date) {
1414
    ($policy_id) = $dbh->selectrow_array ("select id from org_coverage where policy_name = ? and organization_id = ? and datediff(start, ?) <= 1 and datediff(end, ?) >= 0 and isnull(org_coverage.terminated) = 1", undef, $type, $pid, $date, $date);
1415
  } else {
1416
    ($policy_id) = $dbh->selectrow_array ("select id from org_coverage where policy_name = ? and organization_id = ? and datediff(start, now()) < 1 and datediff(end, now()) >= 0 and isnull(org_coverage.terminated) = 1", undef, $type, $pid);
1417
  }
1418
 
1419
  return $policy_id;
1420
}
1421
 
1422
sub isLeagueAdmin {
1423
  my $person = shift // "";
1424
 
1425
  if (ref $person eq "HASH") {
1426
    $person = $person->{person_id};
1427
  }
1428
 
1429
  die "ERROR: function isLeagueAdmin(person_id) didn't receive proper argument" unless $person =~ /^\d+$/;
1430
 
1431
  my @array_of_leagues = map { $_->[0] } @{ $dbh->selectall_arrayref ("select member_org_id from role where person_id = ? and role = ?", undef, $person, "League Admin") };
1432
 
1433
  return scalar @array_of_leagues ? \@array_of_leagues : [];
1434
}
1435
 
1436
sub isWFTDAMember {
1437
  my $pid = shift // "";
1438
  return "" unless $pid =~ /^\d+$/;
1439
 
1440
  my ($membership) = $dbh->selectrow_array ("select count(*) from organization where type = ? and status = ? and id in (select member_org_id from role where person_id = ?)", undef, "member league", "Active", $pid);
1441
 
1442
  return $membership;
1443
}
1444
 
1445
sub remainingPolicyDays {
1446
  my $person = shift // "";
1447
  my $policy = shift // "";
1448
 
1449
  return "" unless $person =~ /^\d+$/;
1450
  return "" unless $policy =~ /^\d+$/;
1451
 
1452
  my ($days_remaining) = $dbh->selectrow_array ("select datediff(end, now()) from coverage where id = ? and person_id = ?", undef, $policy, $person);
1453
 
1454
  return defined $days_remaining ? $days_remaining : "ERROR: Policy Not Found";
1455
}
1456
 
1457
sub remainingOrgPolicyDays {
1458
  my $league = shift // "";
1459
  my $policy = shift // "";
1460
 
1461
  return "" unless $league =~ /^\d+$/;
1462
  return "" unless $policy =~ /^\d+$/;
1463
 
1464
  my ($days_remaining) = $dbh->selectrow_array ("select datediff(end, now()) from org_coverage where id = ? and organization_id = ?", undef, $policy, $league);
1465
 
1466
  return defined $days_remaining ? $days_remaining : "ERROR: Policy Not Found";
1467
}
1468
 
1469
1;