Subversion Repositories PEEPS

Rev

Rev 4 | Rev 12 | 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
 
11 - 387
  printRCHeader("Sign In");
2 - 388
  print<<authpage;
389
  <form action="$ENV{REQUEST_URI}" method=POST name=Req id=Req>
11 - 390
    <h2>Please Sign In</h2>
2 - 391
    <TABLE>
392
    $authenticated->{ERRMSG}
393
authpage
394
 
395
  if ($ENV{'QUERY_STRING'} eq "LOGOUT") {
396
    print "<TR><TD colspan=2>&nbsp</TD></TR>";
397
    print "<TR><TD colspan=2><button onClick=\"location.href='';\">Log In</button></TD></TR>";
398
    print "</TABLE></BODY></HTML>";
399
    exit;
400
  }
401
 
402
  if ($authenticated->{authenticated} eq "inactive") {
403
 
404
    print<<activationpage;
405
      <TR><TD colspan=2 align=center>&nbsp;</TD></TR>
406
      <TR><TD align=right><B>Activation Code:</TD><TD><INPUT type=text id=activate name=activate></TD></TR>
407
      <TR><TD></TD><TD><INPUT type=submit name=login value=Submit></TD></TR>
408
      <TR><TD colspan=2 align=center>&nbsp;</TD></TR>
409
      <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>
410
      <TR><TD colspan=2 align=center><A HREF='' onClick="location.href='?LOGOUT';">[Log Out]</A></TD></TR>
411
      </TABLE></FORM>
412
activationpage
413
 
414
  } elsif ($authenticated->{authenticated} eq "needsMFA") {
415
 
416
    print<<MFApage;
417
      <TR><TD colspan=2 align=center>&nbsp;</TD></TR>
418
      <TR><TD align=right><B>Email Authentication Code:</TD><TD><INPUT type=text id=authenticate name=authenticate></TD></TR>
419
      <TR><TD></TD><TD><INPUT type=submit name=login value=Submit></TD></TR>
420
      <TR><TD colspan=2 align=center>&nbsp;</TD></TR>
421
      <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>
422
      <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>
423
      <TR><TD colspan=2 align=center><A HREF='' onClick="location.href='?LOGOUT';">[Log Out]</A></TD></TR>
424
      </TABLE></FORM>
425
MFApage
426
 
427
  } else {
428
 
429
    print<<authpage2;
430
      <TR>
431
        <TD align=right><B>Username:</TD><TD><INPUT type=text id=login name=userid></TD>
432
      </TR>
433
      <TR>
434
        <TD align=right><B>Password:</TD><TD><INPUT type=password name=pass></TD>
435
      </TR>
436
      <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>
11 - 437
      <TR><TD colspan=2>&nbsp</TD></TR>
2 - 438
      <TR><TD colspan=2 align=center><A HREF="view_user?submit=New%20User">[register as a new user]</A></TD></TR>
439
      <TR><TD colspan=2 align=center><A HREF="recoverAccount">[recover your account]</A></TD></TR>
440
    </TABLE>
441
    </FORM>
442
 
443
    <SCRIPT language="JavaScript">
444
    <!--
445
    document.getElementById("login").focus();
446
 
447
    function Login () {
448
      document.getElementById('Req').action = "$ENV{SCRIPT_NAME}";
449
      document.getElementById('Req').submit.click();
450
      return true;
451
    }
452
 
453
    //-->
454
    </SCRIPT>
455
 
456
authpage2
457
  }
458
 
459
#foreach (keys %ENV) {
460
# print "$_: $ENV{$_}<br>";
461
#}
462
# &JScript;
463
  exit;
464
}
465
 
466
sub canView {
467
  my $A = shift // "";
468
  my $B = shift // "";
469
  # Is A a lead or higher of one of B's Depts? (or they're looking at themselves)
470
  # parameters should be a Hashref to the users' details
471
 
472
  return 1 if $A->{person_id} == $B->{person_id}; # It's the same person.
473
 
474
  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"
475
 
476
  return 1 if $admin_check or $A->{access} > 4; # viewer and target are the same person or it's a SysAdmin.
477
 
478
  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");
479
 
480
  return 1 if $league_admin;
481
 
482
 
483
  return 0;
484
}
485
 
486
sub getShiftDepartment {
487
  my $shiftID = shift // "";
488
  my $dept;
489
 
490
  if ($shiftID =~ /^\d+$/) {
491
    ($dept) = $dbh->selectrow_array ("select dept from shift where id = ?", undef, $shiftID);
492
  } else {
493
    my ($id, $role) = split /-/, $shiftID;
494
    if ($role =~ /^CLA/) {
495
      $dept = "CLA";
496
    } else {
497
      ($dept) = $dbh->selectrow_array ("select distinct department from staff_template where role like ?", undef, $role.'%');
498
    }
499
  }
500
#  } elsif ($shiftID =~ /^\d+-ANN/) {
501
#    $dept = "ANN";
502
#  } else {
503
#    $dept = "OFF";
504
#  }
505
 
506
  return $dept;
507
}
508
 
509
sub getClassID {
510
  my $shift = shift // "";
511
  return unless $shift =~ /^\d+$/;
512
 
513
  my $shiftref = getShiftRef ($shift);
514
  my ($classid) = $dbh->selectrow_array ("select id from class where date = ? and start_time = ? and location = ?", undef, $shiftref->{date}, $shiftref->{start_time}, $shiftref->{location});
515
  return $classid unless !$classid;
516
 
517
  warn "ERROR: No class.id found for shift $shiftref->{id}";
518
  return "";
519
}
520
 
521
sub getShiftRef {
522
  my $shiftID = shift // "";
523
  return unless $shiftID =~ /^\d+$/;
524
 
525
  my ($shiftref) = $dbh->selectrow_hashref ("select * from shift where id = ?", undef, $shiftID);
526
  return $shiftref unless $shiftref->{id} != $shiftID;
527
 
528
  warn "ERROR: Couldn't find shift with ID [$shiftID]";
529
  return "";
530
}
531
 
532
sub getLeagueAffiliation {
533
  my $PEEPSid = shift // "";
534
 
535
  my $results;
536
 
537
  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 = ?");
538
  if ($PEEPSid =~ /^\d+$/) {
539
    $sth->execute ($PEEPSid);
540
    while (my ($orgid, $orgname, $role, $orgtype) = $sth->fetchrow_array ()) {
541
      push (@{$results->{$orgid}}, $role);
542
    }
543
  }
544
  return $results;
545
}
546
 
547
sub getLeagueName {
548
  my $id = shift // "";
549
  my ($name) = $dbh->selectrow_array ("select league_name from organization where id = ?", undef, $id);
550
  return $name;
551
}
552
 
553
sub getLeagues {
554
  my $exclude_id = shift // 0;
555
  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);
556
}
557
 
558
sub getDepartments {
559
  my $RCid = shift // "";
560
  # If we get an RCid, return the list of departments and levels for that user.
561
  #   Otherwise (no parameter), return the list of departments with their display names.
562
 
563
  if ($RCid) {
564
    my $sth = $dbh->prepare("select department from official where RCid = ?");
565
    $sth->execute($RCid);
566
    my ($dlist) = $sth->fetchrow;
567
    return convertDepartments ($dlist);
568
  } else {
569
    my %HASH;
570
    my $sth = $dbh->prepare("select TLA, name from department");
571
    $sth->execute();
572
    while (my ($tla, $name) = $sth->fetchrow) {
573
      $HASH{$tla} = $name;
574
    }
575
    return \%HASH;
576
  }
577
 
578
}
579
 
580
sub convertDepartments {
581
  # For the department membership, converts the DB string back and forth to a hashref...
582
  my $input = shift // "";
583
  my $output;
584
 
585
  if (ref $input eq "HASH") {
586
    $output = join ":", map { $_."-".$input->{$_} } sort keys %{$input};
587
  } else {
588
    foreach (split /:/, $input) {
589
      my ($tla, $level) = split /-/;
590
      $output->{$tla} = $level;
591
    }
592
    $output = {} unless ref $output eq "HASH";
593
  }
594
 
595
  return $output;
596
}
597
 
598
sub getPolicyByID {
599
  my $pid = shift // "";
600
 
601
  (warn "ERROR: No PolicyID passed to getPolicyByID()" and return) unless $pid =~ /^\d+$/;
602
 
603
  my $policy = $dbh->selectrow_hashref ("select * from policy where id = ?", undef, $pid);
604
 
605
  (warn "ERROR: No Policy found with id: $pid" and return) unless $policy->{id} =~ /^\d+$/;
606
 
607
  return $policy;
608
}
609
 
610
sub getCoverageByID {
611
  my $pid = shift // "";
612
  my $person = shift // "";
613
 
614
  (warn "ERROR: No PolicyID passed to getCoverageByID()" and return) unless $pid    =~ /^\d+$/;
615
  (warn "ERROR: No PersonID passed to getCoverageByID()" and return) unless $person =~ /^\d+$/;
616
 
617
  my $policy = $dbh->selectrow_hashref ("select * from coverage where id = ? and person_id = ?", undef, $pid, $person);
618
 
619
  (warn "ERROR: No Coverage found with id: $pid and person_id: $person" and return) unless $policy->{id} =~ /^\d+$/;
620
 
621
  return $policy;
622
}
623
 
624
sub convertTime {
625
  my $time = shift || return;
626
 
627
  if ($time =~ / - /) {
628
    return join " - ", map { convertTime ($_) } split / - /, $time;
629
  }
630
 
631
  $time =~ s/^(\d{1,2}:\d{2}):\d{2}$/$1/;
632
  $time =~ s/^0//;
633
 
634
  if ($ORCUSER->{timeformat} eq "24hr") {
635
    if ($time =~ /^\d{1,2}:\d{2}$/) { return $time; }
636
  } else {
637
    my ($hr, $min) = split /:/, $time;
638
    my $ampm = " am";
639
    if ($hr >= 12) {
640
      $hr -= 12 unless $hr == 12;
641
      $ampm = " pm";
642
    } elsif ($hr == 0) {
643
      $hr = 12;
644
    }
645
    return $hr.":".$min.$ampm;
646
  }
647
}
648
 
649
sub getSchedule {
650
  my $RCid = shift // return "ERROR: No RCid provided to getSchedule";
651
  my $filter = shift // "";
652
  my $output = shift // "";
653
  my $year = 1900 + (localtime)[5];
654
 
655
  my @whereclause;
656
  if ($filter eq "all") {
657
    push @whereclause, "year(date) >= year(now())";
658
  } elsif ($filter eq "prior") {
659
    push @whereclause, "year(date) < year(now())";
660
  } else {
661
    push @whereclause, "date >= date(now())";
662
  }
663
#  if ($RCid ne $ORCUSER->{RCid}) {
664
#    push @whereclause, "dept != 'PER'";
665
#  }
666
 
667
  use DateTime;
668
  my $dt = DateTime->today (time_zone => 'America/Los_Angeles');
669
  $dt =~ s/T00\:00\:00$//;
670
  my $now = DateTime->now (time_zone => 'America/Los_Angeles');
671
 
672
 
673
  use HTML::Tiny;
674
  my $h = HTML::Tiny->new( mode => 'html' );
675
 
676
  my $where = scalar @whereclause ? "where ".join " and ", @whereclause : "";
677
  my @shifts;
678
  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
679
                                          select id, date, dayofweek, track as location, time, role, teams, signup, 'ANN' as dept, volhours from v_shift_announcer where RCid = ? union
680
                                          select id, date, dayofweek, location, time, role, '' as teams, type as signup, dept, volhours from v_shift where RCid = ? union
681
                                          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
682
                           $where order by date, time");
683
  $sth->execute($RCid, $RCid, $RCid, $RCid);
684
  my $hours = 0;
685
  while (my $s = $sth->fetchrow_hashref) {
686
    my ($yyyy, $mm, $dd) = split /\-/, $s->{date};
687
    my $cutoff = DateTime->new(
688
        year => $yyyy,
689
        month => $mm,
690
        day => $dd,
691
        hour => 5,
692
        minute => 0,
693
        second => 0,
694
        time_zone => 'America/Los_Angeles'
695
    );
696
 
697
 
698
    if (!$s->{teams} or $s->{dept} eq "CLA") {
699
      # it's a time-based shift
700
      if ($s->{dept} eq "PER") {
701
        if ($RCid eq $ORCUSER->{RCid}) {
702
          # DROP
703
          $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");
704
        } else {
705
          $s->{location} = "";
706
          $s->{role} = "";
707
        }
708
      } elsif (($RCid == $ORCUSER->{RCid} and $s->{signup} !~ /^selected/ and $now < $cutoff) or ($ORCUSER->{department}->{$s->{dept}} >= 2 or $ORCUSER->{access} >= 5)) {
709
        # DROP
710
        my ($shiftORclass, $linkargs) = ("shift", "");
711
        if ($s->{dept} eq "CLA") {
712
          $shiftORclass = "class";
713
          $linkargs = "&role=$s->{role}";
714
          $s->{role} = $s->{teams};
715
          $s->{teams} = "";
716
        }
717
        $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");
718
        if ($ORCUSER->{department}->{$s->{dept}} >= 2 or $ORCUSER->{access} >= 5) {
719
          # NO SHOW
720
          $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");
721
        }
722
 
723
      }
724
#     $hours += $s->{volhours} unless $s->{dept} eq "PER" or $s->{dept} eq "CLA";
725
 
726
    } elsif (($RCid == $ORCUSER->{RCid} and $s->{signup} !~ /^selected/ and $now < $cutoff) or ($ORCUSER->{department}->{$s->{dept}} >= 2 or $ORCUSER->{access} >= 5)) {
727
      # it's a game shift
728
      #DROP
729
      $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");
730
      if ($ORCUSER->{department}->{$s->{dept}} >= 2 or $ORCUSER->{access} >= 5) {
731
        # NO SHOW
732
        $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");
733
      }
734
#      $hours += $s->{volhours};
735
    }
736
    $s->{role} =~ s/\-\d+$//;
737
 
738
#   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});
739
#   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});
740
    $s->{time} = convertTime $s->{time};
741
    if ($s->{dept} eq "PER") {
742
      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}) ]));
743
    } else {
744
      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}) ]));
745
    }
746
    $hours += $s->{volhours} unless $s->{dept} eq "PER" or $s->{dept} eq "CLA";
747
  }
748
 
749
  if ($output eq "hours") {
750
    return $hours;
751
  }
752
 
753
  if (scalar @shifts) {
754
    return $h->ul ([ @shifts, $h->h5 ("Currently showing $hours hours of Volunteer Time.") ]);
755
  } elsif ($filter eq "prior") {
756
    return $h->p ({ class=>"hint" }, "[nothing to see here]");
757
  } else {
758
    return $h->p ({ class=>"hint" }, "[nothing scheduled at the moment]");
759
  }
760
}
761
 
762
sub getRCid {
763
  my $derbyname = shift;
764
  ($derbyname) = $dbh->selectrow_array ("select RCid from official where derby_name = ?", undef, $derbyname);
765
  return $derbyname;
766
}
767
 
768
sub getSetting {
769
  my $k = shift;
770
 
771
  my ($value) = $dbh->selectrow_array ("select setting.value from setting where setting.key = ?", undef, $k);
772
  return defined $value ? $value : undef;
773
}
774
 
775
sub getUser {
776
  my $ID = shift;
777
 
778
  my $sth;
779
  if ($ID =~ /^\d+$/) {
780
    $sth = $dbh->prepare("select * from person where id = ?");
781
  } elsif ($ID =~ /@/) {
782
    $sth = $dbh->prepare("select * from person where email = ?");
783
  } else {
784
    $sth = $dbh->prepare("select * from person where id = (select person_id from authentication where username = ?)");
785
  }
786
  $sth->execute($ID);
787
  my $user = $sth->fetchrow_hashref;
788
 
789
  my $auth = $dbh->selectrow_hashref ("select * from authentication where person_id = ?", undef, $user->{id});
790
 
791
  map { $user->{$_} = "" unless $user->{$_} } keys %{$user};
792
  map { $user->{$_} = $auth->{$_} ? $auth->{$_} : "" } keys %{$auth};
793
  $user->{person_id} = $user->{id};
794
  return $user->{id} ? $user : "";
795
}
796
 
797
sub getUserEmail {
798
  my $RCid = shift;
799
  my $sth = $dbh->prepare("select email from official where RCid = ?");
800
  $sth->execute($RCid);
801
  my ($email) = $sth->fetchrow_array();
802
  return $email;
803
}
804
 
805
sub getUserDerbyName {
806
  my $RCid = shift;
807
  my $sth = $dbh->prepare("select derby_name from official where RCid = ?");
808
  $sth->execute($RCid);
809
  my ($dname) = $sth->fetchrow_array();
810
  return $dname;
811
}
812
 
813
sub getYears {
814
  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");
815
# my $sth = $dbh->prepare("select distinct year(date) from v_shift_admin_view");
816
  $sth->execute();
817
  my @years;
818
  while (my ($y) =$sth->fetchrow_array()) { push @years, $y; }
819
  return \@years;
820
}
821
 
822
sub printRCHeader {
823
  my $PAGE_TITLE = shift;
824
  my $redirect = shift // "";
825
# use CGI qw/start_html/;
826
  use HTML::Tiny;
827
  my $h = HTML::Tiny->new( mode => 'html' );
4 - 828
  $ENV{HTTPS} = 'ON' if $ENV{SERVER_NAME} =~ /^peeps/;
2 - 829
 
830
#  my $logout = $h->a ({ href=>"index", onClick=>"document.cookie = 'PEEPSAUTH=; expires=Thu, 01 Jan 1970 00:00:01 GMT; path=/';return true;" }, "[Log Out]");
831
  my $referrer = param ("referrer") ? param ("referrer") : $ENV{HTTP_REFERER};
832
  my $logout = (!$referrer or $referrer eq url) ? "" : $h->button ({ onClick=>"window.location.href='$referrer';" }, "Back")."&nbsp;";
833
  $logout .= url =~ /\/(index)?$/ ? "" : $h->button ({ onClick=>"window.location.href='/';" }, "Home")."&nbsp;";
834
#  $logout .= $h->button ({ onClick=>"document.cookie = 'PEEPSAUTH=; expires=Thu, 01 Jan 1970 00:00:01 GMT; path=/'; location.href='/';" }, "Log Out");
835
  $logout .= $h->button ({ onClick=>"location.href='?LOGOUT';" }, "Log Out");
836
 
837
  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 : "";
838
 
839
#  print start_html (-title=>"vORC - $PAGE_TITLE", -style => {'src' => "/style.css"} );
840
 
841
  my $ANALYTICS = <<MATOMO;
842
  var _mtm = window._mtm = window._mtm || [];
843
  _mtm.push({'mtm.startTime': (new Date().getTime()), 'event': 'mtm.Start'});
844
  (function() {
845
    var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
846
    g.async=true; g.src='https://analytics.whump.org/js/container_to4NCtvM.js'; s.parentNode.insertBefore(g,s);
847
  })();
848
MATOMO
849
 
850
  print $h->open ("html");
851
  print $h->head ([$h->title ("PEEPS - $PAGE_TITLE"),
852
                   $h->link  ({ rel  => "stylesheet",
853
                                type => "text/css",
854
                                href => "/style.css" }),
855
                   $redirect ? $h->meta ({ 'http-equiv'=>"refresh", content=>"0; URL=".$redirect }) : "",
856
#                   $h->script ($ANALYTICS)
857
                  ]);
858
  print $h->open ("body");
859
#  print $h->img ({referrerpolicy=>"no-referrer-when-downgrade", src=>"https://analytics.whump.org/matomo.php?idsite=2&amp;rec=1", style=>"border:0", alt=>""});
860
#<html><head><title>Officials' RollerCon Schedule Manager - $PAGE_TITLE</title>
861
#<link rel="stylesheet" type="text/css" href="/style.css">
862
#</head>
863
#<body text="#000000" bgcolor="#FFFFFF" link="#0000EE" vlink="#551A8B" alink="#FF0000">
11 - 864
  print $h->div ({ class=>"sp0" }, [ $h->div ({ class=>"spLeft" },  $h->a ({ href=>"/" }, $h->img ({ src=>"/images/wftdapeeps-powerby-wftdainsurance-2.svg", width=>"400", height=>"75" }))),
865
                                     $h->div ({ class=>"spRight" }, [ $h->h1 (["$PAGE_TITLE", $h->br]),
2 - 866
                                     $loggedinas,
867
                                     ])
868
                                   ]);
869
#print<<rcheader;
870
#  <TABLE>
871
# <TR class="nostripe">
872
#   <TD align=right><img SRC="/logo.jpg"></TD>
873
#   <TD align=center valign=middle><b><font size=+3>Officials' RollerCon<br>Schedule Manager<br>$PAGE_TITLE</FONT></b>
874
# <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>
875
# </TR>
876
 
877
#rcheader
878
}
879
 
880
sub changeShift {
881
  my ($change, $shift_id, $role, $user_id) = @_;
882
  if ($shift_id =~ /(am|pm)/) {
883
    my ($td, $st, $tl) = split /\|/, $shift_id;
884
    my ($hr, $min, $ampm) = split /:|\s/, $st;
885
    if ($ampm eq "pm") { $hr += 12; }
886
    elsif ($ampm eq "am" and $hr == 12) { $hr = "00" }
887
 
888
    $st = $hr.":".$min;
889
    $shift_id = join "|", ($td, $st, $tl);
890
  } else {
891
    $shift_id =~ s/(\d+:\d+):00/$1/;
892
  }
893
#warn join " - ", $change, $shift_id, $role, $user_id;
894
  my $leadership_change = 0;
895
# my $department = getShiftDepartment ($role ? $shift_id."-".$role : $shift_id);
896
  my $department;
897
  if ($shift_id =~ /^\d+$/) {
898
    $department = getShiftDepartment ($role ? $shift_id."-".$role : $shift_id);
899
  } else {
900
    $department = "CLA";
901
    if ($change eq "del") {
902
      ($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);
903
    } else {
904
      if ($change eq "override") {
905
        ($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";
906
      } else {
907
        ($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);
908
      }
909
    }
910
    $role = "CLA-1" unless $role; # If no one has signed up for the class yet, the SQL above doesn't retrieve the first available
911
  }
912
# my $game_based = $role ? "game" : "shift";
913
  my $game_based = $role =~ /^CLA-/ ? "class" : $role ? "game" : "shift";
914
  my $sth;
915
 
916
  if ($change eq "add" or $change eq "override") {
917
    my $taken;
918
    if ($department eq "CLA") {
919
      ($taken) = $shift_id ? 0 : 1;
920
    } elsif ($game_based eq "game") {
921
      ($taken) = $dbh->selectrow_array ("select count(*) from assignment where Gid = ? and role = ?", undef, $shift_id, $role);
922
    } else {
923
      ($taken) = $dbh->selectrow_array ('select count(*) from shift where id = ? and (isnull(assignee_id) = 0 or assignee_id <> "")', undef, $shift_id);
924
    }
925
    if ($taken) {
926
      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";
927
    }
928
  }
929
 
930
  if (lc ($user_id) ne lc ($ORCUSER->{RCid})) { # they're changing someone else's schedule...
931
    if (($department eq "CLA" and $ORCUSER->{department}->{MVP} >= 2) or $ORCUSER->{department}->{$department} >= 2 or $ORCUSER->{access} >= 5 or $ORCUSER->{department}->{VCI} >= 2) {
932
      # the user making the change is either a lead in the dept, a sysadmin, or a VCI lead
933
      logit ($ORCUSER->{RCid}, "$ORCUSER->{derby_name} changed someone else's schedule. ($change, $shift_id, $role, $user_id)");
934
      logit ($user_id, "Schedule was changed by $ORCUSER->{derby_name}. ($change, $shift_id, $role, $user_id)");
935
      $leadership_change = 1;
936
    } else {
937
      logit ($ORCUSER->{RCid}, "Unauthorized attempt to change someone else's schedule. ($change, $shift_id, $role, $user_id)");
938
      return "<br>Denied! You are not authorized to change someone else's schedule in this department ($department).<br>\n";
939
    }
940
  } elsif ($ORCUSER->{department}->{$department} >= 3 or $ORCUSER->{access} >= 5) {
941
    # Managers can sign up for as many shifts within their own department as they like...
942
    $leadership_change = 1;
943
  }
944
 
945
  if ($change eq "add") {
946
    if ($department eq "CLA" and !getUser($user_id)->{MVPid}) {
947
      return "<br>Denied! User ($user_id) does not have an MVP Pass!<br>\n";
948
    } elsif ($department ne "CLA" and getUser($user_id)->{department} and convertDepartments(getUser($user_id)->{department})->{$department} < 1) {
949
      return "<br>Denied! User ($user_id) is not a member of Department ($department)!<br>\n" unless $department eq "CMP";
950
    } elsif ($department eq "EMT" and getUser($user_id)->{emt_verified} == 0) {
951
      return "<br>Denied! User ($user_id) has not had their EMT status verified!<br>\n";
952
    }
953
  }
954
 
955
  my $conflict = findConflict ($user_id, $shift_id, $game_based);
956
  if ($change eq "add" and $conflict) {
957
    return "<br>Denied! There is a conflict ($conflict) with that shift's time!<br>\n";
958
  }
959
 
960
  my $game_type;
961
  if ($department ne "CLA") {
962
    ($game_type) = $dbh->selectrow_array ("select type from ".$game_based." where id = ?", undef, $shift_id);
963
 
964
    if ($game_type =~ /^selected/ and !$leadership_change) {
965
      return "<br>Denied! Only leadership can make changes to 'selected staffing' shifts!<br>\n" unless $department eq "CMP";
966
    }
967
 
968
    if ($change eq "add" and $game_type eq "lead" and convertDepartments(getUser($user_id)->{department})->{$department} < 2 and $ORCUSER->{access} < 3) {
969
      return "<br>Denied! Shift reserved for leadership staff!<br>\n";
970
    }
971
  } else {
972
    $game_type = "class";
973
  }
974
 
975
 
976
#   my $MAXSHIFTS = getSetting ("MAX_SHIFT_SIGNUP_PER_DAY");
977
  my $MAXSHIFTS = getSetting ("MAX_SHIFT_SIGNUP_PER_DAY_".$department);
978
  $MAXSHIFTS = getSetting ("MAX_SHIFT_SIGNUP_PER_DAY") unless defined $MAXSHIFTS;
979
  if ($game_type eq "lead" and $department eq "OFF") { $MAXSHIFTS = 99; }
980
 
981
  my $daily_count;
982
  if ($department eq "CLA") {
983
    # MVP Class Sign-up
984
    $MAXSHIFTS = getSetting ("MAX_CLASS_SIGNUP");
985
    ($daily_count) = $dbh->selectrow_array ("select count(*) from v_class_signup_new where RCid = ? and year(date) = year(now())", undef, $user_id);
986
#   ($daily_count) = $dbh->selectrow_array ("select count(*) from v_shift where RCid = ? and dept = 'CLA'", undef, $user_id);
987
    if ($change eq "add" and $daily_count >= $MAXSHIFTS and !$leadership_change) {
988
      return "<br>Denied! You may only sign up for $MAXSHIFTS Classes!<br>\n";
989
    }
990
  } else {
991
    $daily_count = signUpCount ('get', $user_id, $department);
992
    if ($change eq "add" and $daily_count >= $MAXSHIFTS and !$leadership_change) {
993
      return "<br>Denied! You may only sign up for $MAXSHIFTS $game_type shifts in one day!<br>\n";
994
    }
995
    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) {
996
      my $dept_table = $department eq 'OFF' ? "v_shift_officiating" : "v_shift_announcer";
997
      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);
998
      my $full_length_max = getSetting("MAX_FULL_LENGTH_SIGNUP_".$department);
999
      if ($full_length_count >= $full_length_max) {
1000
        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";
1001
        return $errormsg;
1002
      }
1003
    }
1004
  }
1005
 
1006
  my @DBARGS;
1007
  if ($game_based eq "game" or $game_based eq "class") {
1008
    if ($change eq "add" or $change eq "override") {
1009
      $sth = $dbh->prepare("insert into assignment (Gid, role, RCid) values (?, ?, ?)");
1010
    } elsif ($change eq "del") {
1011
      $sth = $dbh->prepare("delete from assignment where Gid = ? and role = ? and RCid= ?");
1012
    }
1013
    @DBARGS = ($shift_id, $role, $user_id);
1014
  } else {
1015
    if ($change eq "add" or $change eq "override") {
1016
      $sth = $dbh->prepare("update shift set assignee_id = ? where id = ? and isnull(assignee_id) = 1");
1017
      @DBARGS = ($user_id, $shift_id);
1018
    } elsif ($change eq "del") {
1019
      $sth = $dbh->prepare("update shift set assignee_id = null where id = ?");
1020
      @DBARGS = ($shift_id);
1021
    }
1022
  }
1023
 
1024
  my $wb_act_code;
1025
  if ($change eq "del" and $department eq "CLA") {
1026
    ($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-%');
1027
  }
1028
 
1029
  print "<br>attempting to make DB changes...<br>";
1030
  if ($sth->execute (@DBARGS)) {
1031
    $daily_count = signUpCount ($change, $user_id, $department) unless $leadership_change;
1032
    logit ($user_id, "Shift ".ucfirst($change).": $shift_id -> $role");
1033
    logit ($ORCUSER->{RCid}, "OVERRIDE: Shift ".ucfirst($change).": $shift_id -> $role") if $change eq "override";
1034
    if ($department eq "CLA") {
1035
      print "Success!...<br>You've signed up for $daily_count class(es) (you're currently allowed to sign up for $MAXSHIFTS).<br>\n";
1036
      updateWRSTBND ($change, $wb_act_code, $DBARGS[0], $DBARGS[2]);
1037
    } else {
1038
      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";
1039
    }
1040
    return;
1041
  } else {
1042
    if ($department eq "CLA") {
1043
      return "<br><b>You did not get the class</b>, most likely because it filled up while you were looking.<br>\nERROR: ", $sth->errstr();
1044
    } else {
1045
      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();
1046
    }
1047
  }
1048
}
1049
 
1050
sub updateWRSTBND {
1051
  my ($change, $wb_act_code, $shift_id, $user_id) = @_;
1052
  use REST::Client;
1053
  use JSON;
1054
  my $headers = { Authorization => getSetting ("WRSTBND_API_KEY") };
1055
  my $client = REST::Client->new();
1056
  $client->setHost('https://core.wrstbnd.io');
1057
 
1058
  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);
1059
 
1060
  if ($change eq "add" or $change eq "override") {
1061
    my ($classid) = $dbh->selectrow_array ("select wrstbnd_id from class where id = ?", undef, $shift_id);
1062
 
1063
    my $body = {
1064
      "eventId"      => "event_893C6u5olU",
1065
      "activeStatus" => "active",
1066
      "ticketTypeId" => $classid
1067
    };
1068
    my $json_body = encode_json $body;
1069
 
1070
    $client->POST(
1071
      '/rest/core/v1/ticket',
1072
      $json_body,
1073
      $headers
1074
    );
1075
    my $response = from_json($client->responseContent());
1076
 
1077
    my $activationCode = $response->{activationCode};
1078
 
1079
    my $api_key = getSetting ("WRSTBND_API_KEY");
1080
    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'`;
1081
    my $add_response = $add_response[$#add_response];
1082
    chomp $add_response;
1083
 
1084
    $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";
1085
 
1086
    return;
1087
  } elsif ($change eq "del") {
1088
    my $activationCode = $wb_act_code;
1089
    my $api_key = getSetting ("WRSTBND_API_KEY");
1090
    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'`;
1091
  }
1092
 
1093
}
1094
 
1095
sub modShiftTime {
1096
  my ($shift_id, $user_id, $diff) = @_;
1097
  my $ORCUSER = getUser (1);
1098
 
1099
  use Scalar::Util qw(looks_like_number);
1100
  if (!looks_like_number ($diff)) {
1101
    print "<br>ERROR! The time adjustment ($diff) doesn't look like a number.<br>\n";
1102
    return;
1103
  }
1104
 
1105
  my ($validate_assignee) = $dbh->selectrow_array ("select count(*) from v_shift where id = ? and RCid = ?", undef, $shift_id, $user_id);
1106
  if (!$validate_assignee) {
1107
    print "<br>ERROR! This shift is assigned to someone else.<br>\n";
1108
    return;
1109
  }
1110
 
1111
  my $department = getShiftDepartment ($shift_id);
1112
  if (convertDepartments ($ORCUSER->{department})->{$department} < 2 and $ORCUSER->{access} < 5) {
1113
    print "<br>ERROR! You're not authorized to modify this shift's time.<br>\n";
1114
    logit ($ORCUSER->{RCid}, "Unauthorized attempt to modify shift time. ($department, $shift_id)");
1115
    return;
1116
  }
1117
 
1118
  my $rows_changed;
1119
  print "<br>attempting to make DB changes...<br>";
1120
  if ($diff == 0) {
1121
    $rows_changed = $dbh->do ("update shift set mod_time = null where id = ? and assignee_id = ?", undef, $shift_id, $user_id);
1122
  } else {
1123
    $rows_changed = $dbh->do ("update shift set mod_time = ? where id = ? and assignee_id = ?", undef, $diff, $shift_id, $user_id);
1124
  }
1125
 
1126
 
1127
  if (!$rows_changed or $dbh->errstr) {
1128
    print "ERROR: Nothing got updated".$dbh->errstr;
1129
    logit (0, "ERROR modifying a shift time ($diff, $shift_id, $user_id):".$dbh->errstr);
1130
  } else {
1131
    print "SUCCESS: Shift $shift_id succesfully modified by $diff hour(s)";
1132
    logit ($ORCUSER->{RCid}, "SUCCESS: Shift $shift_id succesfully modified by $diff hour(s)");
1133
 
1134
  }
1135
  return;
1136
}
1137
 
1138
sub signUpCount {
1139
  my $action = shift;
1140
  my $id = shift;
1141
  my $dept = shift // "";
1142
 
1143
  if ($id eq $ORCUSER->{RCid}) {
1144
    if ($action eq 'add') {
1145
      if (signUpCount ('get', $id, $dept)) {
1146
        $dbh->do("update sign_up_count set sign_ups = sign_ups + 1 where date = curdate() and RCid = ? and department = ?", undef, $id, $dept);
1147
      } else {
1148
        $dbh->do("replace into sign_up_count (date, RCid, department, sign_ups) values (curdate(), ?, ?, 1)", undef, $id, $dept);
1149
      }
1150
    } elsif ($action eq 'del') {
1151
      if (signUpCount ('get', $id, $dept)) {
1152
        $dbh->do("update sign_up_count set sign_ups = sign_ups - 1 where date = curdate() and RCid = ? and department = ?", undef, $id, $dept);
1153
      }
1154
    }
1155
  }
1156
 
1157
  my ($R) = $dbh->selectrow_array ("select sign_ups from sign_up_count where RCid = ? and department = ? and date = curdate()", undef, $id, $dept);
1158
 
1159
  return $R ? $R : '0';
1160
}
1161
 
1162
sub signUpEligible {
1163
  my $user = shift;
1164
  my $t = shift;
1165
  my $shifttype = shift // "game";
1166
  my $dept = $t->{dept} // "";
1167
  my $DEPTHASH = getDepartments ();
1168
  if ($dept and !exists $DEPTHASH->{$dept}) {
1169
    my %reverso = reverse %{$DEPTHASH};
1170
    $dept = $reverso{$dept};
1171
  }
1172
 
1173
  my $limit = getSetting ("MAX_SHIFT_SIGNUP_PER_DAY_".$dept);
1174
  $limit = getSetting ("MAX_SHIFT_SIGNUP_PER_DAY") unless defined $limit;
1175
 
1176
  if (lc $t->{type} eq "lead" and $dept eq "OFF") { $limit = 99; }
1177
 
1178
  return 0 unless $limit > 0;
1179
 
1180
  my $limitkey = $dept ? "sign_ups_today_".$dept : "sign_ups_today";
1181
 
1182
  if ($shifttype eq "class") {
1183
    my $classid = $t->{id};
1184
    $t->{start_time} =~ s/^(\d+:\d+):00$/$1/;
1185
    ($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});
1186
    $t->{dept} = "CLA";
1187
    $dept = "CLA";
1188
    $t->{type} = "open";
1189
  }
1190
 
1191
  if (findConflict ($user->{RCid}, $t->{id}, $shifttype)) { return 0; }
1192
 
1193
  if (!exists $user->{$limitkey}) {
1194
    $user->{$limitkey} = signUpCount('get', $user->{RCid}, $dept);
1195
  }
1196
 
1197
  if ($shifttype eq "game") {
1198
#    if ($t->{gtype} !~ /^selected/ and $t->{gtype} ne "short track" and $user->{$limitkey} < $limit) {
1199
    if ($t->{gtype} eq "full length" and ($dept eq "OFF" or $dept eq "ANN")) {
1200
      my $table = $dept eq "OFF" ? "v_shift_officiating" : "v_shift_announcer";
1201
      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});
1202
      if ($full_length_count >= getSetting ("MAX_FULL_LENGTH_SIGNUP_".$dept)) {
1203
        return 0;
1204
      }
1205
    }
1206
    if (lc $t->{signup} ne "selected" and $user->{$limitkey} < $limit) {
1207
      return 1;
1208
    } else {
1209
      return 0;
1210
    }
1211
  } else {
1212
    if ($dept eq "CLA") {
1213
      # MVP Class Sign-up
1214
      return 0 unless $user->{MVPid};
1215
      my $class_limit = getSetting ("MAX_CLASS_SIGNUP");
1216
      my ($class_count) = $dbh->selectrow_array ("select count(*) from v_class_signup_new where RCid = ? and year(date) = year(now())", undef, $user->{RCid});
1217
      return 0 unless $class_count < $class_limit;
1218
    } else {
1219
      if ($user->{department}->{$dept} < 1) { return 0; }
1220
    }
1221
    if (lc $t->{type} eq "lead" and $user->{department}->{$dept} < 2) { return 0; }
1222
    if (lc $t->{type} eq "manager" and $user->{department}->{$dept} < 3) { return 0; }
1223
    if ($dept eq "EMT" and $user->{emt_verified} == 0) { return 0; }
1224
    if (lc $t->{type} !~ /^selected/ and $user->{$limitkey} < $limit) {
1225
      return 1;
1226
    } else {
1227
      return 0;
1228
    }
1229
  }
1230
}
1231
 
1232
sub findConflict {
1233
  my $rcid = shift;
1234
  my $gid = shift;
1235
  my $type = shift // "";
1236
  my ($date, $start, $end, $existing, $conflicts);
1237
 
1238
  if ($type eq "game") {
1239
  # Are they already signed up for this game? (It's faster to check the two views one at a time...)
1240
#    ($conflicts) = $dbh->selectrow_array ("select count(*) from v_shift_officiating where substring_index(id, '-', 1) = ? and RCid = ?", undef, $gid, $rcid);
1241
    ($conflicts) = $dbh->selectrow_array ("select count(*) from v_shift_officiating where id = ? and RCid = ?", undef, $gid, $rcid);
1242
    if ($conflicts) { return "OFF-".$gid; } # no need to keep looking...
1243
    ($conflicts) = $dbh->selectrow_array ("select count(*) from v_shift_announcer where id = ? and RCid = ?", undef, $gid, $rcid);
1244
    if ($conflicts) { return "ANN-".$gid; } # no need to keep looking...
1245
 
1246
    ($date, $start, $end) = $dbh->selectrow_array ("select distinct date, time, end_time from game where id = ?", undef, $gid);
1247
  } elsif ($type eq "class")  {
1248
    ($conflicts) = $dbh->selectrow_array ("select count(*) from v_class_signup_new where id = ? and RCid = ?", undef, $gid, $rcid);
1249
    if ($conflicts) { return "CLA:".$gid; } # no need to keep looking...
1250
 
1251
    ($date, $start, $end) = $dbh->selectrow_array ("select distinct date, start_time, end_time from v_class_new where id = ?", undef, $gid);
1252
 
1253
  } elsif ($type eq "personal")  {
1254
    ($date, $start, $end, $existing) = @{ $gid };
1255
  } else {
1256
    ($date, $start, $end) = $dbh->selectrow_array ("select distinct date, start_time, end_time from shift where id = ?", undef, $gid);
1257
  }
1258
 
1259
  # Are they signed up for any games that would conflict with this one?
1260
#  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 = ?");
1261
#  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 = ?");
1262
 
1263
  ($conflicts) = $dbh->selectrow_array ("select * from (
1264
    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
1265
    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
1266
    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
1267
    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
1268
    where conflict <> ?",
1269
    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
1270
  );
1271
 
1272
  return $conflicts;
1273
}
1274
 
1275
sub changeLeadShift {
1276
  my ($change, $lshift, $user_id) = @_;
1277
  my $ERRMSG;
1278
 
1279
  my $sth = $dbh->prepare("update lead_shift set assignee_id = ? where id = ?");
1280
 
1281
  print "<br>attempting to make DB changes...<br>";
1282
  if ($change eq "add") {
1283
    $sth->execute($user_id, $lshift)
1284
      or $ERRMSG = "ERROR: Can't execute SQL statement: ".$sth->errstr()."\n";
1285
  } elsif ($change eq "del") {
1286
    $sth->execute('', $lshift)
1287
      or $ERRMSG = "ERROR: Can't execute SQL statement: ".$sth->errstr()."\n";
1288
  }
1289
  if ($ERRMSG) {
1290
    print $ERRMSG;
1291
  } else {
1292
    logit($user_id, "Lead Shift ".ucfirst($change).": $lshift");
1293
    print "Success.<br>";
1294
  }
1295
}
1296
 
1297
sub logit {
1298
  my $RCid = shift;
1299
  my $msg = shift;
1300
  my $sth = $dbh->prepare("insert into log (person_id, ip_address, event) values (?, ?, ?)");
1301
  $sth->execute($RCid, $ENV{REMOTE_ADDR}, $msg);
1302
}
1303
 
1304
sub orglogit {
1305
  my $RCid = shift;
1306
  my $org = shift;
1307
  my $msg = shift;
1308
  $dbh->do ("insert into organization_log (person_id, organization_id, ip_address, event) values (?, ?, ?, ?)", undef, $RCid, $org, $ENV{REMOTE_ADDR}, $msg);
1309
}
1310
 
1311
sub sendUserMFAEMail {
1312
  my $user = shift // return "ERROR [sendUserMFAEMail]: No user data sent to function.";
1313
  use PEEPSMailer;
1314
  use HTML::Tiny;
1315
  my $h = HTML::Tiny->new( mode => 'html' );
11 - 1316
  $ENV{HTTPS} = 'ON' if $ENV{SERVER_NAME} =~ /^peeps/;
2 - 1317
 
1318
  return "ERROR [sendUserMFAEMail]: No email address found for user" unless $user->{email};
1319
 
1320
  my $random_six_digit_number = 100000 + int(rand(900000));
1321
  my $string_number = sprintf ("%06d", $random_six_digit_number);
1322
  $dbh->do ("update authentication set mfa = ?, mfa_timestamp = now() where person_id = ?", undef, $string_number, $user->{person_id});
1323
 
11 - 1324
  my $subject = 'WFTDI PEEPS - Login MFA Verification Code';
1325
  my $body = $h->p ({ style => "font-family: Verdana;" }, "Greetings,", "It appears you are trying to log into PEEPS from somewhere new. Here's a code to enter:");
1326
  $body .= $h->p ({ style => "font-family: Verdana; font-size: larger; font-weight: bold;" }, $string_number);
1327
  $body .= $h->p ({ style => "font-family: Verdana;" }, "Or click ".$h->a ({ href => url ()."?authenticate=".$string_number }, "this link").".");
1328
  $body .= $h->p ({ style => "font-family: Verdana; font-size: smaller; font-style: italic;" }, "", "Sent by PEEPS Automated Emailer");
2 - 1329
 
1330
  EmailUser ($user->{email}, $subject, $body);
1331
}
1332
 
1333
sub sendNewUserEMail {
1334
  my $context = shift;
1335
  my $data = shift;
1336
  use PEEPSMailer;
1337
  use HTML::Tiny;
1338
  my $h = HTML::Tiny->new( mode => 'html' );
1339
  my $depts = getDepartments (); # HashRef of the department TLAs -> Display Names...
1340
  my $AccessLevel = getAccessLevels;
1341
 
1342
  my $email = $data->{email};
1343
  my $subject = 'WFTDI PEEPS - New User';
1344
  my $body;
1345
  if ($context eq "New User") {
1346
    $subject .= " Request";
4 - 1347
    $ENV{HTTPS} = 'ON' if $ENV{SERVER_NAME} =~ /^peeps/;
2 - 1348
    my $activationlink = url ()."?activate=".$data->{activation};
1349
    $body = $h->p ("Greetings,");
1350
    $body .= $h->p ("It appears as though you've registered a new account in WFTDI's PEEPS system with the following information:");
1351
    $body .= $h->table ([
1352
      $h->tr ([$h->td ("&nbsp;&nbsp;", "Derby Name:",    $data->{derby_name})]),
1353
      $h->tr ([$h->td ("&nbsp;&nbsp;", "Civil Name:",    join (" ", $data->{name_first}, $data->{name_middle}, $data->{name_last}))]),
1354
#      $h->tr ([$h->td ("&nbsp;&nbsp;", "Pronouns:",      $data->{pronouns})]),
1355
#      $h->tr ([$h->td ("&nbsp;&nbsp;", "TShirt Size:",   $data->{tshirt})]),
1356
      $h->tr ([$h->td ("&nbsp;&nbsp;", "Email Address:", $data->{email})]),
1357
#      $h->tr ([$h->td ("&nbsp;&nbsp;", "Phone:",         $data->{phone})])
1358
    ]);
1359
    $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:",
1360
      $h->a ({ HREF=>$activationlink }, "Activate my PEEPS Account!"), $h->br,
1361
      "Or you can copy/paste this into the 'Activation Code' box: ".$data->{activation}, $h->br,
1362
      "Once activated, you'll be able to log in.",
1363
      "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.",
1364
      $h->br,
1365
      "--PEEPS Automated Emailer");
1366
  } elsif ($context eq "Activate") {
1367
    $subject .= " Activated!";
1368
    $body = "Greetings again,
1369
 
1370
Your PEEPS account has been actived.
1371
 
1372
--PEEPS Automated Emailer
1373
";
1374
  } else {
1375
    return;
1376
  }
1377
  # send the message
1378
  EmailUser ($email, $subject, $body);
1379
 
1380
}
1381
 
1382
sub isPersonCovered {
1383
  my $pid = shift // "";
1384
  my $date = shift // "";
1385
 
1386
  return "" unless $pid =~ /^\d+$/;
1387
  return "" unless !$date or $date =~ /^\d{4}-\d{2}-\d{2}$/;
1388
 
1389
  my $policy_id;
1390
  if ($date) {
1391
    ($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);
1392
  } else {
1393
    ($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);
1394
  }
1395
 
1396
  return $policy_id;
1397
}
1398
 
1399
sub isLeagueCovered {
1400
  my $pid = shift // "";
1401
  my $date = shift // "";
1402
  my $type = shift // "WFTDA General Liability Insurance";
1403
 
1404
  return "" unless $pid =~ /^\d+$/;
1405
  return "" unless !$date or $date =~ /^\d{4}-\d{2}-\d{2}$/;
1406
 
1407
  my $policy_id;
1408
  if ($date) {
1409
    ($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);
1410
  } else {
1411
    ($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);
1412
  }
1413
 
1414
  return $policy_id;
1415
}
1416
 
1417
sub isLeagueAdmin {
1418
  my $person = shift // "";
1419
 
1420
  if (ref $person eq "HASH") {
1421
    $person = $person->{person_id};
1422
  }
1423
 
1424
  die "ERROR: function isLeagueAdmin(person_id) didn't receive proper argument" unless $person =~ /^\d+$/;
1425
 
1426
  my @array_of_leagues = map { $_->[0] } @{ $dbh->selectall_arrayref ("select member_org_id from role where person_id = ? and role = ?", undef, $person, "League Admin") };
1427
 
1428
  return scalar @array_of_leagues ? \@array_of_leagues : [];
1429
}
1430
 
1431
sub isWFTDAMember {
1432
  my $pid = shift // "";
1433
  return "" unless $pid =~ /^\d+$/;
1434
 
1435
  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);
1436
 
1437
  return $membership;
1438
}
1439
 
1440
sub remainingPolicyDays {
1441
  my $person = shift // "";
1442
  my $policy = shift // "";
1443
 
1444
  return "" unless $person =~ /^\d+$/;
1445
  return "" unless $policy =~ /^\d+$/;
1446
 
1447
  my ($days_remaining) = $dbh->selectrow_array ("select datediff(end, now()) from coverage where id = ? and person_id = ?", undef, $policy, $person);
1448
 
1449
  return defined $days_remaining ? $days_remaining : "ERROR: Policy Not Found";
1450
}
1451
 
1452
sub remainingOrgPolicyDays {
1453
  my $league = shift // "";
1454
  my $policy = shift // "";
1455
 
1456
  return "" unless $league =~ /^\d+$/;
1457
  return "" unless $policy =~ /^\d+$/;
1458
 
1459
  my ($days_remaining) = $dbh->selectrow_array ("select datediff(end, now()) from org_coverage where id = ? and organization_id = ?", undef, $policy, $league);
1460
 
1461
  return defined $days_remaining ? $days_remaining : "ERROR: Policy Not Found";
1462
}
1463
 
1464
1;