Parent Directory
|
Revision Log
small fix to Timing.pm. key fields are now checked to match m/^\w*$/.
1 ################################################################################ 2 # WeBWorK mod_perl (c) 2000-2002 WeBWorK Project 3 # $Id$ 4 ################################################################################ 5 6 package WeBWorK::DB; 7 8 =head1 NAME 9 10 WeBWorK::DB - interface with the WeBWorK databases. 11 12 =head1 SYNOPSIS 13 14 my $db = WeBWorK::DB->new($courseEnvironment); 15 16 my @userIDs = $db->listUsers(); 17 my $Sam = $db->{user}->{record}->new(); 18 19 $Sam->user_id("sammy"); 20 $Sam->first_name("Sam"); 21 $Sam->last_name("Hathaway"); 22 # etc. 23 24 $db->addUser($User); 25 my $Dennis = $db->getUser("dennis"); 26 $Dennis->status("C"); 27 $db->putUser->($Dennis); 28 29 $db->deleteUser("sammy"); 30 31 =head1 DESCRIPTION 32 33 WeBWorK::DB provides a consistent interface to a number of database backends. 34 Access and modification functions are provided for each logical table used by 35 the webwork system. The particular backend ("schema" and "driver"), record 36 class, data source, and additional parameters are specified by the C<%dbLayout> 37 hash in the course environment. 38 39 =head1 ARCHITECTURE 40 41 The new database system uses a three-tier architecture to insulate each layer 42 from the adjacent layers. 43 44 =head2 Top Layer: DB 45 46 The top layer of the architecture is the DB module. It provides the methods 47 listed below, and uses schema modules (via tables) to implement those methods. 48 49 / list* exists* add* get* put* delete* \ <- api 50 +------------------------------------------------------------------+ 51 | DB | 52 +------------------------------------------------------------------+ 53 \ password permission key user set set_user problem problem_user / <- tables 54 55 =head2 Middle Layer: Schemas 56 57 The middle layer of the architecture is provided by one or more schema modules. 58 They are called "schema" modules because they control the structure of the data 59 for a table. This includes odd things like the way multiple tables are encoded 60 in a single hash in the WW1Hash schema, and the encoding scheme used. 61 62 The schema modules provide an API that matches the requirements of the DB 63 layer, on a per-table basis. Each schema module has a style that determines 64 which drivers it can interface with. For example, WW1Hash is a "hash" style 65 schema. SQL is a "dbi" style schema. 66 67 =head3 Examples 68 69 Both WeBWorK 1.x and 2.x courses use: 70 71 / password permission key \ / user \ <- tables provided 72 +-----------------------------+ +----------------+ 73 | Auth1Hash | | Classlist1Hash | 74 +-----------------------------+ +----------------+ 75 \ hash / \ hash / <- driver style required 76 77 WeBWorK 1.x courses also use: 78 79 / set_user problem_user \ / set problem \ 80 +-------------------------+ +---------------------+ 81 | WW1Hash | | GlobalTableEmulator | 82 +-------------------------+ +---------------------+ 83 \ hash / \ null / 84 85 The GlobalTableEmulator schema emulates the global set and problem tables using 86 data from the set_user and problem_user tables. 87 88 WeBWorK 2.x courses also use: 89 90 / set set_user problem problem_user \ 91 +-------------------------------------+ 92 | WW2Hash | 93 +-------------------------------------+ 94 \ hash / 95 96 =head2 Bottom Layer: Drivers 97 98 Driver modules implement a style for a schema. They provide physical access to 99 a data source containing the data for a table. The style of a driver determines 100 what methods it provides. All drivers provide C<connect(MODE)> and 101 C<disconnect()> methods. A hash style driver provides a C<hash()> method which 102 returns the tied hash. A dbi style driver provides a C<handle()> method which 103 returns the DBI handle. 104 105 =head3 Examples 106 107 / hash \ / hash \ / hash \ <- style 108 +--------+ +--------+ +--------+ 109 | DB | | GDBM | | DB3 | 110 +--------+ +--------+ +--------+ 111 112 / dbi \ / ldap \ 113 +-------+ +--------+ 114 | SQL | | LDAP | 115 +-------+ +--------+ 116 117 =head2 Record Types 118 119 In C<%dblayout>, each table is assigned a record class, used for passing 120 complete records to and from the database. The default record classes are 121 subclasses of the WeBWorK::DB::Record class, and are named as follows: User, 122 Password, PermissionLevel, Key, Set, UserSet, Problem, UserProblem. In the 123 following documentation, a reference the the record class for a table means the 124 record class currently defined for that table in C<%dbLayout>. 125 126 =cut 127 128 use strict; 129 use warnings; 130 use Carp; 131 use Data::Dumper; 132 use WeBWorK::Utils qw(runtime_use); 133 134 use constant TABLES => qw(password permission key user set set_user problem problem_user); 135 136 ################################################################################ 137 # constructor 138 ################################################################################ 139 140 =head1 CONSTRUCTOR 141 142 =over 143 144 =item new($ce) 145 146 The C<new> method creates a DB object and brings up the underlying 147 schema/driver structure according to the C<%dbLayout> hash in C<$ce>, a 148 WeBWorK::CourseEnvironment object. 149 150 =back 151 152 =head2 C<%dbLayout> Format 153 154 The C<%dbLayout> hash consists of items keyed by table names. The value of each 155 item is a reference to a hash containing the following items: 156 157 =over 158 159 =item record 160 161 The name of a perl module to use for representing the data in a record. 162 163 =item schema 164 165 The name of a perl module to use for access to the table. 166 167 =item driver 168 169 The name of a perl module to use for access to the data source. 170 171 =item source 172 173 The location of the data source that should be used by the driver module. 174 Depending on the driver, this may be a path, a url, or a DBI spec. 175 176 =item params 177 178 A reference to a hash containing extra information needed by the schema. Some 179 schemas require parameters, some do not. Consult the documentation for the 180 schema in question. 181 182 =back 183 184 For each table defined in C<%dbLayout>, C<new> loads the record, schema, and 185 driver modules. It the schema module's C<tables> method lists the current table 186 (or contains the string "*") and the output of the schema and driver modules' 187 C<style> methods match, the table is installed. Otherwise, an exception is 188 thrown. 189 190 =cut 191 192 sub new($$) { 193 my ($invocant, $ce) = @_; 194 my $class = ref($invocant) || $invocant; 195 my $self = {}; 196 bless $self, $class; # bless this here so we can pass it to the schema 197 198 # load the modules required to handle each table, and create driver 199 my %dbLayout = %{$ce->{dbLayout}}; 200 foreach my $table (keys %dbLayout) { 201 my $layout = $dbLayout{$table}; 202 my $record = $layout->{record}; 203 my $schema = $layout->{schema}; 204 my $driver = $layout->{driver}; 205 my $source = $layout->{source}; 206 my $params = $layout->{params}; 207 208 runtime_use($record); 209 210 runtime_use($driver); 211 my $driverObject = eval { $driver->new($source, $params) }; 212 croak "error instantiating DB driver $driver for table $table: $@" 213 if $@; 214 215 runtime_use($schema); 216 my $schemaObject = eval { $schema->new( 217 $self, $driver->new($source, $params), 218 $table, $record, $params) }; 219 croak "error instantiating DB schema $schema for table $table: $@" 220 if $@; 221 222 $self->{$table} = $schemaObject; 223 } 224 225 return $self; 226 } 227 228 =head1 METHODS 229 230 =cut 231 232 ################################################################################ 233 # password functions 234 ################################################################################ 235 236 =head2 Password Methods 237 238 =over 239 240 =item listPasswords() 241 242 Returns a list of user IDs representing the records in the password table. 243 244 =cut 245 246 sub listPasswords { 247 my ($self) = @_; 248 249 croak "listPasswords: requires 0 arguments" 250 unless @_ == 1; 251 252 return map { $_->[0] } 253 $self->{password}->list(undef); 254 } 255 256 =item addPassword($Password) 257 258 $Password is a record object. The password will be added to the password table 259 if a password with the same user ID does not already exist. If one does exist, 260 an exception is thrown. To add a password, a user with a matching user ID must 261 exist in the user table. 262 263 =cut 264 265 sub addPassword($$) { 266 my ($self, $Password) = @_; 267 268 croak "addPassword: requires 1 argument" 269 unless @_ == 2; 270 croak "addPassword: argument 1 must be of type ", $self->{password}->{record} 271 unless ref $Password eq $self->{password}->{record}; 272 croak "addPassword: password exists (perhaps you meant to use putPassword?)" 273 if $self->{password}->exists($Password->user_id); 274 croak "addPassword: user ", $Password->user_id, " not found" 275 unless $self->{user}->exists($Password->user_id); 276 277 checkKeyfields($Password); 278 279 return $self->{password}->add($Password); 280 } 281 282 =item getPassword($userID) 283 284 If a record with a matching user ID exists, a record object containting that 285 record's data will be returned. If no such record exists, an undefined value 286 will be returned. 287 288 =cut 289 290 sub getPassword($$) { 291 my ($self, $userID) = @_; 292 293 croak "getPassword: requires 1 argument" 294 unless @_ == 2; 295 croak "getPassword: argument 1 must contain a user_id" 296 unless defined $userID; 297 298 return $self->{password}->get($userID); 299 } 300 301 =item putPassword($Password) 302 303 $Password is a record object. If a password record with the same user ID exists 304 in the password table, the data in the record is replaced with the data in 305 $Password. If a matching password record does not exist, an exception is 306 thrown. 307 308 =cut 309 310 sub putPassword($$) { 311 my ($self, $Password) = @_; 312 313 croak "putPassword: requires 1 argument" 314 unless @_ == 2; 315 croak "putPassword: argument 1 must be of type ", $self->{password}->{record} 316 unless ref $Password eq $self->{password}->{record}; 317 croak "putPassword: password not found (perhaps you meant to use addPassword?)" 318 unless $self->{password}->exists($Password->user_id); 319 320 checkKeyfields($Password); 321 322 return $self->{password}->put($Password); 323 } 324 325 =item deletePassword($userID) 326 327 If a password record with a user ID matching $userID exists in the password 328 table, it is removed and the method returns a true value. If one does exist, 329 a false value is returned. 330 331 =cut 332 333 sub deletePassword($$) { 334 my ($self, $userID) = @_; 335 336 croak "putPassword: requires 1 argument" 337 unless @_ == 2; 338 croak "deletePassword: argument 1 must contain a user_id" 339 unless defined $userID; 340 341 return $self->{password}->delete($userID); 342 } 343 344 =back 345 346 =cut 347 348 ################################################################################ 349 # permission functions 350 ################################################################################ 351 352 =head2 Permission Level Methods 353 354 =over 355 356 =item listPermissionLevels() 357 358 Returns a list of user IDs representing the records in the permission table. 359 360 =cut 361 362 sub listPermissionLevels($) { 363 my ($self) = @_; 364 365 croak "listPermissionLevels: requires 0 arguments" 366 unless @_ == 1; 367 368 return map { $_->[0] } 369 $self->{permission}->list(undef); 370 } 371 372 =item addPermissionLevel($PermissionLevel) 373 374 $PermissionLevel is a record object. The permission level will be added to the 375 permission table if a permission level with the same user ID does not already 376 exist. If one does exist, an exception is thrown. To add a permission level, a 377 user with a matching user ID must exist in the user table. 378 379 =cut 380 381 sub addPermissionLevel($$) { 382 my ($self, $PermissionLevel) = @_; 383 384 croak "addPermissionLevel: requires 1 argument" 385 unless @_ == 2; 386 croak "addPermissionLevel: argument 1 must be of type ", $self->{permission}->{record} 387 unless ref $PermissionLevel eq $self->{permission}->{record}; 388 croak "addPermissionLevel: permission level exists (perhaps you meant to use putPermissionLevel?)" 389 if $self->{permission}->exists($PermissionLevel->user_id); 390 croak "addPermissionLevel: user ", $PermissionLevel->user_id, " not found" 391 unless $self->{user}->exists($PermissionLevel->user_id); 392 393 checkKeyfields($PermissionLevel); 394 395 return $self->{permission}->add($PermissionLevel); 396 } 397 398 =item getPermissionLevel($userID) 399 400 If a record with a matching user ID exists, a record object containting that 401 record's data will be returned. If no such record exists, an undefined value 402 will be returned. 403 404 =cut 405 406 sub getPermissionLevel($$) { 407 my ($self, $userID) = @_; 408 409 croak "getPermissionLevel: requires 1 argument" 410 unless @_ == 2; 411 croak "getPermissionLevel: argument 1 must contain a user_id" 412 unless defined $userID; 413 414 return $self->{permission}->get($userID); 415 } 416 417 =item putPermissionLevel($PermissionLevel) 418 419 $PermissionLevel is a record object. If a permission level record with the same 420 user ID exists in the permission table, the data in the record is replaced with 421 the data in $PermissionLevel. If a matching permission level record does not 422 exist, an exception is thrown. 423 424 =cut 425 426 sub putPermissionLevel($$) { 427 my ($self, $PermissionLevel) = @_; 428 429 croak "putPermissionLevel: requires 1 argument" 430 unless @_ == 2; 431 croak "putPermissionLevel: argument 1 must be of type ", $self->{permission}->{record} 432 unless ref $PermissionLevel eq $self->{permission}->{record}; 433 croak "putPermissionLevel: permission level not found (perhaps you meant to use addPermissionLevel?)" 434 unless $self->{permission}->exists($PermissionLevel->user_id); 435 436 checkKeyfields($PermissionLevel); 437 438 return $self->{permission}->put($PermissionLevel); 439 } 440 441 =item deletePermissionLevel($userID) 442 443 If a permission level record with a user ID matching $userID exists in the 444 permission table, it is removed and the method returns a true value. If one 445 does exist, a false value is returned. 446 447 =cut 448 449 sub deletePermissionLevel($$) { 450 my ($self, $userID) = @_; 451 452 croak "deletePermissionLevel: requires 1 argument" 453 unless @_ == 2; 454 croak "deletePermissionLevel: argument 1 must contain a user_id" 455 unless defined $userID; 456 457 return $self->{permission}->delete($userID); 458 } 459 460 ################################################################################ 461 # key functions 462 ################################################################################ 463 464 =head2 Key Methods 465 466 =over 467 468 =item listKeys() 469 470 Returns a list of user IDs representing the records in the key table. 471 472 =cut 473 474 sub listKeys($) { 475 my ($self) = @_; 476 477 croak "listKeys: requires 0 arguments" 478 unless @_ == 1; 479 480 return map { $_->[0] } 481 $self->{key}->list(undef); 482 } 483 484 =item addKey($Key) 485 486 $Key is a record object. The key will be added to the key table if a key with 487 the same user ID does not already exist. If one does exist, an exception is 488 thrown. To add a key, a user with a matching user ID must exist in the user 489 table. 490 491 =cut 492 493 sub addKey($$) { 494 my ($self, $Key) = @_; 495 496 croak "addKey: requires 1 argument" 497 unless @_ == 2; 498 croak "addKey: argument 1 must be of type ", $self->{key}->{record} 499 unless ref $Key eq $self->{key}->{record}; 500 croak "addKey: key exists (perhaps you meant to use putKey?)" 501 if $self->{key}->exists($Key->user_id); 502 croak "addKey: user ", $Key->user_id, " not found" 503 unless $self->{user}->exists($Key->user_id); 504 505 checkKeyfields($Key); 506 507 return $self->{key}->add($Key); 508 } 509 510 =item getKey($userID) 511 512 If a record with a matching user ID exists, a record object containting that 513 record's data will be returned. If no such record exists, an undefined value 514 will be returned. 515 516 =cut 517 518 sub getKey($$) { 519 my ($self, $userID) = @_; 520 521 croak "getKey: requires 1 argument" 522 unless @_ == 2; 523 croak "getKey: argument 1 must contain a user_id" 524 unless defined $userID; 525 526 return $self->{key}->get($userID); 527 } 528 529 =item putKey($Key) 530 531 $Key is a record object. If a key record with the same user ID exists in the 532 key table, the data in the record is replaced with the data in $Key. If a 533 matching key record does not exist, an exception is thrown. 534 535 =cut 536 537 sub putKey($$) { 538 my ($self, $Key) = @_; 539 540 croak "putKey: requires 1 argument" 541 unless @_ == 2; 542 croak "putKey: argument 1 must be of type ", $self->{key}->{record} 543 unless ref $Key eq $self->{key}->{record}; 544 croak "putKey: key not found (perhaps you meant to use addKey?)" 545 unless $self->{key}->exists($Key->user_id); 546 547 checkKeyfields($Key); 548 549 return $self->{key}->put($Key); 550 } 551 552 =item deleteKey($userID) 553 554 If a key record with a user ID matching $userID exists in the key table, it is 555 removed and the method returns a true value. If one does exist, a false value 556 is returned. 557 558 =cut 559 560 sub deleteKey($$) { 561 my ($self, $userID) = @_; 562 563 croak "deleteKey: requires 1 argument" 564 unless @_ == 2; 565 croak "deleteKey: argument 1 must contain a user_id" 566 unless defined $userID; 567 568 return $self->{key}->delete($userID); 569 } 570 571 ################################################################################ 572 # user functions 573 ################################################################################ 574 575 =head2 User Methods 576 577 =over 578 579 =item listUsers() 580 581 Returns a list of user IDs representing the records in the user table. 582 583 =cut 584 585 sub listUsers($) { 586 my ($self) = @_; 587 588 croak "listUsers: requires 0 arguments" 589 unless @_ == 1; 590 591 return map { $_->[0] } 592 $self->{user}->list(undef); 593 } 594 595 =item addUser($User) 596 597 $User is a record object. The user will be added to the user table if a user 598 with the same user ID does not already exist. If one does exist, an exception 599 is thrown. 600 601 =cut 602 603 sub addUser($$) { 604 my ($self, $User) = @_; 605 606 croak "addUser: requires 1 argument" 607 unless @_ == 2; 608 croak "addUser: argument 1 must be of type ", $self->{user}->{record} 609 unless ref $User eq $self->{user}->{record}; 610 croak "addUser: user exists (perhaps you meant to use putUser?)" 611 if $self->{user}->exists($User->user_id); 612 613 checkKeyfields($User); 614 615 return $self->{user}->add($User); 616 } 617 618 =item getUser($userID) 619 620 If a record with a matching user ID exists, a record object containting that 621 record's data will be returned. If no such record exists, an undefined value 622 will be returned. 623 624 =cut 625 626 sub getUser($$) { 627 my ($self, $userID) = @_; 628 629 croak "getUser: requires 1 argument" 630 unless @_ == 2; 631 croak "getUser: argument 1 must contain a user_id" 632 unless defined $userID; 633 634 return $self->{user}->get($userID); 635 } 636 637 =item putUser($User) 638 639 $User is a record object. If a user record with the same user ID exists in the 640 user table, the data in the record is replaced with the data in $User. If a 641 matching user record does not exist, an exception is thrown. 642 643 =cut 644 645 sub putUser($$) { 646 my ($self, $User) = @_; 647 648 croak "putUser: requires 1 argument" 649 unless @_ == 2; 650 croak "putUser: argument 1 must be of type ", $self->{user}->{record} 651 unless ref $User eq $self->{user}->{record}; 652 croak "putUser: user not found (perhaps you meant to use addUser?)" 653 unless $self->{user}->exists($User->user_id); 654 655 checkKeyfields($User); 656 657 return $self->{user}->put($User); 658 } 659 660 =item deleteUser($userID) 661 662 If a user record with a user ID matching $userID exists in the user table, it 663 is removed and the method returns a true value. If one does exist, a false 664 value is returned. When a user record is deleted, all records associated with 665 that user are also deleted. This includes the password, permission, and key 666 records, and all user set records for that user. 667 668 =cut 669 670 sub deleteUser($$) { 671 my ($self, $userID) = @_; 672 673 croak "deleteUser: requires 1 argument" 674 unless @_ == 2; 675 croak "deleteUser: argument 1 must contain a user_id" 676 unless defined $userID; 677 678 #$self->deleteUserSet($userID, $_) 679 # foreach $self->listUserSets($userID); 680 $self->deleteUserSet($userID, undef); 681 $self->deletePassword($userID); 682 $self->deletePermissionLevel($userID); 683 $self->deleteKey($userID); 684 return $self->{user}->delete($userID); 685 } 686 687 ################################################################################ 688 # set functions 689 ################################################################################ 690 691 sub listGlobalSets($) { 692 my ($self) = @_; 693 694 croak "listGlobalSets: requires 0 arguments" 695 unless @_ == 1; 696 697 return map { $_->[0] } 698 $self->{set}->list(undef); 699 } 700 701 sub addGlobalSet($$) { 702 my ($self, $GlobalSet) = @_; 703 704 croak "addGlobalSet: requires 1 argument" 705 unless @_ == 2; 706 croak "addGlobalSet: argument 1 must be of type ", $self->{set}->{record} 707 unless ref $GlobalSet eq $self->{set}->{record}; 708 croak "addGlobalSet: global set exists (perhaps you meant to use putGlobalSet?)" 709 if $self->{set}->exists($GlobalSet->set_id); 710 711 checkKeyfields($GlobalSet); 712 713 return $self->{set}->add($GlobalSet); 714 } 715 716 sub getGlobalSet($$) { 717 my ($self, $setID) = @_; 718 719 croak "getGlobalSet: requires 1 argument" 720 unless @_ == 2; 721 croak "getGlobalSet: argument 1 must contain a set_id" 722 unless defined $setID; 723 724 return $self->{set}->get($setID); 725 } 726 727 sub putGlobalSet($$) { 728 my ($self, $GlobalSet) = @_; 729 730 croak "putGlobalSet: requires 1 argument" 731 unless @_ == 2; 732 croak "putGlobalSet: argument 1 must be of type ", $self->{set}->{record} 733 unless ref $GlobalSet eq $self->{set}->{record}; 734 croak "putGlobalSet: global set not found (perhaps you meant to use addGlobalSet?)" 735 unless $self->{set}->exists($GlobalSet->set_id); 736 737 checkKeyfields($GlobalSet); 738 739 return $self->{set}->put($GlobalSet); 740 } 741 742 sub deleteGlobalSet($$) { 743 my ($self, $setID) = @_; 744 745 croak "deleteGlobalSet: requires 1 argument" 746 unless @_ == 2; 747 croak "deleteGlobalSet: argument 1 must contain a set_id" 748 unless defined $setID or caller eq __PACKAGE__; 749 750 #$self->deleteUserSet($_, $setID) 751 # foreach $self->listSetUsers($setID); 752 #$self->deleteGlobalProblem($setID, $_) 753 # foreach $self->listGlobalProblems($setID); 754 $self->deleteUserSet(undef, $setID); 755 $self->deleteGlobalProblem($setID, undef); 756 return $self->{set}->delete($setID); 757 } 758 759 ################################################################################ 760 # set_user functions 761 ################################################################################ 762 763 sub listSetUsers($$) { 764 my ($self, $setID) = @_; 765 766 croak "listSetUsers: requires 1 argument" 767 unless @_ == 2; 768 croak "listSetUsers: argument 1 must contain a set_id" 769 unless defined $setID; 770 771 return map { $_->[0] } # extract user_id 772 $self->{set_user}->list(undef, $setID); 773 } 774 775 sub listUserSets($$) { 776 my ($self, $userID) = @_; 777 778 croak "listUserSets: requires 1 argument" 779 unless @_ == 2; 780 croak "listUserSets: argument 1 must contain a user_id" 781 unless defined $userID; 782 783 return map { $_->[1] } # extract set_id 784 $self->{set_user}->list($userID, undef); 785 } 786 787 sub addUserSet($$) { 788 my ($self, $UserSet) = @_; 789 790 croak "addUserSet: requires 1 argument" 791 unless @_ == 2; 792 croak "addUserSet: argument 1 must be of type ", $self->{set_user}->{record} 793 unless ref $UserSet eq $self->{set_user}->{record}; 794 croak "addUserSet: user set exists (perhaps you meant to use putUserSet?)" 795 if $self->{set_user}->exists($UserSet->user_id, $UserSet->set_id); 796 croak "addUserSet: user ", $UserSet->user_id, " not found" 797 unless $self->{user}->exists($UserSet->user_id); 798 croak "addUserSet: set ", $UserSet->set_id, " not found" 799 unless $self->{set}->exists($UserSet->set_id); 800 801 checkKeyfields($UserSet); 802 803 return $self->{set_user}->add($UserSet); 804 } 805 806 sub getUserSet($$$) { 807 my ($self, $userID, $setID) = @_; 808 809 croak "getUserSet: requires 2 arguments" 810 unless @_ == 3; 811 croak "getUserSet: argument 1 must contain a user_id" 812 unless defined $userID; 813 croak "getUserSet: argument 2 must contain a set_id" 814 unless defined $setID; 815 816 return $self->{set_user}->get($userID, $setID); 817 } 818 819 sub putUserSet($$) { 820 my ($self, $UserSet) = @_; 821 822 croak "putUserSet: requires 1 argument" 823 unless @_ == 2; 824 croak "putUserSet: argument 1 must be of type ", $self->{set_user}->{record} 825 unless ref $UserSet eq $self->{set_user}->{record}; 826 croak "putUserSet: user set not found (perhaps you meant to use addUserSet?)" 827 unless $self->{set_user}->exists($UserSet->user_id, $UserSet->set_id); 828 croak "putUserSet: user ", $UserSet->user_id, " not found" 829 unless $self->{user}->exists($UserSet->user_id); 830 croak "putUserSet: set ", $UserSet->set_id, " not found" 831 unless $self->{set}->exists($UserSet->set_id); 832 833 checkKeyfields($UserSet); 834 835 return $self->{set_user}->put($UserSet); 836 } 837 838 sub deleteUserSet($$$) { 839 my ($self, $userID, $setID) = @_; 840 841 croak "getUserSet: requires 2 arguments" 842 unless @_ == 3; 843 croak "getUserSet: argument 1 must contain a user_id" 844 unless defined $userID or caller eq __PACKAGE__; 845 croak "getUserSet: argument 2 must contain a set_id" 846 unless defined $userID or caller eq __PACKAGE__; 847 848 #$self->deleteUserProblem($userID, $setID, $_) 849 # foreach $self->listUserProblems($userID, $setID); 850 $self->deleteUserProblem($userID, $setID, undef); 851 return $self->{set_user}->delete($userID, $setID); 852 } 853 854 ################################################################################ 855 # problem functions 856 ################################################################################ 857 858 sub listGlobalProblems($$) { 859 my ($self, $setID) = @_; 860 861 croak "listGlobalProblems: requires 1 arguments" 862 unless @_ == 2; 863 croak "listGlobalProblems: argument 1 must contain a set_id" 864 unless defined $setID; 865 866 return map { $_->[1] } 867 $self->{problem}->list($setID, undef); 868 } 869 870 sub addGlobalProblem($$) { 871 my ($self, $GlobalProblem) = @_; 872 873 croak "addGlobalProblem: requires 1 argument" 874 unless @_ == 2; 875 croak "addGlobalProblem: argument 1 must be of type ", $self->{problem}->{record} 876 unless ref $GlobalProblem eq $self->{problem}->{record}; 877 croak "addGlobalProblem: global problem exists (perhaps you meant to use putGlobalProblem?)" 878 if $self->{problem}->exists($GlobalProblem->set_id, $GlobalProblem->problem_id); 879 croak "addGlobalProblem: set ", $GlobalProblem->set_id, " not found" 880 unless $self->{set}->exists($GlobalProblem->set_id); 881 882 checkKeyfields($GlobalProblem); 883 884 return $self->{problem}->add($GlobalProblem); 885 } 886 887 sub getGlobalProblem($$$) { 888 my ($self, $setID, $problemID) = @_; 889 890 croak "getGlobalProblem: requires 2 arguments" 891 unless @_ == 3; 892 croak "getGlobalProblem: argument 1 must contain a set_id" 893 unless defined $setID; 894 croak "getGlobalProblem: argument 2 must contain a problem_id" 895 unless defined $problemID; 896 897 return $self->{problem}->get($setID, $problemID); 898 } 899 900 sub putGlobalProblem($$) { 901 my ($self, $GlobalProblem) = @_; 902 903 croak "putGlobalProblem: requires 1 argument" 904 unless @_ == 2; 905 croak "putGlobalProblem: argument 1 must be of type ", $self->{problem}->{record} 906 unless ref $GlobalProblem eq $self->{problem}->{record}; 907 croak "putGlobalProblem: global problem not found (perhaps you meant to use addGlobalProblem?)" 908 unless $self->{problem}->exists($GlobalProblem->set_id, $GlobalProblem->problem_id); 909 croak "putGlobalProblem: set ", $GlobalProblem->set_id, " not found" 910 unless $self->{set}->exists($GlobalProblem->set_id); 911 912 checkKeyfields($GlobalProblem); 913 914 return $self->{problem}->put($GlobalProblem); 915 } 916 917 sub deleteGlobalProblem($$$) { 918 my ($self, $setID, $problemID) = @_; 919 920 croak "deleteGlobalProblem: requires 2 arguments" 921 unless @_ == 3; 922 croak "deleteGlobalProblem: argument 1 must contain a set_id" 923 unless defined $setID or caller eq __PACKAGE__; 924 croak "deleteGlobalProblem: argument 2 must contain a problem_id" 925 unless defined $problemID or caller eq __PACKAGE__; 926 927 #$self->deleteUserProblem($_, $setID, $problemID) 928 # foreach $self->listProblemUsers($setID, $problemID); 929 $self->deleteUserProblem(undef, $setID, $problemID); 930 return $self->{problem}->delete($setID, $problemID); 931 } 932 933 ################################################################################ 934 # problem_user functions 935 ################################################################################ 936 937 sub listProblemUsers($$$) { 938 my ($self, $setID, $problemID) = @_; 939 940 croak "listProblemUsers: requires 2 arguments" 941 unless @_ == 3; 942 croak "listProblemUsers: argument 1 must contain a set_id" 943 unless defined $setID; 944 croak "listProblemUsers: argument 2 must contain a problem_id" 945 unless defined $problemID; 946 947 return map { $_->[0] } # extract user_id 948 $self->{problem_user}->list(undef, $setID, $problemID); 949 } 950 951 sub listUserProblems($$$) { 952 my ($self, $userID, $setID) = @_; 953 954 croak "listUserProblems: requires 2 arguments" 955 unless @_ == 3; 956 croak "listUserProblems: argument 1 must contain a user_id" 957 unless defined $userID; 958 croak "listUserProblems: argument 2 must contain a set_id" 959 unless defined $setID; 960 961 return map { $_->[2] } # extract problem_id 962 $self->{problem_user}->list($userID, $setID, undef); 963 } 964 965 sub addUserProblem($$) { 966 my ($self, $UserProblem) = @_; 967 968 croak "addUserProblem: requires 1 argument" 969 unless @_ == 2; 970 croak "addUserProblem: argument 1 must be of type ", $self->{problem_user}->{record} 971 unless ref $UserProblem eq $self->{problem_user}->{record}; 972 croak "addUserProblem: user problem exists (perhaps you meant to use putUserProblem?)" 973 if $self->{problem_user}->exists($UserProblem->user_id, $UserProblem->set_id, $UserProblem->problem_id); 974 croak "addUserProblem: user set ", $UserProblem->set_id, " for user ", $UserProblem->user_id, " not found" 975 unless $self->{set_user}->exists($UserProblem->user_id, $UserProblem->set_id); 976 croak "addUserProblem: problem ", $UserProblem->problem_id, " in set ", $UserProblem->set_id, " not found" 977 unless $self->{problem}->exists($UserProblem->set_id, $UserProblem->problem_id); 978 979 checkKeyfields($UserProblem); 980 981 return $self->{problem_user}->add($UserProblem); 982 } 983 984 sub getUserProblem($$$$) { 985 my ($self, $userID, $setID, $problemID) = @_; 986 987 croak "getUserProblem: requires 3 arguments" 988 unless @_ == 4; 989 croak "getUserProblem: argument 1 must contain a user_id" 990 unless defined $userID; 991 croak "getUserProblem: argument 2 must contain a set_id" 992 unless defined $setID; 993 croak "getUserProblem: argument 3 must contain a problem_id" 994 unless defined $problemID; 995 996 return $self->{problem_user}->get($userID, $setID, $problemID); 997 } 998 999 sub putUserProblem($$) { 1000 my ($self, $UserProblem) = @_; 1001 1002 croak "putUserProblem: requires 1 argument" 1003 unless @_ == 2; 1004 croak "putUserProblem: argument 1 must be of type ", $self->{problem_user}->{record} 1005 unless ref $UserProblem eq $self->{problem_user}->{record}; 1006 croak "putUserProblem: user set ", $UserProblem->set_id, " for user ", $UserProblem->user_id, " not found" 1007 unless $self->{set_user}->exists($UserProblem->user_id, $UserProblem->set_id); 1008 croak "putUserProblem: user problem not found (perhaps you meant to use addUserProblem?)" 1009 unless $self->{problem_user}->exists($UserProblem->user_id, $UserProblem->set_id, $UserProblem->problem_id); 1010 croak "putUserProblem: problem ", $UserProblem->problem_id, " in set ", $UserProblem->set_id, " not found" 1011 unless $self->{problem}->exists($UserProblem->set_id, $UserProblem->problem_id); 1012 1013 checkKeyfields($UserProblem); 1014 1015 return $self->{problem_user}->put($UserProblem); 1016 } 1017 1018 sub deleteUserProblem($$$$) { 1019 my ($self, $userID, $setID, $problemID) = @_; 1020 1021 croak "getUserProblem: requires 3 arguments" 1022 unless @_ == 4; 1023 croak "getUserProblem: argument 1 must contain a user_id" 1024 unless defined $userID or caller eq __PACKAGE__; 1025 croak "getUserProblem: argument 2 must contain a set_id" 1026 unless defined $setID or caller eq __PACKAGE__; 1027 croak "getUserProblem: argument 3 must contain a problem_id" 1028 unless defined $problemID or caller eq __PACKAGE__; 1029 1030 return $self->{problem_user}->delete($userID, $setID, $problemID); 1031 } 1032 1033 ################################################################################ 1034 # set+set_user functions 1035 ################################################################################ 1036 1037 sub getGlobalUserSet { 1038 carp "getGlobalUserSet: this method is deprecated -- use getMergedSet instead"; 1039 return shift->getMergedSet(@_); 1040 } 1041 1042 sub getMergedSet { 1043 my ($self, $userID, $setID) = @_; 1044 1045 croak "getGlobalUserSet: requires 2 arguments" 1046 unless @_ == 3; 1047 croak "getGlobalUserSet: argument 1 must contain a user_id" 1048 unless defined $userID; 1049 croak "getGlobalUserSet: argument 2 must contain a set_id" 1050 unless defined $setID; 1051 1052 my $UserSet = $self->getUserSet($userID, $setID); 1053 return unless $UserSet; 1054 my $GlobalSet = $self->getGlobalSet($setID); 1055 if ($GlobalSet) { 1056 foreach ($UserSet->FIELDS()) { 1057 next unless $GlobalSet->can($_); 1058 next if $UserSet->$_(); 1059 $UserSet->$_($GlobalSet->$_()); 1060 } 1061 } 1062 return $UserSet; 1063 } 1064 1065 ################################################################################ 1066 # problem+problem_user functions 1067 ################################################################################ 1068 1069 sub getGlobalUserProblem { 1070 carp "getGlobalUserProblem: this method is deprecated -- use getMergedProblem instead"; 1071 return shift->getMergedProblem(@_); 1072 } 1073 1074 sub getMergedProblem { 1075 my ($self, $userID, $setID, $problemID) = @_; 1076 1077 croak "getGlobalUserSet: requires 3 arguments" 1078 unless @_ == 4; 1079 croak "getGlobalUserSet: argument 1 must contain a user_id" 1080 unless defined $userID; 1081 croak "getGlobalUserSet: argument 2 must contain a set_id" 1082 unless defined $setID; 1083 croak "getGlobalUserSet: argument 3 must contain a problem_id" 1084 unless defined $problemID; 1085 1086 my $UserProblem = $self->getUserProblem($userID, $setID, $problemID); 1087 return unless $UserProblem; 1088 my $GlobalProblem = $self->getGlobalProblem($setID, $problemID); 1089 if ($GlobalProblem) { 1090 foreach ($UserProblem->FIELDS()) { 1091 next unless $GlobalProblem->can($_); 1092 next if $UserProblem->$_(); 1093 $UserProblem->$_($GlobalProblem->$_()); 1094 } 1095 } 1096 return $UserProblem; 1097 } 1098 1099 ################################################################################ 1100 # debugging 1101 ################################################################################ 1102 1103 sub dumpDB($$) { 1104 my ($self, $table) = @_; 1105 return $self->{$table}->dumpDB(); 1106 } 1107 1108 ################################################################################ 1109 # sanity checking 1110 ################################################################################ 1111 1112 sub checkKeyfields($) { 1113 my ($Record) = @_; 1114 foreach my $keyfield ($Record->KEYFIELDS) { 1115 croak "checkKeyfields: invalid character in $keyfield field (valid characters are [A-Za-z0-9_])" 1116 unless $Record->$keyfield =~ m/^\w*$/; 1117 } 1118 } 1119 1120 =head1 AUTHOR 1121 1122 Written by Sam Hathaway, sh002i (at) math.rochester.edu. 1123 1124 =cut 1125 1126 1;
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |