Subversion Repositories VORC

Rev

Rev 262 | Details | Compare with Previous | Last modification | View Log | RSS feed

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