[system] / branches / bw / webwork2 / lib / WeBWorK / URLPath.pm Repository:
ViewVC logotype

View of /branches/bw/webwork2/lib/WeBWorK/URLPath.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 6934 - (download) (as text) (annotate)
Fri Jul 15 15:04:42 2011 UTC (22 months ago) by benjamin
File size: 29471 byte(s)
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