Parent Directory
|
Revision Log
fixed several stupid errors. -sam
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 DESCRIPTION 13 14 WeBWorK::DB provides a consistent interface to a number of database backends. 15 Access and modification functions are provided for each logical table used by 16 the webwork system. The particular backend ("schema" and "driver"), record 17 class, data source, and additional parameters are specified by the %dbLayout 18 hash in the course environment. 19 20 =head1 ARCHITECTURE 21 22 The new database system uses a three-tier architecture to insulate each layer 23 from the adjacent layers. 24 25 =head2 Top Layer: DB 26 27 The top layer of the architecture is the DB module. It provides the methods 28 listed below, and uses schema modules (via tables) to implement those methods. 29 30 / list* exists* add* get* put* delete* \ <- api 31 +------------------------------------------------------------------+ 32 | DB | 33 +------------------------------------------------------------------+ 34 \ password permission key user set set_user problem problem_user / <- tables 35 36 =head2 Middle Layer: Schemas 37 38 The middle layer of the architecture is provided by one or more schema modules. 39 They are called "schema" modules because they control the structure of the data 40 for a table. This includes odd things like the way multiple tables are encoded 41 in a single hash in the WWHash schema, and the encoding scheme used. 42 43 The schema modules provide an API that matches the requirements of the DB 44 layer, on a per-table basis. Each schema module has a style that determines 45 which drivers it can interface with. For example, WW1Hash is a "hash" style 46 schema. SQL is a "dbi" style schema. 47 48 =head3 Examples 49 50 Both WeBWorK 1.x and 2.x courses use: 51 52 / password permission key \ / user \ <- tables provided 53 +-----------------------------+ +----------------+ 54 | Auth1Hash | | Classlist1Hash | 55 +-----------------------------+ +----------------+ 56 \ hash / \ hash / <- driver style required 57 58 WeBWorK 1.x courses also use: 59 60 / set_user problem_user \ / set problem \ 61 +-------------------------+ +---------------------+ 62 | WW1Hash | | GlobalTableEmulator | 63 +-------------------------+ +---------------------+ 64 \ hash / \ null / 65 66 The GlobalTableEmulator schema emulates the global set and problem tables using 67 data from the set_user and problem_user tables. 68 69 WeBWorK 2.x courses also use: 70 71 / set set_user problem problem_user \ 72 +-------------------------------------+ 73 | WW2Hash | 74 +-------------------------------------+ 75 \ hash / 76 77 =head2 Bottom Layer: Drivers 78 79 Driver modules implement a style for a schema. They provide physical access to 80 a data source containing the data for a table. The style of a driver determines 81 what methods it provides. All drivers provide connect(MODE) and disconnect() 82 methods. A hash style driver provides a hash() method which returns the tied 83 hash. A dbi style driver provides a handle() method which returns the DBI 84 handle. 85 86 =head3 Examples 87 88 / hash \ / hash \ / hash \ <- style 89 +--------+ +--------+ +--------+ 90 | DB | | GDBM | | DB3 | 91 +--------+ +--------+ +--------+ 92 93 / dbi \ / ldap \ 94 +-------+ +--------+ 95 | SQL | | LDAP | 96 +-------+ +--------+ 97 98 =cut 99 100 use strict; 101 use warnings; 102 use Data::Dumper; 103 use WeBWorK::Utils qw(runtime_use); 104 105 use constant TABLES => qw(password permission key user set set_user problem problem_user); 106 107 ################################################################################ 108 # constructor 109 ################################################################################ 110 111 =head1 CONSTRUCTOR 112 =over 113 =item new (ENVIRONMENT) 114 115 The C<new> method creates a DB object and brings up the underlying schema/driver 116 structure according to the C<%dbLayout> hash in the ENVIRONMENT. Environment is 117 a C<WeBWorK::CourseEnvironment> object. 118 119 =cut 120 121 sub new($$) { 122 my ($invocant, $ce) = @_; 123 my $class = ref($invocant) || $invocant; 124 my $self = {}; 125 bless $self, $class; # bless this here so we can pass it to the schema 126 127 # load the modules required to handle each table, and create driver 128 foreach my $table (TABLES) { 129 unless (defined $ce->{dbLayout}->{$table}) { 130 warn "ignoring table $table: layout not specified in dbLayout"; # *** 131 next; 132 } 133 134 my $layout = $ce->{dbLayout}->{$table}; 135 my $record = $layout->{record}; 136 my $schema = $layout->{schema}; 137 my $driver = $layout->{driver}; 138 my $source = $layout->{source}; 139 my $params = $layout->{params}; 140 141 runtime_use($record); 142 runtime_use($schema); 143 runtime_use($driver); 144 $self->{$table} = $schema->new( 145 $self, 146 $driver->new($source, $params), 147 $table, 148 $record, 149 $params 150 ); 151 } 152 153 return $self; 154 } 155 156 ################################################################################ 157 # password functions 158 ################################################################################ 159 160 sub listPasswords($) { 161 my ($self) = @_; 162 return map { $_->[0] } 163 $self->{password}->list(undef); 164 } 165 166 sub addPassword($$) { 167 my ($self, $Password) = @_; 168 die "addPassword failed: user ", $Password->user_id, " does not exist.\n" 169 unless $self->{user}->exists($Password->user_id); 170 return $self->{password}->add($Password); 171 } 172 173 sub getPassword($$) { 174 my ($self, $userID) = @_; 175 return $self->{password}->get($userID); 176 } 177 178 sub putPassword($$) { 179 my ($self, $Password) = @_; 180 return $self->{password}->put($Password); 181 } 182 183 sub deletePassword($$) { 184 my ($self, $userID) = @_; 185 return $self->{password}->delete($userID); 186 } 187 188 ################################################################################ 189 # permission functions 190 ################################################################################ 191 192 sub listPermissionLevels($) { 193 my ($self) = @_; 194 return map { $_->[0] } 195 $self->{permission}->list(undef); 196 } 197 198 sub addPermissionLevel($$) { 199 my ($self, $PermissionLevel) = @_; 200 die "addPermissionLevel failed: user ", $PermissionLevel->user_id, " does not exist.\n" 201 unless $self->{user}->exists($PermissionLevel->user_id); 202 return $self->{permission}->add($PermissionLevel); 203 } 204 205 sub getPermissionLevel($$) { 206 my ($self, $userID) = @_; 207 return $self->{permission}->get($userID); 208 } 209 210 sub putPermissionLevel($$) { 211 my ($self, $PermissionLevel) = @_; 212 return $self->{permission}->put($PermissionLevel); 213 } 214 215 sub deletePermissionLevel($$) { 216 my ($self, $userID) = @_; 217 return $self->{permission}->delete($userID); 218 } 219 220 ################################################################################ 221 # key functions 222 ################################################################################ 223 224 sub listKeys($) { 225 my ($self) = @_; 226 return map { $_->[0] } 227 $self->{key}->list(undef); 228 } 229 230 sub addKey($$) { 231 my ($self, $Key) = @_; 232 die "addKey failed: user ", $Key->user_id, " does not exist.\n" 233 unless $self->{user}->exists($Key->user_id); 234 return $self->{key}->add($Key); 235 } 236 237 sub getKey($$) { 238 my ($self, $userID) = @_; 239 return $self->{key}->get($userID); 240 } 241 242 sub putKey($$) { 243 my ($self, $Key) = @_; 244 return $self->{key}->put($Key); 245 } 246 247 sub deleteKey($$) { 248 my ($self, $userID) = @_; 249 return $self->{key}->delete($userID); 250 } 251 252 ################################################################################ 253 # user functions 254 ################################################################################ 255 256 sub listUsers($) { 257 my ($self) = @_; 258 return map { $_->[0] } 259 $self->{user}->list(undef); 260 } 261 262 sub addUser($$) { 263 my ($self, $User) = @_; 264 return $self->{user}->add($User); 265 } 266 267 sub getUser($$) { 268 my ($self, $userID) = @_; 269 return $self->{user}->get($userID); 270 } 271 272 sub putUser($$) { 273 my ($self, $User) = @_; 274 return $self->{user}->put($User); 275 } 276 277 sub deleteUser($$) { 278 my ($self, $userID) = @_; 279 $self->deletePassword($userID); 280 $self->deletePermissionLevel($userID); 281 $self->deleteKey($userID); 282 $self->deleteUserSet($userID, $_) 283 foreach $self->listUsers(); 284 return $self->{user}->delete($userID); 285 } 286 287 ################################################################################ 288 # set functions 289 ################################################################################ 290 291 sub listGlobalSets($) { 292 my ($self) = @_; 293 return map { $_->[0] } 294 $self->{set}->list(undef); 295 } 296 297 sub addGlobalSet($$) { 298 my ($self, $GlobalSet) = @_; 299 return $self->{set}->add($GlobalSet); 300 } 301 302 sub getGlobalSet($$) { 303 my ($self, $setID) = @_; 304 return $self->{set}->get($setID); 305 } 306 307 sub putGlobalSet($$) { 308 my ($self, $GlobalSet) = @_; 309 return $self->{set}->put($GlobalSet); 310 } 311 312 sub deleteGlobalSet($$) { 313 my ($self, $setID) = @_; 314 $self->deleteGlobalProblem($setID, $_) 315 foreach $self->listGlobalProblems($setID); 316 $self->deleteUserSet($_, $setID) 317 foreach $self->listUsers(); 318 return $self->{set}->delete($setID); 319 } 320 321 ################################################################################ 322 # set_user functions 323 ################################################################################ 324 325 sub listSetUsers($$) { 326 my ($self, $setID) = @_; 327 return map { $_->[0] } # extract user_id 328 $self->{set_user}->list(undef, $setID); 329 } 330 331 sub listUserSets($$) { 332 my ($self, $userID) = @_; 333 return map { $_->[1] } # extract set_id 334 $self->{set_user}->list($userID, undef); 335 } 336 337 sub addUserSet($$) { 338 my ($self, $UserSet) = @_; 339 die "addUserSet failed: user ", $UserSet->user_id, " does not exist.\n" 340 unless $self->{user}->exists($UserSet->user_id); 341 die "addUserSet failed: set ", $UserSet->set_id, " does not exist.\n" 342 unless $self->{set}->exists($UserSet->set_id); 343 return $self->{set_user}->add($UserSet); 344 } 345 346 sub getUserSet($$$) { 347 my ($self, $userID, $setID) = @_; 348 return $self->{set_user}->get($userID, $setID); 349 } 350 351 sub putUserSet($$) { 352 my ($self, $UserSet) = @_; 353 return $self->{set_user}->put($UserSet); 354 } 355 356 sub deleteUserSet($$$) { 357 my ($self, $userID, $setID) = @_; 358 $self->deleteUserProblem($userID, $setID, $_) 359 foreach $self->listUserProblems($userID, $setID); 360 return $self->{set_user}->delete($userID, $setID); 361 } 362 363 ################################################################################ 364 # problem functions 365 ################################################################################ 366 367 sub listGlobalProblems($$) { 368 my ($self, $setID) = @_; 369 return map { $_->[1] } 370 #grep { $_->[0] eq $setID } 371 $self->{problem}->list($setID, undef); 372 } 373 374 sub addGlobalProblem($$) { 375 my ($self, $GlobalProblem) = @_; 376 die "addGlobalProblem failed: set ", $GlobalProblem->set_id, " does not exist.\n" 377 unless $self->{set}->exists($GlobalProblem->set_id); 378 return $self->{problem}->add($GlobalProblem); 379 } 380 381 sub getGlobalProblem($$$) { 382 my ($self, $setID, $problemID) = @_; 383 return $self->{problem}->get($setID, $problemID); 384 } 385 386 sub putGlobalProblem($$) { 387 my ($self, $GlobalProblem) = @_; 388 return $self->{problem}->put($GlobalProblem); 389 } 390 391 sub deleteGlobalProblem($$$) { 392 my ($self, $setID, $problemID) = @_; 393 $self->deleteUserProblem($_, $setID, $problemID) 394 foreach $self->listUsers(); 395 return $self->{problem}->delete($setID, $problemID); 396 } 397 398 ################################################################################ 399 # problem_user functions 400 ################################################################################ 401 402 sub listProblemUsers($$$) { 403 my ($self, $setID, $problemID) = @_; 404 return map { $_->[0] } # extract user_id 405 $self->{problem_user}->list(undef, $setID, $problemID); 406 } 407 408 sub listUserProblems($$$) { 409 my ($self, $userID, $setID) = @_; 410 return map { $_->[2] } # extract problem_id 411 $self->{problem_user}->list($userID, $setID, undef); 412 } 413 414 sub addUserProblem($$) { 415 my ($self, $UserProblem) = @_; 416 die "addUserProblem failed: user set ", $UserProblem->set_id, " does not exist.\n" 417 unless $self->{set_user}->exists($UserProblem->user_id, $UserProblem->set_id); 418 die "addUserProblem failed: problem ", $UserProblem->problem_id, " does not exist.\n" 419 unless $self->{problem}->exists($UserProblem->set_id, $UserProblem->problem_id); 420 return $self->{problem_user}->add($UserProblem); 421 } 422 423 sub getUserProblem($$$$) { 424 my ($self, $userID, $setID, $problemID) = @_; 425 return $self->{problem_user}->get($userID, $setID, $problemID); 426 } 427 428 sub putUserProblem($$) { 429 my ($self, $UserProblem) = @_; 430 return $self->{problem_user}->put($UserProblem); 431 } 432 433 sub deleteUserProblem($$$$) { 434 my ($self, $userID, $setID, $problemID) = @_; 435 return $self->{problem_user}->delete($userID, $setID, $problemID); 436 } 437 438 ################################################################################ 439 # set+set_user functions 440 ################################################################################ 441 442 sub getGlobalUserSet($$$) { 443 my ($self, $userID, $setID) = @_; 444 my $UserSet = $self->getUserSet($userID, $setID); 445 return unless $UserSet; 446 my $GlobalSet = $self->getGlobalSet($setID); 447 if ($GlobalSet) { 448 foreach ($UserSet->FIELDS()) { 449 next unless $GlobalSet->can($_); 450 next if $UserSet->$_(); 451 $UserSet->$_($GlobalSet->$_()); 452 } 453 } 454 return $UserSet; 455 } 456 457 ################################################################################ 458 # problem+problem_user functions 459 ################################################################################ 460 461 sub getGlobalUserProblem($$$$) { 462 my ($self, $userID, $setID, $problemID) = @_; 463 my $UserProblem = $self->getUserProblem($userID, $setID, $problemID); 464 return unless $UserProblem; 465 my $GlobalProblem = $self->getGlobalProblem($setID, $problemID); 466 if ($GlobalProblem) { 467 foreach ($UserProblem->FIELDS()) { 468 next unless $GlobalProblem->can($_); 469 next if $UserProblem->$_(); 470 $UserProblem->$_($GlobalProblem->$_()); 471 } 472 } 473 return $UserProblem; 474 } 475 476 ################################################################################ 477 # enforcement 478 ################################################################################ 479 480 ################################################################################ 481 # debugging 482 ################################################################################ 483 484 sub dumpDB($$) { 485 my ($self, $table) = @_; 486 return $self->{$table}->dumpDB(); 487 } 488 489 1;
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |