version 1.138, 2011/07/26 10:40:23
|
version 1.139, 2011/08/04 10:57:26
|
Line 70 sub versionerror {
|
Line 70 sub versionerror {
|
$uri,$usedversion,$unusedversion).'<br />'; |
$uri,$usedversion,$unusedversion).'<br />'; |
} |
} |
|
|
|
# Removes the version number from a URI and returns the resulting |
|
# URI (e.g. mumbly.version.stuff => mumbly.stuff). |
|
# |
|
# If the URI has not been seen with a versio before the |
|
# hash{'version_'.resultingURI} is set to the version number. |
|
# If the URI has been seen and the version does not match and error |
|
# is added to the error string. |
|
# |
|
# Parameters: |
|
# URI potentially with a version. |
|
# Returns: |
|
# URI with the version cut out. |
|
# See above for side effects. |
|
# |
|
|
sub versiontrack { |
sub versiontrack { |
my $uri=shift; |
my $uri=shift; |
if ($uri=~/\.(\d+)\.\w+$/) { |
if ($uri=~/\.(\d+)\.\w+$/) { |
Line 218 sub loadmap {
|
Line 233 sub loadmap {
|
if ($token->[1] eq 'resource') { |
if ($token->[1] eq 'resource') { |
my $resource_id = &parse_resource($token,$lpc,$ispage,$uri); |
my $resource_id = &parse_resource($token,$lpc,$ispage,$uri); |
if (defined $resource_id) { |
if (defined $resource_id) { |
push(@map_ids, $resource_id); |
push(@map_ids, $resource_id); |
} |
} |
|
|
# Link |
# Link |
Line 226 sub loadmap {
|
Line 241 sub loadmap {
|
} elsif ($token->[1] eq 'link' && !$randomize) { |
} elsif ($token->[1] eq 'link' && !$randomize) { |
&make_link(++$linkpc,$lpc,$token->[2]->{'to'}, |
&make_link(++$linkpc,$lpc,$token->[2]->{'to'}, |
$token->[2]->{'from'}, |
$token->[2]->{'from'}, |
$token->[2]->{'condition'}); |
$token->[2]->{'condition'}); # note ..condition may be undefined. |
|
|
# condition |
# condition |
|
|
Line 241 sub loadmap {
|
Line 256 sub loadmap {
|
if ($randomize) { |
if ($randomize) { |
if (!$env{'request.role.adv'}) { |
if (!$env{'request.role.adv'}) { |
my $seed; |
my $seed; |
|
|
|
# In the advanced role, the map's random seed |
|
# parameter is used as the basis for computing the |
|
# seed ... if it has been specified: |
|
|
if (defined($randompickseed{$parent_rid})) { |
if (defined($randompickseed{$parent_rid})) { |
$seed = $randompickseed{$parent_rid}; |
$seed = $randompickseed{$parent_rid}; |
} else { |
} else { |
|
|
|
# Otherwise the parent's fully encoded symb is used. |
|
|
my ($mapid,$resid)=split(/\./,$parent_rid); |
my ($mapid,$resid)=split(/\./,$parent_rid); |
my $symb= |
my $symb= |
&Apache::lonnet::encode_symb($hash{'map_id_'.$mapid}, |
&Apache::lonnet::encode_symb($hash{'map_id_'.$mapid}, |
Line 252 sub loadmap {
|
Line 275 sub loadmap {
|
$seed = $symb; |
$seed = $symb; |
} |
} |
|
|
# Here for sure we need to pass along the username/domain |
# TODO: Here for sure we need to pass along the username/domain |
# so that we can impersonate users in lonprintout e.g. |
# so that we can impersonate users in lonprintout e.g. |
|
|
my $rndseed=&Apache::lonnet::rndseed($seed); |
my $rndseed=&Apache::lonnet::rndseed($seed); |
&Apache::lonnet::setup_random_from_rndseed($rndseed); |
&Apache::lonnet::setup_random_from_rndseed($rndseed); |
@map_ids=&Math::Random::random_permutation(@map_ids); |
@map_ids=&math::Random::random_permutation(@map_ids); # randomorder. |
} |
} |
|
|
|
|
my $from = shift(@map_ids); |
my $from = shift(@map_ids); |
my $from_rid = $lpc.'.'.$from; |
my $from_rid = $lpc.'.'.$from; |
$hash{'map_start_'.$uri} = $from_rid; |
$hash{'map_start_'.$uri} = $from_rid; |
$hash{'type_'.$from_rid}='start'; |
$hash{'type_'.$from_rid}='start'; |
|
|
|
# Create links to reflect this random ordering. |
|
# BUG? If there are conditions, this invalidates them? Then again |
|
# with randompick there's no gaurentee the resources required for the |
|
# conditinos to work will be selected into the map. |
|
# so randompick is inconsistent with a map that has conditions? |
|
|
while (my $to = shift(@map_ids)) { |
while (my $to = shift(@map_ids)) { |
&make_link(++$linkpc,$lpc,$to,$from); |
&make_link(++$linkpc,$lpc,$to,$from); |
my $to_rid = $lpc.'.'.$to; |
my $to_rid = $lpc.'.'.$to; |
Line 278 sub loadmap {
|
Line 309 sub loadmap {
|
|
|
$parser = HTML::TokeParser->new(\$instr); |
$parser = HTML::TokeParser->new(\$instr); |
$parser->attr_encoded(1); |
$parser->attr_encoded(1); |
|
|
# last parse out the mapalias params so as to ignore anything |
# last parse out the mapalias params so as to ignore anything |
# refering to non-existant resources |
# refering to non-existant resources |
|
|
while (my $token = $parser->get_token) { |
while (my $token = $parser->get_token) { |
next if ($token->[0] ne 'S'); |
next if ($token->[0] ne 'S'); |
if ($token->[1] eq 'param') { |
if ($token->[1] eq 'param') { |
Line 305 sub loadmap {
|
Line 338 sub loadmap {
|
# $ispage - True if this resource is encapsulated in a .page (assembled resourcde). |
# $ispage - True if this resource is encapsulated in a .page (assembled resourcde). |
# $uri - URI of the enclosing resource. |
# $uri - URI of the enclosing resource. |
# Returns: |
# Returns: |
|
# Value of the id attribute of the tag. |
# |
# |
# Note: |
# Note: |
# The token is an array that contains the following elements: |
# The token is an array that contains the following elements: |
Line 325 sub loadmap {
|
Line 359 sub loadmap {
|
sub parse_resource { |
sub parse_resource { |
my ($token,$lpc,$ispage,$uri) = @_; |
my ($token,$lpc,$ispage,$uri) = @_; |
|
|
# I refuse to coutenance code like this that has |
# I refuse to countenance code like this that has |
# such a dirty side effect (and forcing this sub to be called within a loop). |
# such a dirty side effect (and forcing this sub to be called within a loop). |
# |
# |
# if ($token->[2]->{'type'} eq 'zombie') { next; } |
# if ($token->[2]->{'type'} eq 'zombie') { next; } |
|
# |
|
# The original code both returns _and_ skips to the next pass of the >caller's< |
|
# loop, that's just dirty. |
|
# |
|
|
# Zombie resources don't produce anything useful. |
# Zombie resources don't produce anything useful. |
|
|
Line 336 sub parse_resource {
|
Line 374 sub parse_resource {
|
return undef; |
return undef; |
} |
} |
|
|
my $rid=$lpc.'.'.$token->[2]->{'id'}; |
my $rid=$lpc.'.'.$token->[2]->{'id'}; # Resource id in hash is levelcounter.id-in-xml. |
|
|
|
# Save the hash element type and title: |
|
|
$hash{'kind_'.$rid}='res'; |
$hash{'kind_'.$rid}='res'; |
$hash{'title_'.$rid}=$token->[2]->{'title'}; |
$hash{'title_'.$rid}=$token->[2]->{'title'}; |
|
|
|
# Get the version free URI for the resource. |
|
# If a 'version' attribute was supplied, and this resource's version |
|
# information has not yet been stored, store it. |
|
# |
|
|
my $turi=&versiontrack($token->[2]->{'src'}); |
my $turi=&versiontrack($token->[2]->{'src'}); |
if ($token->[2]->{'version'}) { |
if ($token->[2]->{'version'}) { |
unless ($hash{'version_'.$turi}) { |
unless ($hash{'version_'.$turi}) { |
$hash{'version_'.$turi}=$1; |
$hash{'version_'.$turi}=$1; |
} |
} |
} |
} |
|
# Pull out the title and do entity substitution on &colon |
|
# Q: Why no other entity substitutions? |
|
|
my $title=$token->[2]->{'title'}; |
my $title=$token->[2]->{'title'}; |
$title=~s/\&colon\;/\:/gs; |
$title=~s/\&colon\;/\:/gs; |
# my $symb=&Apache::lonnet::encode_symb($uri, |
|
# $token->[2]->{'id'}, |
|
# $turi); |
|
# &Apache::lonnet::do_cache_new('title',$symb,$title); |
# I think the point of all this code is to construct a final |
|
# URI that apache and its rewrite rules can use to |
|
# fetch the resource. Thi s sonly necessary if the resource |
|
# is not a page. If the resource is a page then it must be |
|
# assembled (at fetch time?). |
|
|
unless ($ispage) { |
unless ($ispage) { |
$turi=~/\.(\w+)$/; |
$turi=~/\.(\w+)$/; |
my $embstyle=&Apache::loncommon::fileembstyle($1); |
my $embstyle=&Apache::loncommon::fileembstyle($1); |
Line 378 sub parse_resource {
|
Line 432 sub parse_resource {
|
} |
} |
} |
} |
} |
} |
# Store reverse lookup, remove query string |
# Store reverse lookup, remove query string resource 'ids'_uri => resource id. |
|
# If the URI appears more than one time in the sequence, it's resourcde |
|
# id's are constructed as a comma spearated list. |
|
|
my $idsuri=$turi; |
my $idsuri=$turi; |
$idsuri=~s/\?.+$//; |
$idsuri=~s/\?.+$//; |
if (defined($hash{'ids_'.$idsuri})) { |
if (defined($hash{'ids_'.$idsuri})) { |
Line 387 sub parse_resource {
|
Line 444 sub parse_resource {
|
$hash{'ids_'.$idsuri}=''.$rid; |
$hash{'ids_'.$idsuri}=''.$rid; |
} |
} |
|
|
|
|
|
|
if ($turi=~/\/(syllabus|aboutme|navmaps|smppg|bulletinboard|viewclasslist)$/) { |
if ($turi=~/\/(syllabus|aboutme|navmaps|smppg|bulletinboard|viewclasslist)$/) { |
$turi.='?register=1'; |
$turi.='?register=1'; |
} |
} |
|
|
|
|
|
# resource id lookup: 'src'_resourc-di => URI decorated with a query |
|
# parameter as above if necessary due to the resource type. |
|
|
$hash{'src_'.$rid}=$turi; |
$hash{'src_'.$rid}=$turi; |
|
|
|
# Mark the external-ness of the resource: |
|
|
if ($token->[2]->{'external'} eq 'true') { |
if ($token->[2]->{'external'} eq 'true') { |
$hash{'ext_'.$rid}='true:'; |
$hash{'ext_'.$rid}='true:'; |
} else { |
} else { |
$hash{'ext_'.$rid}='false:'; |
$hash{'ext_'.$rid}='false:'; |
} |
} |
|
|
|
# If the resource is a start/finish resource set those |
|
# entries in the has so that navigation knows where everything starts. |
|
# TODO? If there is a malformed sequence that has no start or no finish |
|
# resource, should this be detected and errors thrown? How would such a |
|
# resource come into being other than being manually constructed by a person |
|
# and then uploaded? Could that happen if an author decided a sequence was almost |
|
# right edited it by hand and then reuploaded it to 'fix it' but accidently cut the |
|
# start or finish resources? |
|
# |
|
# All resourcess also get a type_id => (start | finish | normal) hash entr. |
|
# |
if ($token->[2]->{'type'}) { |
if ($token->[2]->{'type'}) { |
$hash{'type_'.$rid}=$token->[2]->{'type'}; |
$hash{'type_'.$rid}=$token->[2]->{'type'}; |
if ($token->[2]->{'type'} eq 'start') { |
if ($token->[2]->{'type'} eq 'start') { |
Line 409 sub parse_resource {
|
Line 486 sub parse_resource {
|
} else { |
} else { |
$hash{'type_'.$rid}='normal'; |
$hash{'type_'.$rid}='normal'; |
} |
} |
|
|
|
# Sequences end pages are constructed entities. They require that the |
|
# map that defines _them_ be loaded as well into the hash...with this resourcde |
|
# as the base of the nesting. |
|
# Resources like that are also marked with is_map_id => 1 entries. |
|
# |
|
|
if (($turi=~/\.sequence$/) || |
if (($turi=~/\.sequence$/) || |
($turi=~/\.page$/)) { |
($turi=~/\.page$/)) { |
Line 418 sub parse_resource {
|
Line 501 sub parse_resource {
|
return $token->[2]->{'id'}; |
return $token->[2]->{'id'}; |
} |
} |
|
|
|
#-------------------------------------------------------------------- link |
|
# Links define how you are allowed to move from one resource to another. |
|
# They are the transition edges in the directed graph that a map is. |
|
# This sub takes informatino from a <link> tag and constructs the |
|
# navigation bits and pieces of a map. There is no requirement that the |
|
# resources that are linke are already defined, however clearly the map is |
|
# badly broken if they are not _eventually_ defined. |
|
# |
|
# Note that links can be unconditional or conditional. |
|
# |
|
# Parameters: |
|
# linkpc - The link counter for this level of map nesting (this is |
|
# reset to zero by loadmap prior to starting to process |
|
# links for map). |
|
# lpc - The map level ocounter (how deeply nested this map is in |
|
# the hierarchy of maps that are recursively read in. |
|
# to - resource id (within the XML) of the target of the edge. |
|
# from - resource id (within the XML) of the source of the edge. |
|
# condition- id of condition associated with the edge (also within the XML). |
|
# |
|
|
sub make_link { |
sub make_link { |
my ($linkpc,$lpc,$to,$from,$condition) = @_; |
my ($linkpc,$lpc,$to,$from,$condition) = @_; |
|
|
|
# Compute fully qualified ids for the link, the |
|
# and from/to by prepending lpc. |
|
# |
|
|
my $linkid=$lpc.'.'.$linkpc; |
my $linkid=$lpc.'.'.$linkpc; |
my $goesto=$lpc.'.'.$to; |
my $goesto=$lpc.'.'.$to; |
my $comesfrom=$lpc.'.'.$from; |
my $comesfrom=$lpc.'.'.$from; |
my $undercond=0; |
my $undercond=0; |
|
|
|
|
|
# If there is a condition, qualify it with the level counter. |
|
|
if ($condition) { |
if ($condition) { |
$undercond=$lpc.'.'.$condition; |
$undercond=$lpc.'.'.$condition; |
} |
} |
|
|
|
# Links are represnted by: |
|
# goesto_.fuullyqualifedlinkid => fully qualified to |
|
# comesfrom.fullyqualifiedlinkid => fully qualified from |
|
# undercond_.fullyqualifiedlinkid => fully qualified condition id. |
|
|
$hash{'goesto_'.$linkid}=$goesto; |
$hash{'goesto_'.$linkid}=$goesto; |
$hash{'comesfrom_'.$linkid}=$comesfrom; |
$hash{'comesfrom_'.$linkid}=$comesfrom; |
$hash{'undercond_'.$linkid}=$undercond; |
$hash{'undercond_'.$linkid}=$undercond; |
|
|
|
# In addition: |
|
# to_.fully qualified from => comma separated list of |
|
# link ids with that from. |
|
# Similarly: |
|
# from_.fully qualified to => comma separated list of link ids` |
|
# with that to. |
|
# That allows us given a resource id to know all edges that go to it |
|
# and leave from it. |
|
# |
|
|
if (defined($hash{'to_'.$comesfrom})) { |
if (defined($hash{'to_'.$comesfrom})) { |
$hash{'to_'.$comesfrom}.=','.$linkid; |
$hash{'to_'.$comesfrom}.=','.$linkid; |
} else { |
} else { |
Line 447 sub make_link {
|
Line 573 sub make_link {
|
} |
} |
|
|
# ------------------------------------------------------------------- Condition |
# ------------------------------------------------------------------- Condition |
|
# |
|
# Processes <condition> tags, storing sufficient information about them |
|
# in the hash so that they can be evaluated and used to conditionalize |
|
# what is presented to the student. |
|
# |
|
# these can have the following attributes |
|
# |
|
# id = A unique identifier of the condition within the map. |
|
# |
|
# value = Is a perl script-let that, when evaluated in safe space |
|
# determines whether or not the condition is true. |
|
# Normally this takes the form of a test on an Apache::lonnet::EXT call |
|
# to find the value of variable associated with a resource in the |
|
# map identified by a mapalias. |
|
# Here's a fragment of XML code that illustrates this: |
|
# |
|
# <param to="5" value="mainproblem" name="parameter_0_mapalias" type="string" /> |
|
# <resource src="" id="1" type="start" title="Start" /> |
|
# <resource src="/res/msu/albertel/b_and_c/p1.problem" id="5" title="p1.problem" /> |
|
# <condition value="&EXT('user.resource.resource.0.tries','mainproblem') |
|
# <2 " id="61" type="stop" /> |
|
# <link to="5" index="1" from="1" condition="61" /> |
|
# |
|
# In this fragment: |
|
# - The param tag establishes an alias to resource id 5 of 'mainproblem'. |
|
# - The resource that is the start of the map is identified. |
|
# - The resource tag identifies the resource associated with this tag |
|
# and gives it the id 5. |
|
# - The condition is true if the tries variable associated with mainproblem |
|
# is less than 2 (that is the user has had more than 2 tries). |
|
# The condition type is a stop condition which inhibits(?) the associated |
|
# link if the condition is false. |
|
# - The link to resource 5 from resource 1 is affected by this condition. |
|
# |
|
# type = Type of the condition. The type determines how the condition affects the |
|
# link associated with it and is one of |
|
# - 'force' |
|
# - 'stop' |
|
# anything else including not supplied..which treated as: |
|
# - 'normal'. |
|
# Presumably maps get created by the resource assembly tool and therefore |
|
# illegal type values won't squirm their way into the XML. |
|
# |
|
# Side effects: |
|
# - The kind_level-qualified-condition-id hash element is set to 'cond'. |
|
# - The condition text is pushed into the cond array and its element number is |
|
# set in the condid_level-qualified-condition-id element of the hash. |
|
# - The condition type is colon appneded to the cond array element for this condition. |
sub parse_condition { |
sub parse_condition { |
my ($token,$lpc) = @_; |
my ($token,$lpc) = @_; |
my $rid=$lpc.'.'.$token->[2]->{'id'}; |
my $rid=$lpc.'.'.$token->[2]->{'id'}; |