Parent Directory
|
Revision Log
Removed header info left over from previous localization organization.
1 ################################################################################ 2 # WeBWorK Online Homework Delivery System 3 # Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ 4 # $CVSHeader: webwork2/lib/WeBWorK/URLPath.pm,v 1.36 2008/04/29 19:27:34 sh002i Exp $ 5 # 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 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. 10 # 11 # This program is distributed in the hope that it will be useful, but WITHOUT 12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 # FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the 14 # Artistic License for more details. 15 ################################################################################ 16 17 package WeBWorK::URLPath; 18 19 =head1 NAME 20 21 WeBWorK::URLPath - the WeBWorK virtual URL heirarchy. 22 23 =cut 24 25 use strict; 26 use warnings; 27 use Carp; 28 use WeBWorK::Debug; 29 30 31 { 32 no warnings "redefine"; 33 34 sub debug { 35 my ($label, $indent, @message) = @_; 36 my $header = " "x$indent; 37 $header .= "$label: " if $label ne ""; 38 WeBWorK::Debug::debug($header, @message); 39 } 40 } 41 42 =head1 VIRTUAL HEIRARCHY 43 44 PLEASE FOR THE LOVE OF GOD UPDATE THIS IF YOU CHANGE THE HEIRARCHY BELOW!!! 45 46 root / 47 48 course_admin /admin/ -> logout, options, instructor_tools 49 html2xml /html2xml/ 50 set_list /$courseID/ 51 52 equation_display /$courseID/equation/ 53 feedback /$courseID/feedback/ 54 gateway_quiz /$courseID/quiz_mode/$setID/ 55 proctored_gateway_quiz /$courseID/proctored_quiz_mode/$setID/ 56 proctored_gateway_proctor_login /$courseID/proctored_quiz_mode/$setID/proctor_login/ 57 grades /$courseID/grades/ 58 hardcopy /$courseID/hardcopy/ 59 hardcopy_preselect_set /$courseID/hardcopy/$setID/ 60 logout /$courseID/logout/ 61 options /$courseID/options/ 62 #test /$courseID/test/ 63 #render /$courseID/render/ 64 65 instructor_tools /$courseID/instructor/ 66 67 instructor_user_list /$courseID/instructor/users/ 68 instructor_user_detail /$courseID/instructor/users/$userID/ 69 instructor_sets_assigned_to_user /$courseID/instructor/users/$userID/sets/ 70 71 instructor_set_list /$courseID/instructor/sets/ 72 instructor_set_detail /$courseID/instructor/sets/$setID/ 73 instructor_users_assigned_to_set /$courseID/instructor/sets/$setID/users/ 74 75 instructor_add_users /$courseID/instructor/add_users/ 76 instructor_set_assigner /$courseID/instructor/assigner/ 77 instructor_file_transfer /$courseID/instructor/files/ 78 instructor_file_manager /$courseID/instructor/file_manager/ 79 instructor_set_maker /$courseID/instructor/setmaker/ 80 instructor_config /$courseID/instructor/config/ 81 instructor_compare /$courseID/instructor/compare/ 82 83 instructor_problem_editor /$courseID/instructor/pgProblemEditor/ 84 instructor_problem_editor_withset /$courseID/instructor/pgProblemEditor/$setID/ 85 instructor_problem_editor_withset_withproblem 86 /$courseID/instructor/pgProblemEditor/$setID/$problemID/ 87 88 instructor_scoring /$courseID/instructor/scoring/ 89 instructor_scoring_download /$courseID/instructor/scoringDownload/ 90 instructor_mail_merge /$courseID/instructor/send_mail/ 91 instructor_answer_log /$courseID/instructor/show_answers/ 92 instructor_preflight /$courseID/instructor/preflight/ 93 94 instructor_statistics /$courseID/instructor/stats/ 95 instructor_set_statistics /$courseID/instructor/stats/set/$setID/ 96 instructor_user_statistics /$courseID/instructor/stats/student/$userID/ 97 98 instructor_progress /$courseID/instructor/StudentProgress/ 99 instructor_set_progress /$courseID/instructor/StudentProgress/set/$setID/ 100 instructor_user_progress /$courseID/instructor/StudentProgress/student/$userID/ 101 102 problem_list /$courseID/$setID/ 103 problem_detail /$courseID/$setID/$problemID/ 104 105 =cut 106 107 ################################################################################ 108 # tree of path types 109 ################################################################################ 110 111 our %pathTypes = ( 112 root => { 113 name => 'WeBWorK', 114 parent => '', 115 kids => [ qw/course_admin html2xml set_list / ], 116 match => qr|^/|, 117 capture => [ qw// ], 118 produce => '/', 119 display => 'WeBWorK::ContentGenerator::Home', 120 }, 121 course_admin => { 122 name => 'Course Administration', 123 parent => 'root', 124 kids => [ qw/logout options instructor_tools/ ], 125 match => qr|^(admin)/|, 126 capture => [ qw/courseID/ ], 127 produce => 'admin/', 128 display => 'WeBWorK::ContentGenerator::CourseAdmin', 129 }, 130 131 ################################################################################ 132 html2xml => { 133 name => 'html2xml', 134 parent => 'root', # 'set_list', 135 kids => [ qw// ], 136 match => qr|^html2xml/|, 137 capture => [ qw// ], 138 produce => 'html2xml/', 139 display => 'WeBWorK::ContentGenerator::renderViaXMLRPC', 140 }, 141 142 set_list => { 143 name => '$courseID', 144 parent => 'root', 145 kids => [ qw/equation_display feedback gateway_quiz proctored_gateway_quiz grades hardcopy 146 logout options instructor_tools problem_list 147 / ], 148 match => qr|^([^/]+)/|, 149 capture => [ qw/courseID/ ], 150 produce => '$courseID/', 151 display => 'WeBWorK::ContentGenerator::ProblemSets', 152 }, 153 154 ################################################################################ 155 156 equation_display => { 157 name => 'Equation Display', 158 parent => 'set_list', 159 kids => [ qw// ], 160 match => qr|^equation/|, 161 capture => [ qw// ], 162 produce => 'equation/', 163 display => 'WeBWorK::ContentGenerator::EquationDisplay', 164 }, 165 feedback => { 166 name => 'Feedback', 167 parent => 'set_list', 168 kids => [ qw// ], 169 match => qr|^feedback/|, 170 capture => [ qw// ], 171 produce => 'feedback/', 172 display => 'WeBWorK::ContentGenerator::Feedback', 173 }, 174 gateway_quiz => { 175 name => 'Gateway Quiz $setID', 176 parent => 'set_list', 177 kids => [ qw// ], 178 match => qr|^quiz_mode/([^/]+)/|, 179 capture => [ qw/setID/ ], 180 produce => 'quiz_mode/$setID/', 181 display => 'WeBWorK::ContentGenerator::GatewayQuiz', 182 }, 183 proctored_gateway_quiz => { 184 name => 'Proctored Gateway Quiz $setID', 185 parent => 'set_list', 186 kids => [ qw/proctored_gateway_proctor_login/ ], 187 match => qr|^proctored_quiz_mode/([^/]+)/|, 188 capture => [ qw/setID/ ], 189 produce => 'proctored_quiz_mode/$setID/', 190 display => 'WeBWorK::ContentGenerator::GatewayQuiz', 191 }, 192 proctored_gateway_proctor_login => { 193 name => 'Proctored Gateway Quiz $setID Proctor Login', 194 parent => 'proctored_gateway_quiz', 195 kids => [ qw// ], 196 match => qr|^proctored_quiz_mode/([^/]+)/|, 197 capture => [ qw/setID/ ], 198 produce => 'proctored_quiz_mode/$setID/proctor_login', 199 display => 'WeBWorK::ContentGenerator::LoginProctor', 200 }, 201 grades => { 202 name => 'Grades', 203 parent => 'set_list', 204 kids => [ qw// ], 205 match => qr|^grades/|, 206 capture => [ qw// ], 207 produce => 'grades/', 208 display => 'WeBWorK::ContentGenerator::Grades', 209 }, 210 hardcopy => { 211 name => 'Hardcopy Generator', 212 parent => 'set_list', 213 kids => [ qw/hardcopy_preselect_set/ ], 214 match => qr|^hardcopy/|, 215 capture => [ qw// ], 216 produce => 'hardcopy/', 217 display => 'WeBWorK::ContentGenerator::Hardcopy', 218 }, 219 hardcopy_preselect_set => { 220 name => 'Hardcopy Generator', 221 parent => 'hardcopy', 222 kids => [ qw// ], 223 match => qr|^([^/]+)/|, 224 capture => [ qw/setID/ ], 225 produce => '$setID/', 226 display => 'WeBWorK::ContentGenerator::Hardcopy', 227 }, 228 logout => { 229 name => 'Logout', 230 parent => 'set_list', 231 kids => [ qw// ], 232 match => qr|^logout/|, 233 capture => [ qw// ], 234 produce => 'logout/', 235 display => 'WeBWorK::ContentGenerator::Logout', 236 }, 237 options => { 238 name => 'Password/Email', 239 parent => 'set_list', 240 kids => [ qw// ], 241 match => qr|^options/|, 242 capture => [ qw// ], 243 produce => 'options/', 244 display => 'WeBWorK::ContentGenerator::Options', 245 }, 246 #test => { 247 # name => 'Test', 248 # parent => 'set_list', 249 # kids => [ qw// ], 250 # match => qr|^test/|, 251 # capture => [ qw// ], 252 # produce => 'test/', 253 # display => 'WeBWorK::ContentGenerator::Test', 254 #}, 255 #render => { 256 # name => 'Render', 257 # parent => 'set_list', 258 # kids => [ qw// ], 259 # match => qr|^render/|, 260 # capture => [ qw// ], 261 # produce => 'render/', 262 # display => 'WeBWorK::ContentGenerator::ProblemRenderer', 263 #}, 264 265 ################################################################################ 266 267 instructor_tools => { 268 name => 'Instructor Tools', 269 parent => 'set_list', 270 kids => [ qw/instructor_user_list instructor_set_list instructor_add_users 271 instructor_set_assigner instructor_file_manager 272 instructor_problem_editor instructor_set_maker instructor_compare 273 instructor_config 274 instructor_scoring instructor_scoring_download instructor_mail_merge 275 instructor_answer_log instructor_preflight instructor_statistics 276 instructor_progress 277 / ], 278 match => qr|^instructor/|, 279 capture => [ qw// ], 280 produce => 'instructor/', 281 display => 'WeBWorK::ContentGenerator::Instructor::Index', 282 }, 283 284 ################################################################################ 285 286 instructor_user_list => { 287 name => 'Classlist Editor', 288 parent => 'instructor_tools', 289 kids => [ qw/instructor_user_detail/ ], 290 match => qr|^users/|, 291 capture => [ qw// ], 292 produce => 'users/', 293 display => 'WeBWorK::ContentGenerator::Instructor::UserList', 294 }, 295 instructor_user_detail => { 296 name => 'Sets assigned to $userID', 297 parent => 'instructor_user_list', 298 kids => [ qw/instructor_sets_assigned_to_user/ ], 299 match => qr|^([^/]+)/|, 300 capture => [ qw/userID/ ], 301 produce => '$userID/', 302 display => 'WeBWorK::ContentGenerator::Instructor::UserDetail', 303 }, 304 instructor_sets_assigned_to_user => { 305 name => 'Sets Assigned to User', 306 parent => 'instructor_user_detail', 307 kids => [ qw// ], 308 match => qr|^sets/|, 309 capture => [ qw// ], 310 produce => 'sets/', 311 display => 'WeBWorK::ContentGenerator::Instructor::SetsAssignedToUser', 312 }, 313 314 ################################################################################ 315 316 instructor_set_list => { 317 name => 'Hmwk Sets Editor', 318 parent => 'instructor_tools', 319 kids => [ qw/instructor_set_detail/ ], 320 match => qr|^sets/|, 321 capture => [ qw// ], 322 produce => 'sets/', 323 display => 'WeBWorK::ContentGenerator::Instructor::ProblemSetList', 324 }, 325 instructor_set_detail => { 326 name => 'Set Detail for set $setID', 327 parent => 'instructor_set_list', 328 kids => [ qw/instructor_users_assigned_to_set/ ], 329 match => qr|^([^/]+)/|, 330 capture => [ qw/setID/ ], 331 produce => '$setID/', 332 display => 'WeBWorK::ContentGenerator::Instructor::ProblemSetDetail', 333 }, 334 instructor_users_assigned_to_set => { 335 name => 'Users Assigned to Set $setID', 336 parent => 'instructor_set_detail', 337 kids => [ qw// ], 338 match => qr|^users/|, 339 capture => [ qw// ], 340 produce => 'users/', 341 display => 'WeBWorK::ContentGenerator::Instructor::UsersAssignedToSet', 342 }, 343 344 ################################################################################ 345 346 instructor_add_users => { 347 name => 'Add Users', 348 parent => 'instructor_tools', 349 kids => [ qw// ], 350 match => qr|^add_users/|, 351 capture => [ qw// ], 352 produce => 'add_users/', 353 display => 'WeBWorK::ContentGenerator::Instructor::AddUsers', 354 }, 355 instructor_set_assigner => { 356 name => 'Set Assigner', 357 parent => 'instructor_tools', 358 kids => [ qw// ], 359 match => qr|^assigner/|, 360 capture => [ qw// ], 361 produce => 'assigner/', 362 display => 'WeBWorK::ContentGenerator::Instructor::Assigner', 363 }, 364 instructor_config => { 365 name => 'Course Configuration', 366 parent => 'instructor_tools', 367 kids => [ qw// ], 368 match => qr|^config/|, 369 capture => [ qw// ], 370 produce => 'config/', 371 display => 'WeBWorK::ContentGenerator::Instructor::Config', 372 }, 373 instructor_compare => { 374 name => 'File Compare', 375 parent => 'instructor_tools', 376 kids => [ qw// ], 377 match => qr|^compare/|, 378 capture => [ qw// ], 379 produce => 'comp/', 380 display => 'WeBWorK::ContentGenerator::Instructor::Compare', 381 }, 382 instructor_set_maker => { 383 name => 'Library Browser', 384 parent => 'instructor_tools', 385 kids => [ qw// ], 386 match => qr|^setmaker/|, 387 capture => [ qw// ], 388 produce => 'setmaker/', 389 display => 'WeBWorK::ContentGenerator::Instructor::SetMaker', 390 }, 391 instructor_file_manager => { 392 name => 'File Manager', 393 parent => 'instructor_tools', 394 kids => [ qw// ], 395 match => qr|^file_manager/|, 396 capture => [ qw// ], 397 produce => 'file_manager/', 398 display => 'WeBWorK::ContentGenerator::Instructor::FileManager', 399 }, 400 instructor_problem_editor => { 401 name => 'Problem Editor', 402 parent => 'instructor_tools', 403 kids => [ qw/instructor_problem_editor_withset/ ], 404 match => qr|^pgProblemEditor/|, 405 capture => [ qw// ], 406 produce => 'pgProblemEditor/', 407 display => 'WeBWorK::ContentGenerator::Instructor::PGProblemEditor', 408 }, 409 instructor_problem_editor_withset => { 410 name => '$setID', 411 parent => 'instructor_problem_editor', 412 kids => [ qw/instructor_problem_editor_withset_withproblem/ ], 413 match => qr|^([^/]+)/|, 414 capture => [ qw/setID/ ], 415 produce => '$setID/', 416 display => undef, 417 }, 418 instructor_problem_editor_withset_withproblem => { 419 name => '$problemID', 420 parent => 'instructor_problem_editor_withset', 421 kids => [ qw// ], 422 match => qr|^([^/]+)/|, 423 capture => [ qw/problemID/ ], 424 produce => '$problemID/', 425 display => 'WeBWorK::ContentGenerator::Instructor::PGProblemEditor', 426 }, 427 instructor_scoring => { 428 name => 'Scoring Tools', 429 parent => 'instructor_tools', 430 kids => [ qw// ], 431 match => qr|^scoring/|, 432 capture => [ qw// ], 433 produce => 'scoring/', 434 display => 'WeBWorK::ContentGenerator::Instructor::Scoring', 435 }, 436 instructor_scoring_download => { 437 name => 'Scoring Download', 438 parent => 'instructor_tools', 439 kids => [ qw// ], 440 match => qr|^scoringDownload/|, 441 capture => [ qw// ], 442 produce => 'scoringDownload/', 443 display => 'WeBWorK::ContentGenerator::Instructor::ScoringDownload', 444 }, 445 instructor_mail_merge => { 446 name => 'Email', 447 parent => 'instructor_tools', 448 kids => [ qw// ], 449 match => qr|^send_mail/|, 450 capture => [ qw// ], 451 produce => 'send_mail/', 452 display => 'WeBWorK::ContentGenerator::Instructor::SendMail', 453 }, 454 instructor_answer_log => { 455 name => 'Answer Log', 456 parent => 'instructor_tools', 457 kids => [ qw// ], 458 match => qr|^show_answers/|, 459 capture => [ qw// ], 460 produce => 'show_answers/', 461 display => 'WeBWorK::ContentGenerator::Instructor::ShowAnswers', 462 }, 463 instructor_preflight => { 464 name => 'Preflight Log', 465 parent => 'instructor_tools', 466 kids => [ qw// ], 467 match => qr|^preflight/|, 468 capture => [ qw// ], 469 produce => 'preflight/', 470 display => 'WeBWorK::ContentGenerator::Instructor::Preflight', 471 }, 472 473 ################################################################################ 474 475 instructor_statistics => { 476 name => 'Statistics', 477 parent => 'instructor_tools', 478 kids => [ qw/instructor_set_statistics instructor_user_statistics/ ], 479 match => qr|^stats/|, 480 capture => [ qw// ], 481 produce => 'stats/', 482 display => 'WeBWorK::ContentGenerator::Instructor::Stats', 483 }, 484 instructor_set_statistics => { 485 name => 'Statistics', 486 parent => 'instructor_statistics', 487 kids => [ qw// ], 488 match => qr|^(set)/([^/]+)/|, 489 capture => [ qw/statType setID/ ], 490 produce => 'set/$setID/', 491 display => 'WeBWorK::ContentGenerator::Instructor::Stats', 492 }, 493 instructor_user_statistics => { 494 name => 'Statistics', 495 parent => 'instructor_statistics', 496 kids => [ qw// ], 497 match => qr|^(student)/([^/]+)/|, 498 capture => [ qw/statType userID/ ], 499 produce => 'student/$userID/', 500 display => 'WeBWorK::ContentGenerator::Instructor::Stats', 501 }, 502 503 ################################################################################ 504 505 instructor_progress => { 506 name => 'Student Progress', 507 parent => 'instructor_tools', 508 kids => [ qw/instructor_set_progress instructor_user_progress/ ], 509 match => qr|^progress/|, 510 capture => [ qw// ], 511 produce => 'progress/', 512 display => 'WeBWorK::ContentGenerator::Instructor::StudentProgress', 513 }, 514 instructor_set_progress => { 515 name => 'Student Progress', 516 parent => 'instructor_progress', 517 kids => [ qw// ], 518 match => qr|^(set)/([^/]+)/|, 519 capture => [ qw/statType setID/ ], 520 produce => 'set/$setID/', 521 display => 'WeBWorK::ContentGenerator::Instructor::StudentProgress', 522 }, 523 instructor_user_progress => { 524 name => 'Student Progress', 525 parent => 'instructor_progress', 526 kids => [ qw// ], 527 match => qr|^(student)/([^/]+)/|, 528 capture => [ qw/statType userID/ ], 529 produce => 'student/$userID/', 530 display => 'WeBWorK::ContentGenerator::Instructor::StudentProgress', 531 }, 532 533 ################################################################################ 534 535 problem_list => { 536 name => '$setID', 537 parent => 'set_list', 538 kids => [ qw/problem_detail/ ], 539 match => qr|^([^/]+)/|, 540 capture => [ qw/setID/ ], 541 produce => '$setID/', 542 display => 'WeBWorK::ContentGenerator::ProblemSet', 543 }, 544 problem_detail => { 545 name => '$problemID', 546 parent => 'problem_list', 547 kids => [ qw// ], 548 match => qr|^([^/]+)/|, 549 capture => [ qw/problemID/ ], 550 produce => '$problemID/', 551 display => 'WeBWorK::ContentGenerator::Problem', 552 }, 553 554 ); 555 556 =for comment 557 558 a handy template: 559 560 id => { 561 name => '', 562 parent => '', 563 kids => [ qw// ], 564 match => qr|^/|, 565 capture => [ qw// ], 566 produce => '', 567 display => '', 568 }, 569 570 =cut 571 572 ################################################################################ 573 574 =head1 CONSTRUCTORS 575 576 =over 577 578 =item new(%fields) 579 580 Creates a new WeBWorK::URLPath. %fields may contain the following: 581 582 type => the internal path type associated with this 583 args => a reference to a hash associating path arguments with values 584 585 This constructor is used internally. Refer to newFromPath() and newFromModule() 586 for more useful constructors. 587 588 =cut 589 590 sub new { 591 my ($invocant, %fields) = @_; 592 my $class = ref $invocant || $invocant; 593 my $self = { 594 type => undef, 595 r => undef, # will point to the parent request object (for access to the $ce) 596 args => {}, 597 %fields, 598 }; 599 return bless $self, $class; 600 } 601 602 =item newFromPath($path) 603 604 Creates a new WeBWorK::URLPath by parsing the path given in $path. It the path 605 is invalid, an exception is thrown. 606 607 =cut 608 609 sub newFromPath { 610 my ($invocant, $path, $r) = @_; 611 612 my ($type, %args) = getPathType($path); 613 croak "no type matches path $path" unless $type; 614 croak "URLPath requires a request object parent as second element" unless (ref($r) =~/WeBWorK::Request/); 615 616 return $invocant->new( 617 type => $type, 618 r => $r, # will point to the parent request object (for access to the $ce) 619 args => \%args, 620 ); 621 } 622 623 =item newFromModule($module, $r, %args) 624 625 Creates a new WeBWorK::URLPath by finding a path type which matches the module 626 and path arguments given. If no type matches, an exception is thrown. 627 628 =cut 629 630 sub newFromModule { 631 my ($invocant, $module, $r, %args) = @_; 632 633 my $type = getModuleType($module, keys %args); 634 croak "URLPath requires a request object parent as second element" unless (ref($r) =~/WeBWorK::Request/); 635 croak "no type matches module $module with args", map { " $_=>$args{$_}" } keys %args unless $type; 636 637 return $invocant->new( 638 type => $type, 639 r => $r, 640 args => \%args 641 ); 642 } 643 644 =back 645 646 =cut 647 648 ################################################################################ 649 650 =head1 METHODS 651 652 =head2 Methods that return information from the object itself 653 654 =over 655 656 =item type() 657 658 Returns the path type of the WeBWorK::URLPath. 659 660 =cut 661 662 sub type { 663 my ($self) = @_; 664 my $type = $self->{type}; 665 666 return $type; 667 } 668 669 =item args() 670 671 Returns a hash of arguments derived from the WeBWorK::URLPath. 672 673 =cut 674 675 sub args { 676 my ($self) = @_; 677 my %args = %{ $self->{args} }; 678 679 return %args; 680 } 681 682 =item arg($name) 683 684 Returns the named argument, as derived from the WeBWorK::URLPath. 685 686 =cut 687 688 sub arg { 689 my ($self, $name) = @_; 690 my %args = %{ $self->{args} }; 691 692 return $args{$name}; 693 } 694 695 =back 696 697 =cut 698 699 # ------------------------------------------------------------------------------ 700 701 =head2 Methods that return information from path node associated with the object 702 703 =over 704 705 =item name() 706 707 Returns the human-readable name of this WeBWorK::URLPath. 708 709 =cut 710 711 sub name { 712 my ($self) = @_; 713 my $type = $self->{type}; 714 my %args = $self->args; 715 716 my $name = $pathTypes{$type}->{name}; 717 $name = $self->{r}->maketext($name); # translate the display name 718 $name = interpolate($name, %args); 719 720 return $name; 721 } 722 723 =item module() 724 725 Returns the name of the module that will handle this WeBWorK::URLPath. 726 727 =cut 728 729 sub module { 730 my ($self) = @_; 731 my $type = $self->{type}; 732 733 return $pathTypes{$type}->{display}; 734 } 735 736 =back 737 738 =cut 739 740 # ------------------------------------------------------------------------------ 741 742 =head2 Methods that search the virtual heirarchy 743 744 =over 745 746 =item parent() 747 748 Returns a new WeBWorK::URLPath representing the parent of the current URLPath. 749 Returns an undefined value if the URLPath has no parent. 750 751 =cut 752 753 sub parent { 754 my ($self) = @_; 755 my $type = $self->{type}; 756 757 my $newType = $pathTypes{$self->{type}}->{parent}; 758 return undef unless $newType; 759 760 # remove any arguments added by the current node (and therefore not needed by the parent) 761 my @currArgs = @{ $pathTypes{$type}->{capture} }; 762 my %newArgs = %{ $self->{args} }; 763 delete @newArgs{@currArgs} if @currArgs; 764 # use the same request object "parent" for parent URLPath as for the child 765 return $self->new(type => $newType, r => $self->{r}, args => \%newArgs); 766 } 767 768 =item child($module, %newArgs) 769 770 Returns a new WeBWorK::URLPath representing the child of the current URLPath 771 whose module is C<$module>. If no child matches, an undefined value is returned. 772 Pass additional arguments needed by the child in C<%newArgs>. 773 774 =cut 775 776 sub child { 777 my ($self, $module, %newArgs) = @_; 778 my $type = $self->{type}; 779 780 my @kids = @{ $pathTypes{$type}->{kids} }; 781 my $newType; 782 foreach my $kid (@kids) { 783 if ($pathTypes{$kid}->{module} eq $module) { 784 $newType = $kid; 785 last; 786 } 787 } 788 789 if ($newType) { 790 return $self->new(type => $newType, args => \%newArgs); 791 } else { 792 return undef; 793 } 794 } 795 796 =item path() 797 798 Reconstructs the path string from a WeBWorK::URLPath. 799 800 =cut 801 802 sub path { 803 my ($self) = @_; 804 my $type = $self->type; 805 my %args = %{ $self->{args} }; 806 807 my $path = buildPathFromType($type); 808 $path = interpolate($path, %args); 809 810 return $path; 811 } 812 813 =back 814 815 =cut 816 817 818 819 ################################################################################ 820 821 =head1 UTILITY FUNCTIONS 822 823 =over 824 825 =item all_modules() 826 827 Return a list of the display modules associated with all possible path types. 828 829 =cut 830 831 sub all_modules { 832 my @modules = grep { defined } map { $pathTypes{$_}{display} } keys %pathTypes; 833 my %modules; @modules{@modules} = (); # remove duplicates 834 return keys %modules; 835 } 836 837 =item interpolate($string, %symbols) 838 839 Replaces simple scalars (\$\w+) in $string with values in %symbols. If a scalar 840 does not exist in %symbols, it is left alone. 841 842 =cut 843 844 sub interpolate { 845 my ($string, %symbols) = @_; 846 847 $string =~ s/\$(\w+)/exists $symbols{$1} ? $symbols{$1} : "\$$1"/eg; 848 849 return $string; 850 } 851 852 =back 853 854 =cut 855 856 # ------------------------------------------------------------------------------ 857 858 =over 859 860 =item getPathType($path) 861 862 Parse the string $path, determining the path type. Returns ($type, %args), where 863 $type is the type of the path and %args contains any extracted path arguments. 864 If conversion fails, a false value is returned. 865 866 =cut 867 868 sub getPathType($) { 869 my ($path) = @_; 870 871 my %args; 872 my $context = visitPathTypeNode("root", $path, \%args, 0); 873 874 return $context, %args; 875 } 876 877 =item getModuleType($module, @args) 878 879 Returns the path type matching the given module and argument names, or a false 880 value if no type matches. 881 882 =cut 883 884 sub getModuleType { 885 my ($module, @args) = @_; 886 @args = sort @args; 887 my %args; 888 @args{@args} = (); 889 890 NODE: foreach my $nodeID (keys %pathTypes) { 891 my $node = $pathTypes{$nodeID}; 892 893 # module name matches? 894 next NODE unless defined $node->{display} and $node->{display} eq $module; 895 896 # collect all captures from here to root 897 my @captures; 898 my $tmpNodeID = $nodeID; 899 while ($tmpNodeID) { 900 my $tmpNode = $pathTypes{$tmpNodeID}; 901 push @captures, @{ $tmpNode->{capture} }; 902 $tmpNodeID = $tmpNode->{parent}; 903 } 904 905 # same number of captures? 906 next NODE unless @captures == @args; 907 908 # same captures? 909 @captures = sort @captures; 910 for (my $i = 0; $i < @args; $i++) { 911 next NODE unless $args[$i] eq $captures[$i]; 912 } 913 914 # if we got here, this node matches 915 return $nodeID; 916 } 917 918 return 0; # no node matches 919 } 920 921 =item buildPathFromType($type) 922 923 Returns a string path for the given path type. Since arguments are not supplied, 924 the string may contain scalar variables ripe for interpolation. 925 926 =cut 927 928 sub buildPathFromType($) { 929 my ($type) = @_; 930 931 my $path = ""; 932 933 while ($type) { 934 $path = $pathTypes{$type}->{produce} . $path; 935 $type = $pathTypes{$type}->{parent}; 936 }; 937 938 return $path; 939 } 940 941 =item visitPathTypeNode($nodeID, $path, $argsRef, $indent) 942 943 Internal search function. See getPathType(). 944 945 Returns the nodeID of the node that consumed the final characters in $path, or 946 the following failure conditions: 947 948 Returns 0 if $nodeID doesn't match $path. 949 950 Returns -1 if $nodeID matched $path, but no children of $nodeID consumed the 951 remaining path. In this case, the stack is unwound immediately. 952 953 =cut 954 955 sub visitPathTypeNode($$$$); 956 957 sub visitPathTypeNode($$$$) { 958 my ($nodeID, $path, $argsRef, $indent) = @_; 959 debug("visitPathTypeNode", $indent, "visiting node $nodeID with path $path"); 960 961 unless (exists $pathTypes{$nodeID}) { 962 debug("visitPathTypeNode", $indent, "node $nodeID doesn't exist in node list: failed"); 963 die "node $nodeID doesn't exist in node list: failed"; 964 } 965 966 my %node = %{ $pathTypes{$nodeID} }; 967 my $match = $node{match}; 968 my @capture_names = @{ $node{capture} }; 969 970 # attempt to match $path against $match. 971 debug("visitPathTypeNode", $indent, "trying to match $match: "); 972 if ($path =~ s/($match)//) { 973 # it matches! store captured strings in $argsRef and remove the matched 974 # characters from $path. waste a lot of lines on sanity checking... ;) 975 debug("", 0, "success!"); 976 my @capture_values = $1 =~ m/$match/; 977 if (@capture_names) { 978 my $nexpected = @capture_names; 979 my $ncaptured = @capture_values; 980 my $max = $nexpected > $ncaptured ? $nexpected : $ncaptured; 981 warn "captured $ncaptured arguments, expected $nexpected." unless $ncaptured == $nexpected; 982 for (my $i = 0; $i < $max; $i++) { 983 my $name = $capture_names[$i]; 984 my $value = $capture_values[$i]; 985 if ($i > $nexpected) { 986 warn "captured an unexpected argument: $value -- ignoring it."; 987 next; 988 } 989 if ($i > $ncaptured) { 990 warn "expected an uncaptured argument named: $name -- ignoring it."; 991 next; 992 } 993 if (exists $argsRef->{$name}) { 994 my $old = $argsRef->{$name}; 995 warn "encountered argument $name again, old value: $old new value: $value -- replacing."; 996 } 997 debug("visitPathTypeNode", $indent, "setting argument $name => $value."); 998 $argsRef->{$name} = $value; 999 } 1000 } 1001 } else { 1002 # it doesn't match. bail out now with return value 0 1003 debug("", 0, "failed."); 1004 return 0; 1005 } 1006 1007 ##### if we're here we matched ##### 1008 1009 # if there's no more path left, then this node is the one! return $nodeID 1010 if ($path eq "") { 1011 debug("visitPathTypeNode", $indent, "no path left, type is $nodeID"); 1012 return $nodeID; 1013 } 1014 1015 # otherwise, we have to send the remaining path to the node's children 1016 debug("visitPathTypeNode", $indent, "but path remains: $path"); 1017 my @kids = @{ $node{kids} }; 1018 if (@kids) { 1019 foreach my $kid (@kids) { 1020 debug("visitPathTypeNode", $indent, "trying child $kid:"); 1021 my $result = visitPathTypeNode($kid, $path, $argsRef, $indent+1); 1022 # we return in two situations: 1023 # if $result is -1, then the kid matched but couldn't consume the rest of the path 1024 # if $result is the ID of a node, then the kid matched and consumed the rest of the path 1025 # these are all true values (assuming that "0" isn't a valid node ID), so we say: 1026 return $result if $result; 1027 } 1028 debug("visitPathTypeNode", $indent, "no children claimed the remaining path: failed."); 1029 } else { 1030 debug("visitPathTypeNode", $indent, "no children to claim the remaining path: failed."); 1031 } 1032 1033 # in both of the above cases, we matched but couldn't provide children that 1034 # would consume the rest of the path. so we return -1, causing the whole 1035 # stack to unwind. WHEEEEEEE! 1036 return -1; 1037 } 1038 1039 =back 1040 1041 =cut 1042 1043 1;
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |