Subversion Repositories PEEPS

Rev

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