Subversion Repositories PEEPS

Rev

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