| 1 | ################################################################################ |
1 | ################################################################################ |
| 2 | # WeBWorK Online Homework Delivery System |
2 | # WeBWorK Online Homework Delivery System |
| 3 | # Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ |
3 | # Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ |
| 4 | # $CVSHeader: webwork-modperl/lib/WeBWorK/DB.pm,v 1.46 2004/04/27 03:37:56 sh002i Exp $ |
4 | # $CVSHeader: webwork2/lib/WeBWorK/DB.pm,v 1.55 2004/09/23 18:45:48 sh002i Exp $ |
| 5 | # |
5 | # |
| 6 | # This program is free software; you can redistribute it and/or modify it under |
6 | # This program is free software; you can redistribute it and/or modify it under |
| 7 | # the terms of either: (a) the GNU General Public License as published by the |
7 | # the terms of either: (a) the GNU General Public License as published by the |
| 8 | # Free Software Foundation; either version 2, or (at your option) any later |
8 | # Free Software Foundation; either version 2, or (at your option) any later |
| 9 | # version, or (b) the "Artistic License" which comes with this package. |
9 | # version, or (b) the "Artistic License" which comes with this package. |
| … | |
… | |
| 149 | |
149 | |
| 150 | =head1 CONSTRUCTOR |
150 | =head1 CONSTRUCTOR |
| 151 | |
151 | |
| 152 | =over |
152 | =over |
| 153 | |
153 | |
| 154 | =item new($ce) |
154 | =item new($dbLayout) |
| 155 | |
155 | |
| 156 | The C<new> method creates a DB object and brings up the underlying |
156 | The C<new> method creates a DB object and brings up the underlying schema/driver |
| 157 | schema/driver structure according to the C<%dbLayout> hash in C<$ce>, a |
157 | structure according to the hash referenced by C<$dbLayout>. |
| 158 | WeBWorK::CourseEnvironment object. |
|
|
| 159 | |
158 | |
| 160 | =back |
159 | =back |
| 161 | |
160 | |
| 162 | =head2 C<$dbLayout> Format |
161 | =head2 C<$dbLayout> Format |
| 163 | |
162 | |
| … | |
… | |
| 237 | =head1 METHODS |
236 | =head1 METHODS |
| 238 | |
237 | |
| 239 | =cut |
238 | =cut |
| 240 | |
239 | |
| 241 | ################################################################################ |
240 | ################################################################################ |
|
|
241 | # general functions |
|
|
242 | ################################################################################ |
|
|
243 | |
|
|
244 | =head2 General Methods |
|
|
245 | |
|
|
246 | =over |
|
|
247 | |
|
|
248 | =cut |
|
|
249 | |
|
|
250 | =item hashDatabaseOK($fix) |
|
|
251 | |
|
|
252 | If the schema module in use for the C<set> and C<problem> tables is |
|
|
253 | WeBWorK::DB::Schema::GlobalTableEmulator, the database is checked to make sure |
|
|
254 | that the "global user" exists and all sets and problems are assigned to it. If |
|
|
255 | $fix is true, problems found will be fixed: A global user will be created and |
|
|
256 | all sets/problems assigned to it. |
|
|
257 | |
|
|
258 | A list of values is returned. The first value is a boolean value indicating |
|
|
259 | whether problems remain in the database after hashDatabaseOK() is called. The |
|
|
260 | remaining values are a list of strings indicating the particular ways in which |
|
|
261 | the database is (or was) broken. |
|
|
262 | |
|
|
263 | =cut |
|
|
264 | |
|
|
265 | sub hashDatabaseOK { |
|
|
266 | my ($self, $fix) = @_; |
|
|
267 | |
|
|
268 | my $errorsExist; |
|
|
269 | my @results; |
|
|
270 | |
|
|
271 | ##### do we need to run? ##### |
|
|
272 | |
|
|
273 | unless (ref $self->{set} eq "WeBWorK::DB::Schema::GlobalTableEmulator") { |
|
|
274 | #warn "hashDatabaseOK($fix): no checks necessary, set table does not use GlobalTableEmulator.\n"; |
|
|
275 | return 1; |
|
|
276 | } |
|
|
277 | |
|
|
278 | ##### is globalUserID defined? ##### |
|
|
279 | |
|
|
280 | my $globalUserID = $self->{set}->{params}->{globalUserID}; |
|
|
281 | if ($globalUserID eq "") { |
|
|
282 | return 0, "globalUserID not specified (fix this in %dbLayout.)"; |
|
|
283 | } else { |
|
|
284 | #warn "hashDatabaseOK($fix): globalUserID not empty ($globalUserID) -- good.\n"; |
|
|
285 | } |
|
|
286 | |
|
|
287 | ##### does a user with ID globalUserID exist? ##### |
|
|
288 | |
|
|
289 | my $GlobalUser = $self->getUser($globalUserID); |
|
|
290 | if (defined $GlobalUser) { |
|
|
291 | #warn "hashDatabaseOK($fix): user with ID '$globalUserID' exists -- good.\n"; |
|
|
292 | } else { |
|
|
293 | #warn "hashDatabaseOK($fix): user with ID '$globalUserID' not found -- bad!\n"; |
|
|
294 | if ($fix) { |
|
|
295 | $self->addUser($self->newUser( |
|
|
296 | user_id => $globalUserID, |
|
|
297 | first_name => "Global", |
|
|
298 | last_name => "User", |
|
|
299 | email_address => "", |
|
|
300 | student_id => $globalUserID, |
|
|
301 | status => "D", |
|
|
302 | section => "", |
|
|
303 | recitation => "", |
|
|
304 | comment => "This user is used to store data about global set and problem records when using a hash-style database.", |
|
|
305 | )); |
|
|
306 | push @results, "User $globalUserID does not exist -- FIXED."; |
|
|
307 | #warn "hashDatabaseOK($fix): created user with ID '$globalUserID' -- good.\n"; |
|
|
308 | } else { |
|
|
309 | # at this point, we don't go on. no global user means everything below is going to fail. |
|
|
310 | return 0, "User $globalUserID does not exist."; |
|
|
311 | } |
|
|
312 | } |
|
|
313 | |
|
|
314 | ##### are all sets assigned to the user with ID globalUserID? ##### |
|
|
315 | |
|
|
316 | # FIXME: this is way too slow! |
|
|
317 | #my @userSetIDs = $self->{set_user}->list(undef, undef); |
|
|
318 | |
|
|
319 | # Timing Data |
|
|
320 | # |
|
|
321 | # old method: |
|
|
322 | # TIMING 36119 1 1087502726.923311 (0.139117) mth143: WeBWorK::DB::hashDatabaseOK: about to get orphaned UserSets |
|
|
323 | # TIMING 36119 1 1087502768.074221 (41.290027) mth143: WeBWorK::DB::hashDatabaseOK: done getting orphaned UserSets |
|
|
324 | # |
|
|
325 | # new method: |
|
|
326 | # TIMING 36134 0 1087502854.579133 (0.141437) mth143: WeBWorK::DB::hashDatabaseOK: about to get orphaned UserSets |
|
|
327 | # TIMING 36134 0 1087502856.852504 (2.414808) mth143: WeBWorK::DB::hashDatabaseOK: done getting orphaned UserSets |
|
|
328 | # |
|
|
329 | # yay! |
|
|
330 | |
|
|
331 | $WeBWorK::timer->continue(__PACKAGE__ . "::hashDatabaseOK: about to get orphaned UserSets") if defined $WeBWorK::timer; |
|
|
332 | |
|
|
333 | # ... so instead, we're going to do things manually |
|
|
334 | |
|
|
335 | # key: setID, value: hash of userIDs of users to whom this set is assigned |
|
|
336 | my %orphanUserSets; |
|
|
337 | |
|
|
338 | if (ref $self->{set_user} eq "WeBWorK::DB::Schema::WW1Hash") { |
|
|
339 | # we can only do this with WW1Hash |
|
|
340 | #warn "the fast way!\n"; |
|
|
341 | |
|
|
342 | # connect |
|
|
343 | $self->{set_user}->{driver}->connect("ro") |
|
|
344 | or return 0, @results, "Failed to connect to set_user database."; |
|
|
345 | |
|
|
346 | # get PSVNs for global user ( N) |
|
|
347 | # this reads from "login<>global_user" |
|
|
348 | my @globalUserPSVNs = $self->{set_user}->getPSVNsForUser($globalUserID); |
|
|
349 | #warn "found ", scalar @globalUserPSVNs, " PSVNs for the global user.\n"; |
|
|
350 | |
|
|
351 | # get setIDs for PSVNs (M) |
|
|
352 | my @globalUserSetIDs; |
|
|
353 | foreach my $PSVN (@globalUserPSVNs) { |
|
|
354 | #warn "getting setID for PSVN '$PSVN'...\n"; |
|
|
355 | my $string = $self->{set_user}->fetchString($PSVN); |
|
|
356 | my (undef, $setID) = $self->{set_user}->string2IDs($string); # discard userID, problemIDs |
|
|
357 | push @globalUserSetIDs, $setID; |
|
|
358 | #warn "got setID '$setID'\n"; |
|
|
359 | } |
|
|
360 | |
|
|
361 | # get PSVNs for each setID ( N*M) |
|
|
362 | # this reads from "set<>$_" |
|
|
363 | my @okPSVNs = map { $self->{set_user}->getPSVNsForSet($_) } @globalUserSetIDs; |
|
|
364 | #warn "found ", scalar @okPSVNs, " PSVNs for sets assigned to the global user.\n"; |
|
|
365 | |
|
|
366 | # get all PSVNs (N*M) |
|
|
367 | # uses: grep { m/^\d+$/ } keys %{ $self->{driver}->hash() } |
|
|
368 | my @allPSVNs = $self->{set_user}->getAllPSVNs; |
|
|
369 | #warn "found ", scalar @allPSVNs, " PSVNs total.\n"; |
|
|
370 | |
|
|
371 | # eliminate PSVNs of sets that are assigned to the global user |
|
|
372 | my %allPSVNs; |
|
|
373 | @allPSVNs{@allPSVNs} = (); |
|
|
374 | |
|
|
375 | foreach my $PSVN (@okPSVNs) { |
|
|
376 | delete $allPSVNs{$PSVN}; |
|
|
377 | } |
|
|
378 | |
|
|
379 | #warn "the orphan PSVNs are: ", join(", ", keys %allPSVNs), "\n"; |
|
|
380 | |
|
|
381 | # get setIDs for orphan PSVNs |
|
|
382 | foreach my $PSVN (keys %allPSVNs) { |
|
|
383 | #warn "getting userID and setID for PSVN '$PSVN'...\n"; |
|
|
384 | my $string = $self->{set_user}->fetchString($PSVN); |
|
|
385 | my ($userID, $setID) = $self->{set_user}->string2IDs($string); |
|
|
386 | $orphanUserSets{$setID}->{$userID} = 1; |
|
|
387 | #warn "got setID '$setID' for userID '$userID'\n"; |
|
|
388 | } |
|
|
389 | |
|
|
390 | # disconnect |
|
|
391 | $self->{set_user}->{driver}->disconnect; |
|
|
392 | } else { |
|
|
393 | # otherwise, do it the slow way (maybe it's not slow with some other schema?) |
|
|
394 | #warn "oddly enough, set_user isn't using WW1Hash, so we have to use the slow list() method"; |
|
|
395 | my @userSetIDs = $self->{set_user}->list(undef, undef); |
|
|
396 | |
|
|
397 | foreach my $userSetID (@userSetIDs) { |
|
|
398 | my ($userID, $setID) = @$userSetID; |
|
|
399 | $orphanUserSets{$setID}->{$userID} = 1; |
|
|
400 | } |
|
|
401 | |
|
|
402 | foreach my $setID (keys %orphanUserSets) { |
|
|
403 | delete $orphanUserSets{$setID} |
|
|
404 | if exists $orphanUserSets{$setID}->{$globalUserID}; |
|
|
405 | } |
|
|
406 | } |
|
|
407 | |
|
|
408 | $WeBWorK::timer->continue(__PACKAGE__ . "::hashDatabaseOK: done getting orphaned UserSets") if defined $WeBWorK::timer; |
|
|
409 | |
|
|
410 | if (keys %orphanUserSets) { |
|
|
411 | foreach my $setID (keys %orphanUserSets) { |
|
|
412 | # detect "false positives" -- sets that are assigned to the global user |
|
|
413 | # but for some reason don't appear in any set index. |
|
|
414 | if ($self->{set_user}->exists($globalUserID, $setID)) { |
|
|
415 | my @userIDs = keys %{$orphanUserSets{$setID}}; |
|
|
416 | warn "Set ID '$setID' for users '@userIDs' do not appear in any set index. Index re-build recommended.\n"; |
|
|
417 | push @results, "Set ID '$setID' for users '@userIDs' do not appear in any set index. Index re-build recommended.\n"; |
|
|
418 | } else { |
|
|
419 | if ($fix) { |
|
|
420 | my ($userID) = keys %{$orphanUserSets{$setID}}; |
|
|
421 | |
|
|
422 | # grab the first UserSet of this set (connect and disconnect required for get1*) |
|
|
423 | $self->{set_user}->{driver}->connect("ro") |
|
|
424 | or return 0, @results, "Failed to connect to set_user database."; |
|
|
425 | my $RawUserSet = $self->{set_user}->get1NoFilter($userID, $setID); |
|
|
426 | my @RawUserProblems = $self->{problem_user}->getAllNoFilter($userID, $setID); |
|
|
427 | $self->{set_user}->{driver}->disconnect(); |
|
|
428 | unless ($RawUserSet) { |
|
|
429 | warn "failed to fetch UserSet '$setID' for user '$userID'!\n"; |
|
|
430 | next; |
|
|
431 | } |
|
|
432 | |
|
|
433 | # change user ID to globalUserID and add to database |
|
|
434 | $RawUserSet->user_id($globalUserID); |
|
|
435 | $self->{set_user}->add($RawUserSet); |
|
|
436 | foreach my $RawUserProblem (@RawUserProblems) { |
|
|
437 | $RawUserProblem->user_id($globalUserID); |
|
|
438 | $self->{problem_user}->add($RawUserProblem); |
|
|
439 | #warn "hashDatabaseOK($fix): assigned problem '", $RawUserProblem->problem_id, "' from set '$setID' to global user '$globalUserID' -- good.\n"; |
|
|
440 | } |
|
|
441 | |
|
|
442 | #warn "hashDatabaseOK($fix): assigned set '$setID' to global user '$globalUserID' -- good.\n"; |
|
|
443 | push @results, "Set '$setID' not assigned to global user '$globalUserID' -- FIXED."; |
|
|
444 | } else { |
|
|
445 | #warn "hashDatabaseOK($fix): set '$setID' not assigned to global user '$globalUserID' -- bad!\n"; |
|
|
446 | push @results, "Set '$setID' not assigned to global user '$globalUserID'."; |
|
|
447 | } |
|
|
448 | } |
|
|
449 | } |
|
|
450 | } else { |
|
|
451 | #warn "hashDatabaseOK($fix): all sets assigned to global user '$globalUserID' -- good.\n"; |
|
|
452 | } |
|
|
453 | |
|
|
454 | ##### done! ##### |
|
|
455 | |
|
|
456 | my $status = not $errorsExist; |
|
|
457 | return $status, @results; |
|
|
458 | } |
|
|
459 | |
|
|
460 | =back |
|
|
461 | |
|
|
462 | =cut |
|
|
463 | |
|
|
464 | ################################################################################ |
| 242 | # password functions |
465 | # password functions |
| 243 | ################################################################################ |
466 | ################################################################################ |
| 244 | |
467 | |
| 245 | =head2 Password Methods |
468 | =head2 Password Methods |
| 246 | |
469 | |
| … | |
… | |
| 340 | |
563 | |
| 341 | for (my $i = 0; $i < @Passwords; $i++) { |
564 | for (my $i = 0; $i < @Passwords; $i++) { |
| 342 | my $Password = $Passwords[$i]; |
565 | my $Password = $Passwords[$i]; |
| 343 | my $userID = $userIDs[$i]; |
566 | my $userID = $userIDs[$i]; |
| 344 | if (not defined $Password) { |
567 | if (not defined $Password) { |
| 345 | #warn "not defined\n"; |
|
|
| 346 | if ($self->{user}->exists($userID)) { |
568 | if ($self->{user}->exists($userID)) { |
| 347 | #warn "user exists\n"; |
|
|
| 348 | $Password = $self->newPassword(user_id => $userID); |
569 | $Password = $self->newPassword(user_id => $userID); |
| 349 | eval { $self->addPassword($Password) }; |
570 | eval { $self->addPassword($Password) }; |
| 350 | if ($@ and $@ !~ m/password exists/) { |
571 | if ($@ and $@ !~ m/password exists/) { |
| 351 | die "error while auto-creating password record for user $userID: \"$@\""; |
572 | die "error while auto-creating password record for user $userID: \"$@\""; |
| 352 | } |
573 | } |
| … | |
… | |
| 359 | |
580 | |
| 360 | =item putPassword($Password) |
581 | =item putPassword($Password) |
| 361 | |
582 | |
| 362 | $Password is a record object. If a password record with the same user ID exists |
583 | $Password is a record object. If a password record with the same user ID exists |
| 363 | in the password table, the data in the record is replaced with the data in |
584 | in the password table, the data in the record is replaced with the data in |
| 364 | $Password. If a matching password record does not exist, an exception is |
585 | $Password. If a matching password record does not exist, one will be created. |
| 365 | thrown. |
586 | (This is different from most other "put" methods.) |
| 366 | |
587 | |
| 367 | =cut |
588 | =cut |
| 368 | |
589 | |
| 369 | sub putPassword($$) { |
590 | sub putPassword($$) { |
| 370 | my ($self, $Password) = @_; |
591 | my ($self, $Password) = @_; |
| … | |
… | |
| 374 | croak "putPassword: argument 1 must be of type ", $self->{password}->{record} |
595 | croak "putPassword: argument 1 must be of type ", $self->{password}->{record} |
| 375 | unless ref $Password eq $self->{password}->{record}; |
596 | unless ref $Password eq $self->{password}->{record}; |
| 376 | |
597 | |
| 377 | checkKeyfields($Password); |
598 | checkKeyfields($Password); |
| 378 | |
599 | |
| 379 | croak "putPassword: password not found (perhaps you meant to use addPassword?)" |
600 | # For Passwords and PermissionLevels, auto-create a record when it doesn't |
|
|
601 | # already exist. This should be safe. |
| 380 | unless $self->{password}->exists($Password->user_id); |
602 | if ($self->{password}->exists($Password->user_id)) { |
| 381 | |
|
|
| 382 | return $self->{password}->put($Password); |
603 | return $self->{password}->put($Password); |
|
|
604 | } else { |
|
|
605 | return $self->addPassword($Password); |
|
|
606 | } |
| 383 | } |
607 | } |
| 384 | |
608 | |
| 385 | =item deletePassword($userID) |
609 | =item deletePassword($userID) |
| 386 | |
610 | |
| 387 | If a password record with a user ID matching $userID exists in the password |
611 | If a password record with a user ID matching $userID exists in the password |
| … | |
… | |
| 507 | |
731 | |
| 508 | for (my $i = 0; $i < @PermissionLevels; $i++) { |
732 | for (my $i = 0; $i < @PermissionLevels; $i++) { |
| 509 | my $PermissionLevel = $PermissionLevels[$i]; |
733 | my $PermissionLevel = $PermissionLevels[$i]; |
| 510 | my $userID = $userIDs[$i]; |
734 | my $userID = $userIDs[$i]; |
| 511 | if (not defined $PermissionLevel) { |
735 | if (not defined $PermissionLevel) { |
| 512 | #warn "not defined\n"; |
|
|
| 513 | if ($self->{user}->exists($userID)) { |
736 | if ($self->{user}->exists($userID)) { |
| 514 | #warn "user exists\n"; |
|
|
| 515 | $PermissionLevel = $self->newPermissionLevel(user_id => $userID); |
737 | $PermissionLevel = $self->newPermissionLevel(user_id => $userID); |
| 516 | warn $PermissionLevel->toString, "\n"; |
|
|
| 517 | eval { $self->addPermissionLevel($PermissionLevel) }; |
738 | eval { $self->addPermissionLevel($PermissionLevel) }; |
| 518 | if ($@ and $@ !~ m/permission level exists/) { |
739 | if ($@ and $@ !~ m/permission level exists/) { |
| 519 | die "error while auto-creating permission level record for user $userID: \"$@\""; |
740 | die "error while auto-creating permission level record for user $userID: \"$@\""; |
| 520 | } |
741 | } |
| 521 | $PermissionLevels[$i] = $PermissionLevel; |
742 | $PermissionLevels[$i] = $PermissionLevel; |
| … | |
… | |
| 529 | =item putPermissionLevel($PermissionLevel) |
750 | =item putPermissionLevel($PermissionLevel) |
| 530 | |
751 | |
| 531 | $PermissionLevel is a record object. If a permission level record with the same |
752 | $PermissionLevel is a record object. If a permission level record with the same |
| 532 | user ID exists in the permission table, the data in the record is replaced with |
753 | user ID exists in the permission table, the data in the record is replaced with |
| 533 | the data in $PermissionLevel. If a matching permission level record does not |
754 | the data in $PermissionLevel. If a matching permission level record does not |
| 534 | exist, an exception is thrown. |
755 | exist, one will be created. (This is different from most other "put" methods.) |
| 535 | |
756 | |
| 536 | =cut |
757 | =cut |
| 537 | |
758 | |
| 538 | sub putPermissionLevel($$) { |
759 | sub putPermissionLevel($$) { |
| 539 | my ($self, $PermissionLevel) = @_; |
760 | my ($self, $PermissionLevel) = @_; |
| … | |
… | |
| 543 | croak "putPermissionLevel: argument 1 must be of type ", $self->{permission}->{record} |
764 | croak "putPermissionLevel: argument 1 must be of type ", $self->{permission}->{record} |
| 544 | unless ref $PermissionLevel eq $self->{permission}->{record}; |
765 | unless ref $PermissionLevel eq $self->{permission}->{record}; |
| 545 | |
766 | |
| 546 | checkKeyfields($PermissionLevel); |
767 | checkKeyfields($PermissionLevel); |
| 547 | |
768 | |
| 548 | croak "putPermissionLevel: permission level not found (perhaps you meant to use addPermissionLevel?)" |
769 | # For Passwords and PermissionLevels, auto-create a record when it doesn't |
|
|
770 | # already exist. This should be safe. |
| 549 | unless $self->{permission}->exists($PermissionLevel->user_id); |
771 | if ($self->{permission}->exists($PermissionLevel->user_id)) { |
| 550 | |
|
|
| 551 | return $self->{permission}->put($PermissionLevel); |
772 | return $self->{permission}->put($PermissionLevel); |
|
|
773 | } else { |
|
|
774 | return $self->{permission}->add($PermissionLevel); |
|
|
775 | } |
| 552 | } |
776 | } |
| 553 | |
777 | |
| 554 | =item deletePermissionLevel($userID) |
778 | =item deletePermissionLevel($userID) |
| 555 | |
779 | |
| 556 | If a permission level record with a user ID matching $userID exists in the |
780 | If a permission level record with a user ID matching $userID exists in the |
| … | |
… | |
| 1017 | } |
1241 | } |
| 1018 | |
1242 | |
| 1019 | sub countSetUsers { |
1243 | sub countSetUsers { |
| 1020 | my ($self, $setID) = @_; |
1244 | my ($self, $setID) = @_; |
| 1021 | |
1245 | |
|
|
1246 | croak "countSetUsers: requires 1 argument" |
|
|
1247 | unless @_ == 2; |
|
|
1248 | croak "countSetUsers: argument 1 must contain a set_id" |
|
|
1249 | unless defined $setID; |
|
|
1250 | |
|
|
1251 | # inefficient way |
|
|
1252 | #return scalar $self->{set_user}->list(undef, $setID); |
|
|
1253 | |
|
|
1254 | # efficient way |
|
|
1255 | return $self->{set_user}->count(undef, $setID); |
|
|
1256 | } |
|
|
1257 | |
|
|
1258 | sub listSetUsers { |
|
|
1259 | my ($self, $setID) = @_; |
|
|
1260 | |
|
|
1261 | carp "listSetUsers called in SCALAR context: use countSetUsers instead!\n" |
|
|
1262 | unless wantarray; |
|
|
1263 | |
| 1022 | croak "listSetUsers: requires 1 argument" |
1264 | croak "listSetUsers: requires 1 argument" |
| 1023 | unless @_ == 2; |
1265 | unless @_ == 2; |
| 1024 | croak "listSetUsers: argument 1 must contain a set_id" |
1266 | croak "listSetUsers: argument 1 must contain a set_id" |
| 1025 | unless defined $setID; |
1267 | unless defined $setID; |
| 1026 | |
1268 | |
| 1027 | # inefficient way |
|
|
| 1028 | #return scalar $self->{set_user}->list(undef, $setID); |
|
|
| 1029 | |
|
|
| 1030 | # efficient way |
|
|
| 1031 | return $self->{set_user}->count(undef, $setID); |
|
|
| 1032 | } |
|
|
| 1033 | |
|
|
| 1034 | sub listSetUsers { |
|
|
| 1035 | my ($self, $setID) = @_; |
|
|
| 1036 | |
|
|
| 1037 | carp "listSetUsers called in SCALAR context: use countSetUsers instead!\n" |
|
|
| 1038 | unless wantarray; |
|
|
| 1039 | |
|
|
| 1040 | croak "listSetUsers: requires 1 argument" |
|
|
| 1041 | unless @_ == 2; |
|
|
| 1042 | croak "listSetUsers: argument 1 must contain a set_id" |
|
|
| 1043 | unless defined $setID; |
|
|
| 1044 | |
|
|
| 1045 | return map { $_->[0] } # extract user_id |
1269 | return map { $_->[0] } # extract user_id |
| 1046 | $self->{set_user}->list(undef, $setID); |
1270 | $self->{set_user}->list(undef, $setID); |
|
|
1271 | } |
|
|
1272 | |
|
|
1273 | sub countUserSets { |
|
|
1274 | my ($self, $userID) = @_; |
|
|
1275 | |
|
|
1276 | croak "countUserSets: requires 1 argument" |
|
|
1277 | unless @_ == 2; |
|
|
1278 | croak "countUserSets: argument 1 must contain a user_id" |
|
|
1279 | unless defined $userID; |
|
|
1280 | |
|
|
1281 | return $self->{set_user}->count($userID, undef); |
| 1047 | } |
1282 | } |
| 1048 | |
1283 | |
| 1049 | sub listUserSets { |
1284 | sub listUserSets { |
| 1050 | my ($self, $userID) = @_; |
1285 | my ($self, $userID) = @_; |
| 1051 | |
1286 | |
| … | |
… | |
| 1583 | for (my $i = 0; $i < @UserSets; $i++) { |
1818 | for (my $i = 0; $i < @UserSets; $i++) { |
| 1584 | my $UserSet = $UserSets[$i]; |
1819 | my $UserSet = $UserSets[$i]; |
| 1585 | my $GlobalSet = $GlobalSets[$i]; |
1820 | my $GlobalSet = $GlobalSets[$i]; |
| 1586 | next unless defined $UserSet and defined $GlobalSet; |
1821 | next unless defined $UserSet and defined $GlobalSet; |
| 1587 | foreach my $field (@commonFields) { |
1822 | foreach my $field (@commonFields) { |
| 1588 | next if defined $UserSet->$field; |
1823 | #next if defined $UserSet->$field; |
|
|
1824 | # ok, now we're testing for emptiness as well as definedness. |
|
|
1825 | next if defined $UserSet->$field and $UserSet->$field ne ""; |
| 1589 | $UserSet->$field($GlobalSet->$field); |
1826 | $UserSet->$field($GlobalSet->$field); |
| 1590 | } |
1827 | } |
| 1591 | } |
1828 | } |
| 1592 | $WeBWorK::timer->continue("DB: merge done!") if defined($WeBWorK::timer); |
1829 | $WeBWorK::timer->continue("DB: merge done!") if defined($WeBWorK::timer); |
| 1593 | |
1830 | |
| … | |
… | |
| 1684 | next unless defined $UserProblem and defined $GlobalProblem; |
1921 | next unless defined $UserProblem and defined $GlobalProblem; |
| 1685 | foreach my $field (@commonFields) { |
1922 | foreach my $field (@commonFields) { |
| 1686 | # FIXME: WW1Hash upgrades undefined fileds to "" when creating record objects |
1923 | # FIXME: WW1Hash upgrades undefined fileds to "" when creating record objects |
| 1687 | # Shouldn't we be testing for emptiness rather than definedness? |
1924 | # Shouldn't we be testing for emptiness rather than definedness? |
| 1688 | # I think the spec says that if a field is EMPTY the global value is used. |
1925 | # I think the spec says that if a field is EMPTY the global value is used. |
| 1689 | next if defined $UserProblem->$field; |
1926 | #next if defined $UserProblem->$field; |
|
|
1927 | # ok, now we're testing for emptiness as well as definedness. |
|
|
1928 | next if defined $UserProblem->$field and $UserProblem->$field ne ""; |
| 1690 | $UserProblem->$field($GlobalProblem->$field); |
1929 | $UserProblem->$field($GlobalProblem->$field); |
| 1691 | } |
1930 | } |
| 1692 | } |
1931 | } |
| 1693 | $WeBWorK::timer->continue("DB: merge done!") if defined($WeBWorK::timer); |
1932 | $WeBWorK::timer->continue("DB: merge done!") if defined($WeBWorK::timer); |
| 1694 | |
1933 | |