[LON-CAPA-cvs] cvs: rat /client parameter.html loncom loncapa_apache.conf loncom/interface courseprefs.pm loncommon.pm lonconfigsettings.pm lonnavmaps.pm lonparmset.pm loncom/lti ltiauth.pm
raeburn
raeburn at source.lon-capa.org
Wed Aug 4 15:59:12 EDT 2021
raeburn Wed Aug 4 19:59:12 2021 EDT
Modified files:
/loncom/interface courseprefs.pm lonconfigsettings.pm loncommon.pm
lonnavmaps.pm lonparmset.pm
/loncom loncapa_apache.conf
/loncom/lti ltiauth.pm
/rat/client parameter.html
Log:
- Bug 6907 Content in a course can be set to be deep-link only.
- deeplink parameter has 5 components:
state, others, listing, scope, protect and menus.
- deeplink protection for launch from non-LON-CAPA LTI-enabled systems
uses /adm/launch/tiny/$dom/$uniqueID, and key and secret used by launcher
can be set in a course (by CC), or for domain (by DC).
-------------- next part --------------
Index: loncom/interface/courseprefs.pm
diff -u loncom/interface/courseprefs.pm:1.92 loncom/interface/courseprefs.pm:1.93
--- loncom/interface/courseprefs.pm:1.92 Mon Jun 7 02:19:51 2021
+++ loncom/interface/courseprefs.pm Wed Aug 4 19:59:10 2021
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Handler to set configuration settings for a course
#
-# $Id: courseprefs.pm,v 1.92 2021/06/07 02:19:51 raeburn Exp $
+# $Id: courseprefs.pm,v 1.93 2021/08/04 19:59:10 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -365,9 +365,15 @@
}
my %values=&Apache::lonnet::dump('environment',$cdom,$cnum);
+ my %courselti=&Apache::lonnet::dump('lti',$cdom,$cnum,undef,undef,undef,1);
+ if ($courselti{'lock'}) {
+ delete($courselti{'lock'});
+ }
+ $values{'linkprotection'} = \%courselti;
my @prefs_order = ('courseinfo','localization','feedback','discussion',
'classlists','appearance','grading','printouts',
- 'menuitems','spreadsheet','bridgetasks','lti','other');
+ 'menuitems','linkprotection','spreadsheet','bridgetasks',
+ 'lti','other');
my %prefs = (
'courseinfo' =>
@@ -557,6 +563,14 @@
menucollections => 'Menu collections',
},
},
+ 'linkprotection' =>
+ {
+ text => 'Link protection',
+ help => 'Course_Prefs_Linkprotection',
+ header => [{col1 => 'Item',
+ col2 => 'Settings',
+ }],
+ },
'other' =>
{ text => 'Other settings',
help => 'Course_Prefs_Other',
@@ -764,6 +778,8 @@
$output .= &print_lti($cdom,$settings,$ordered,$itemtext,\$rowtotal,$crstype,$noedit);
} elsif ($action eq 'menuitems') {
$output .= &print_menuitems('bottom',$cdom,$settings,$itemtext,\$rowtotal,$crstype,$noedit);
+ } elsif ($action eq 'linkprotection') {
+ $output .= &print_linkprotection($cdom,$settings,\$rowtotal,$crstype,$noedit);
} elsif ($action eq 'other') {
$output .= &print_other($cdom,$settings,$allitems,\$rowtotal,$crstype,$noedit);
}
@@ -776,8 +792,8 @@
}
sub process_changes {
- my ($cdom,$action,$values,$item,$changes,$allitems,$disallowed,$crstype) = @_;
- my %newvalues;
+ my ($cdom,$cnum,$action,$values,$item,$changes,$allitems,$disallowed,$crstype) = @_;
+ my (%newvalues,%courselti,$errors);
if (ref($item) eq 'HASH') {
if (ref($changes) eq 'HASH') {
my @ordered;
@@ -794,6 +810,21 @@
}
}
}
+ } elsif ($action eq 'linkprotection') {
+ if (ref($values->{'linkprotection'}) eq 'HASH') {
+ foreach my $id (keys(%{$values->{'linkprotection'}})) {
+ if ($id =~ /^\d+$/) {
+ push(@ordered,$id);
+ unless (ref($values->{'linkprotection'}->{$id}) eq 'HASH') {
+ $courselti{$id} = '';
+ }
+ }
+ }
+ }
+ @ordered = sort { $a <=> $b } @ordered;
+ if (($env{'form.linkprot_add'}) && ($env{'form.linkprot_maxnum'} =~ /^\d+$/)) {
+ push(@ordered,$env{'form.linkprot_maxnum'});
+ }
} elsif (ref($item->{'ordered'}) eq 'ARRAY') {
if ($action eq 'courseinfo') {
my ($can_toggle_cat,$can_categorize) =
@@ -931,6 +962,78 @@
} elsif ($values->{'menucollections'}) {
$changes->{'menucollections'} = '';
}
+ } elsif ($action eq 'linkprotection') {
+ my %menutitles = <imenu_titles();
+ my (@items,%deletions,%itemids,%haschanges);
+ if ($env{'form.linkprot_add'}) {
+ my $name = $env{'form.linkprot_name_add'};
+ $name =~ s/(`)/'/g;
+ my ($newid,$error) = &get_courselti_id($cdom,$cnum,$name);
+ if ($newid) {
+ $itemids{'add'} = $newid;
+ push(@items,'add');
+ $haschanges{$newid} = 1;
+ } else {
+ $errors .= '<span class="LC_error">'.
+ &mt('Failed to acquire unique ID for link protection').
+ '</span>';
+ }
+ }
+ if (ref($values->{'linkprotection'}) eq 'HASH') {
+ my @todelete = &Apache::loncommon::get_env_multiple('form.linkprot_del');
+ my $maxnum = $env{'form.linkprot_maxnum'};
+ for (my $i=0; $i<=$maxnum; $i++) {
+ my $itemid = $env{'form.linkprot_id_'.$i};
+ $itemid =~ s/\D+//g;
+ if ($itemid) {
+ if (ref($values->{'linkprotection'}->{$itemid}) eq 'HASH') {
+ push(@items,$i);
+ $itemids{$i} = $itemid;
+ if ((@todelete > 0) && (grep(/^$i$/, at todelete))) {
+ $deletions{$itemid} = $values->{'linkprotection'}->{$itemid}->{'name'};
+ }
+ }
+ }
+ }
+
+ }
+ foreach my $idx (@items) {
+ my $itemid = $itemids{$idx};
+ next unless ($itemid);
+ if (exists($deletions{$itemid})) {
+ $courselti{$itemid} = $deletions{$itemid};
+ $haschanges{$itemid} = 1;
+ next;
+ }
+ my %current;
+ if (ref($values->{'linkprotection'}) eq 'HASH') {
+ if (ref($values->{'linkprotection'}->{$itemid}) eq 'HASH') {
+ foreach my $key (keys(%{$values->{'linkprotection'}->{$itemid}})) {
+ $current{$key} = $values->{'linkprotection'}->{$itemid}->{$key};
+ }
+ }
+ }
+ foreach my $inner ('name','key','secret','lifetime','version') {
+ my $formitem = 'form.linkprot_'.$inner.'_'.$idx;
+ $env{$formitem} =~ s/(`)/'/g;
+ if ($inner eq 'lifetime') {
+ $env{$formitem} =~ s/[^\d.]//g;
+ }
+ unless ($idx eq 'add') {
+ if ($current{$inner} ne $env{$formitem}) {
+ $haschanges{$itemid} = 1;
+ }
+ }
+ if ($env{$formitem} ne '') {
+ $courselti{$itemid}{$inner} = $env{$formitem};
+ }
+ }
+ }
+ if (keys(%haschanges)) {
+ foreach my $entry (keys(%haschanges)) {
+ $changes->{$entry} = $courselti{$entry};
+ }
+ }
} else {
foreach my $entry (@ordered) {
if ($entry eq 'cloners') {
@@ -971,7 +1074,7 @@
my $clonedom = $env{'form.cloners_newdom'};
if (&check_clone($clonedom,$disallowed) eq 'ok') {
my $newdom = '*:'.$env{'form.cloners_newdom'};
- if (@clonedoms) {
+ if (@clonedoms) {
if (!grep(/^\Q$newdom\E$/, at clonedoms)) {
$newvalues{$entry} .= ','.$newdom;
}
@@ -1417,7 +1520,51 @@
}
}
}
- return;
+ return $errors;
+}
+
+sub get_courselti_id {
+ my ($cdom,$cnum,$name) = @_;
+ # get lock on lti db in course
+ my $lockhash = {
+ lock => $env{'user.name'}.
+ ':'.$env{'user.domain'},
+ };
+ my $tries = 0;
+ my $gotlock = &Apache::lonnet::newput('lti',$lockhash,$cdom,$cnum);
+ my ($id,$error);
+ while (($gotlock ne 'ok') && ($tries<10)) {
+ $tries ++;
+ sleep (0.1);
+ $gotlock = &Apache::lonnet::newput('lti',$lockhash,$cdom,$cnum);
+ }
+ if ($gotlock eq 'ok') {
+ my %currids = &Apache::lonnet::dump('lti',$cdom,$cnum,undef,undef,undef,1);
+ if ($currids{'lock'}) {
+ delete($currids{'lock'});
+ if (keys(%currids)) {
+ my @curr = sort { $a <=> $b } keys(%currids);
+ if ($curr[-1] =~ /^\d+$/) {
+ $id = 1 + $curr[-1];
+ } else {
+ $id = 1;
+ }
+ } else {
+ $id = 1;
+ }
+ if ($id) {
+ unless (&Apache::lonnet::newput('lti',{ $id => $name },$cdom,$cnum) eq 'ok') {
+ $error = 'nostore';
+ }
+ } else {
+ $error = 'nonumber';
+ }
+ }
+ my $dellockoutcome = &Apache::lonnet::del('lti',['lock'],$cdom,$cnum);
+ } else {
+ $error = 'nolock';
+ }
+ return ($id,$error);
}
sub get_sec_str {
@@ -1462,8 +1609,12 @@
sub store_changes {
my ($cdom,$cnum,$prefs_order,$actions,$prefs,$values,$changes,$crstype) = @_;
my ($chome,$output);
- my (%storehash, at delkeys, at need_env_update, at oldcloner);
+ my (%storehash, at delkeys, at need_env_update, at oldcloner,%oldlinkprot);
if ((ref($values) eq 'HASH') && (ref($changes) eq 'HASH')) {
+ if (ref($values->{'linkprotection'}) eq 'HASH') {
+ %oldlinkprot = %{$values->{'linkprotection'}};
+ }
+ delete($values->{'linkprotection'});
%storehash = %{$values};
} else {
if ($crstype eq 'Community') {
@@ -1473,6 +1624,20 @@
}
return $output;
}
+ my ($numchanges,$skipstore);
+ if (ref($changes) eq 'HASH') {
+ $numchanges = scalar(keys(%{$changes}));
+ if (($numchanges == 1) && (exists($changes->{'linkprotection'}))) {
+ $skipstore = 1;
+ } elsif (!$numchanges) {
+ if ($crstype eq 'Community') {
+ $output = &mt('No changes made to community settings.');
+ } else {
+ $output = &mt('No changes made to course settings.');
+ }
+ return $output;
+ }
+ }
my %yesno = (
hidefromcat => '1',
problem_stream_switch => '1',
@@ -1485,7 +1650,7 @@
if (grep(/^\Q$item\E$/,@{$actions})) {
$output .= '<h3>'.&mt($prefs->{$item}{'text'}).'</h3>';
if (ref($changes->{$item}) eq 'HASH') {
- if (keys(%{$changes->{$item}}) > 0) {
+ if ((keys(%{$changes->{$item}}) > 0) || ($item eq 'linkprotection')) {
$output .= &mt('Changes made:').'<ul style="list-style:none;">';
if ($item eq 'other') {
foreach my $key (sort(keys(%{$changes->{$item}}))) {
@@ -1498,6 +1663,41 @@
"'$storehash{$key}'")).'</li>';
}
}
+ } elsif ($item eq 'linkprotection') {
+ if (&Apache::lonnet::put('lti',$changes->{'linkprotection'},$cdom,$cnum,1) eq 'ok') {
+ my $hashid=$cdom.'_'.$cnum;
+ &Apache::lonnet::devalidate_cache_new('courselti',$hashid);
+ foreach my $itemid (sort { $a <=> $b } %{$changes->{'linkprotection'}}) {
+ if (ref($changes->{'linkprotection'}->{$itemid}) eq 'HASH') {
+ my %values = %{$changes->{'linkprotection'}->{$itemid}};
+ my %desc = &linkprot_names();
+ my $display;
+ foreach my $title ('name','lifetime','version','key','secret') {
+ if ($title eq 'secret') {
+ my $length = length($values{$title});
+ $display .= $desc{$title}.': '.('*' x $length);
+ } elsif ($title eq 'version') {
+ if ($values{$title} eq 'LTI-1p0') {
+ $display .= $desc{$title}.': 1.1, ';
+ }
+ } else {
+ $display .= $desc{$title}.': '.$values{$title}.', ';
+ }
+ }
+ $output .= '<li>'.&Apache::lonhtmlcommon::confirm_success(&mt('[_1] set to [_2]','<i>'.$itemid.'</i>',
+ "'$display'")).'</li>';
+ } elsif (ref($oldlinkprot{$itemid}) eq 'HASH') {
+ my $oldname = $oldlinkprot{$itemid}{'name'};
+ $output .= '<li>'.&Apache::lonhtmlcommon::confirm_success(&mt('Deleted setting for [_1]','<i>'."$itemid ($oldname)".'</i>')).'</li>';
+ }
+ }
+ } else {
+ $output .= '<li>'.
+ '<span class="LC_error">'.
+ &mt('An error occurred when saving changes to link protection settings, which remain unchanged.').
+ '</span>'.
+ '</li>';
+ }
} else {
if (ref($prefs->{$item}->{'ordered'}) eq 'ARRAY') {
my @settings = @{$prefs->{$item}->{'ordered'}};
@@ -1785,6 +1985,9 @@
}
}
}
+ if ($skipstore) {
+ return $output;
+ }
if (&Apache::lonnet::put('environment',\%storehash,$cdom,$cnum) eq 'ok') {
if (ref($changes) eq 'HASH') {
if (ref($changes->{'courseinfo'}) eq 'HASH') {
@@ -5039,6 +5242,97 @@
return $output;
}
+sub print_linkprotection {
+ my ($cdom,$settings,$rowtotal,$crstype,$noedit) = @_;
+ unless (ref($settings) eq 'HASH') {
+ return;
+ }
+
+ my %linkprotection;
+ my $count = 0;
+ my $next = 1;
+ my ($datatable,$disabled,$css_class);
+ if ($noedit) {
+ $disabled = ' disabled="disabled"';
+ }
+ my %lt = &linkprot_names();
+ my $itemcount = 0;
+
+ if (ref($settings->{'linkprotection'}) eq 'HASH') {
+ if (keys(%{$settings->{'linkprotection'}})) {
+ my @current = sort { $a <=> $b } keys(%{$settings->{'linkprotection'}});
+ $next += $current[-1];
+ for (my $i=0; $i<@current; $i++) {
+ my $num = $current[$i];
+ my %values;
+ if (ref($settings->{'linkprotection'}->{$num}) eq 'HASH') {
+ %values = %{$settings->{'linkprotection'}->{$num}};
+ } else {
+ next;
+ }
+ my $selected;
+ if (($values{'version'} eq 'LTI-1p0') || ($values{'version'} eq '')) {
+ $selected = ' selected="selected"';
+ }
+ $css_class = $itemcount%2?' class="LC_odd_row"':'';
+ $datatable .=
+ '<tr '.$css_class.'><td><span class="LC_nobreak">'.
+ '<label><input type="checkbox" name="linkprot_del" value="'.$i.'"'.$disabled.' />'.
+ &mt('Delete?').'</label></span></td>'.
+ '<td><span class="LC_nobreak">'.$lt{'name'}.
+ ':<input type="text" size="15" name="linkprot_name_'.$i.'" value="'.$values{'name'}.'"'.$disabled.' /></span> '.
+ (' 'x2).
+ '<span class="LC_nobreak">'.$lt{'version'}.':<select name="linkprot_version_'.$i.'">'.
+ '<option value="LTI-1p0" '.$selected.'>1.1</option></select></span> '."\n".
+ (' 'x2).
+ '<span class="LC_nobreak">'.$lt{'lifetime'}.':<input type="text" name="linkprot_lifetime_'.$i.'"'.
+ 'value="'.$values{'lifetime'}.'" size="3"'.$disabled.' /></span>'.
+ '<br /><br />'.
+ '<span class="LC_nobreak">'.$lt{'key'}.
+ ':<input type="text" size="25" name="linkprot_key_'.$i.'" value="'.$values{'key'}.'"'.$disabled.' /></span> '.
+ (' 'x2).
+ '<span class="LC_nobreak">'.$lt{'secret'}.':'.
+ '<input type="password" size="20" name="linkprot_secret_'.$i.'" value="'.$values{'secret'}.'"'.$disabled.' />'.
+ '<label><input type="checkbox" name="visible" onclick="if (this.checked) { this.form.linkprot_secret_'.$i.'.type='."'text'".' } else { this.form.linkprot_secret_'.$i.'.type='."'password'".' }" />'.&mt('Visible input').'</label>'.
+ '<input type="hidden" name="linkprot_id_'.$i.'" value="'.$num.'" /></span>'.
+ '</td></tr>';
+ $itemcount ++;
+ }
+ }
+ }
+ $css_class = $itemcount%2?' class="LC_odd_row"':'';
+ $datatable .= '<tr '.$css_class.'><td><span class="LC_nobreak">'."\n".
+ '<input type="hidden" name="linkprot_maxnum" value="'.$next.'" />'."\n".
+ '<input type="checkbox" name="linkprot_add" value="1" />'.&mt('Add').'</span></td>'."\n".
+ '<td>'.
+ '<span class="LC_nobreak">'.$lt{'name'}.
+ ':<input type="text" size="15" name="linkprot_name_add" value="" /></span> '."\n".
+ (' 'x2).
+ '<span class="LC_nobreak">'.$lt{'version'}.':<select name="linkprot_version_add">'.
+ '<option value="LTI-1p0" selected="selected">1.1</option></select></span> '."\n".
+ (' 'x2).
+ '<span class="LC_nobreak">'.$lt{'lifetime'}.':<input type="text" size="3" name="linkprot_lifetime_add" value="300" /></span> '."\n".
+ '<br /><br />'.
+ '<span class="LC_nobreak">'.$lt{'key'}.':<input type="text" size="25" name="linkprot_key_add" value="" /></span> '."\n".
+ (' 'x2).
+ '<span class="LC_nobreak">'.$lt{'secret'}.':<input type="password" size="20" name="linkprot_secret_add" value="" />'.
+ '<label><input type="checkbox" name="visible" onclick="if (this.checked) { this.form.linkprot_secret_add.type='."'text'".' } else { this.form.linkprot_secret_add.type='."'password'".' }" />'.&mt('Visible input').'</label></span> '."\n".
+ '</td></tr>';
+ $$rowtotal ++;
+ return $datatable;;
+}
+
+sub linkprot_names {
+ my %lt = &Apache::lonlocal::texthash(
+ 'version' => 'LTI Version',
+ 'key' => 'Key',
+ 'lifetime' => 'Nonce lifetime (s)',
+ 'name' => 'Launcher Application Name',
+ 'secret' => 'Secret',
+ );
+ return %lt;
+}
+
sub print_other {
my ($cdom,$settings,$allitems,$rowtotal,$crstype,$noedit) = @_;
unless ((ref($settings) eq 'HASH') && (ref($allitems) eq 'ARRAY')) {
@@ -5061,7 +5355,7 @@
input => 'textbox',
size => '30',
};
- my $output = &make_item_rows($cdom,\%items,\@ordered,$settings,$rowtotal,$crstype,'other',$noedit);
+ return &make_item_rows($cdom,\%items,\@ordered,$settings,$rowtotal,$crstype,'other',$noedit);
}
sub get_other_items {
Index: loncom/interface/lonconfigsettings.pm
diff -u loncom/interface/lonconfigsettings.pm:1.49 loncom/interface/lonconfigsettings.pm:1.50
--- loncom/interface/lonconfigsettings.pm:1.49 Fri Jul 30 13:48:20 2021
+++ loncom/interface/lonconfigsettings.pm Wed Aug 4 19:59:10 2021
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Handler to set domain-wide configuration settings
#
-# $Id: lonconfigsettings.pm,v 1.49 2021/07/30 13:48:20 raeburn Exp $
+# $Id: lonconfigsettings.pm,v 1.50 2021/08/04 19:59:10 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -355,7 +355,7 @@
{href=>"javascript:changePage(document.$phase,'$phase')",
text=>"Updated"});
&print_header($r,$phase,$context,undef,$container);
- my ($crstype,%lastact);
+ my ($crstype,%lastact,$errors);
if ($context eq 'course') {
$crstype = &Apache::loncommon::course_type();
}
@@ -369,9 +369,10 @@
$confname,$item,$roles,$values,\%lastact));
} else {
$changes{$item} = {};
- &Apache::courseprefs::process_changes($dom,$item,$values,
- $prefs->{$item},$changes{$item},
- $allitems,\%disallowed,$crstype);
+ $errors =
+ &Apache::courseprefs::process_changes($dom,$item,$values,
+ $prefs->{$item},$changes{$item},
+ $allitems,\%disallowed,$crstype);
if (keys(%{$changes{$item}}) > 0) {
$numchanged ++;
}
@@ -403,6 +404,9 @@
}
$r->print('</p>');
}
+ if ($errors) {
+ $r->print('<p>'.$errors.'</p>');
+ }
}
$r->print('<p>');
my $footer_text = 'Back to configuration display';
Index: loncom/interface/loncommon.pm
diff -u loncom/interface/loncommon.pm:1.1362 loncom/interface/loncommon.pm:1.1363
--- loncom/interface/loncommon.pm:1.1362 Mon Jul 19 15:48:26 2021
+++ loncom/interface/loncommon.pm Wed Aug 4 19:59:10 2021
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# a pile of common routines
#
-# $Id: loncommon.pm,v 1.1362 2021/07/19 15:48:26 raeburn Exp $
+# $Id: loncommon.pm,v 1.1363 2021/08/04 19:59:10 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -9170,7 +9170,7 @@
}
}
if ($deeplink ne '') {
- my ($listed,$scope,$access,$display) = split(/,/,$deeplink);
+ my ($state,$others,$listed,$scope,$protect,$display) = split(/,/,$deeplink);
if ($display =~ /^\d+$/) {
$deeplinkmenu = 1;
$menucoll = $display;
Index: loncom/interface/lonnavmaps.pm
diff -u loncom/interface/lonnavmaps.pm:1.553 loncom/interface/lonnavmaps.pm:1.554
--- loncom/interface/lonnavmaps.pm:1.553 Mon Jul 19 15:48:26 2021
+++ loncom/interface/lonnavmaps.pm Wed Aug 4 19:59:10 2021
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Navigate Maps Handler
#
-# $Id: lonnavmaps.pm,v 1.553 2021/07/19 15:48:26 raeburn Exp $
+# $Id: lonnavmaps.pm,v 1.554 2021/08/04 19:59:10 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -1858,7 +1858,8 @@
}
} else {
my $deeplink = $navmap->get_mapparam(undef,$mapname,"0.deeplink");
- if ($deeplink =~ /^(absent|grades),/) {
+ my ($state,$others,$listed) = split(/,/,$deeplink);
+ if (($listed eq 'absent') || ($listed eq 'grades')) {
if ($userCanSeeHidden) {
$args->{'mapUnlisted'} = 1;
} else {
@@ -3662,7 +3663,7 @@
before anything else. deeplinklisted if true (default false), will
check "listed" status of a resource with a deeplink, and unless "absent"
will exclude deeplink checking when retrieving the browsePriv from
-lonnet::allowed().
+lonnet::allowed().
Thus, by default, only top-level resources will be shown. Change the
condition to a 1 without changing the hash, and all resources will be
@@ -5192,7 +5193,7 @@
my ($self,$caller,$action) = @_;
my $deeplink = $self->parmval("deeplink");
if ($deeplink) {
- my ($listed,$scope,$access) = split(/,/,$deeplink);
+ my ($state,$others,$listed,$scope) = split(/,/,$deeplink);
if ($action eq 'getlisted') {
return $listed;
}
@@ -5222,7 +5223,7 @@
}
}
}
- unless (($caller eq 'sequence') || ($access eq 'any')) {
+ unless (($caller eq 'sequence') || ($state eq 'both')) {
return $listed;
}
}
@@ -6435,7 +6436,7 @@
$self->{SYMB},undef,
undef,$noblockcheck,
undef,$nodeeplinkcheck,
- $nodeeplinkout);
+ $nodeeplinkout);
}
=pod
Index: loncom/interface/lonparmset.pm
diff -u loncom/interface/lonparmset.pm:1.600 loncom/interface/lonparmset.pm:1.601
--- loncom/interface/lonparmset.pm:1.600 Sat Jun 12 22:06:32 2021
+++ loncom/interface/lonparmset.pm Wed Aug 4 19:59:10 2021
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Handler to set parameters for assessments
#
-# $Id: lonparmset.pm,v 1.600 2021/06/12 22:06:32 raeburn Exp $
+# $Id: lonparmset.pm,v 1.601 2021/08/04 19:59:10 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -1245,9 +1245,10 @@
var ipallowRegExp = /^setipallow_/;
var ipdenyRegExp = /^setipdeny_/;
var deeplinkRegExp = /^deeplink_/;
- var dlListScopeRegExp = /^deeplink_(listing|scope)_/;
- var dlLinkUrlsRegExp = /^deeplink_urls_/;
- var dlLtiRegExp = /^deeplink_lti_/;
+ var dlListScopeRegExp = /^deeplink_(state|others|listing|scope)_/;
+ var dlLinkProtectRegExp = /^deeplink_protect_/;
+ var dlLtidRegExp = /^deeplink_ltid_/;
+ var dlLticRegExp = /^deeplink_ltic_/;
var dlKeyRegExp = /^deeplink_key_/;
var dlMenusRegExp = /^deeplink_menus_/;
var dlCollsRegExp = /^deeplink_colls_/;
@@ -1314,9 +1315,9 @@
}
document.parmform.elements['set_'+identifier].value += possdeeplink;
}
- } else if (dlLinkUrlsRegExp.test(name)) {
+ } else if (dlLinkProtectRegExp.test(name)) {
if (document.parmform.elements[i].checked) {
- var identifier = name.replace(dlLinkUrlsRegExp,'');
+ var identifier = name.replace(dlLinkProtectRegExp,'');
var posslinkurl = document.parmform.elements[i].value;
posslinkurl = posslinkurl.replace(/^\s+|\s+$/g,'');
if (document.parmform.elements['set_'+identifier].value) {
@@ -1324,25 +1325,41 @@
}
document.parmform.elements['set_'+identifier].value += posslinkurl;
}
- } else if (dlLtiRegExp.test(name)) {
- var identifier = name.replace(dlLtiRegExp,'');
- if (isRadioSet('deeplink_urls_'+identifier,'lti')) {
- var posslti = document.parmform.elements[i].value;
- posslti = posslti.replace(/\D+/g,'');
- if (posslti.length) {
+ } else if (dlLtidRegExp.test(name)) {
+ var identifier = name.replace(dlLtidRegExp,'');
+ if (isRadioSet('deeplink_protect_'+identifier,'ltid')) {
+ var possltid = document.parmform.elements[i].value;
+ possltid = possltid.replace(/\D+/g,'');
+ if (possltid.length) {
if (document.parmform.elements['set_'+identifier].value) {
- posslti = ':'+posslti;
+ possltid = ':'+possltid;
}
- document.parmform.elements['set_'+identifier].value += posslti;
+ document.parmform.elements['set_'+identifier].value += possltid;
} else {
document.parmform.elements['set_'+identifier].value = '';
- alert("A link type of 'deep with LTI launch' was selected but no LTI launcher was selected.\nPlease select one, or choose a different supported link type.");
+ alert("A link type of 'domain LTI launch' was selected but no domain LTI launcher was selected.\nPlease select one, or choose a different supported link type.");
return false;
}
}
+ } else if (dlLticRegExp.test(name)) {
+ var identifier = name.replace(dlLticRegExp,'');
+ if (isRadioSet('deeplink_protect_'+identifier,'ltic')) {
+ var possltic = document.parmform.elements[i].value;
+ possltic = possltic.replace(/\D+/g,'');
+ if (possltic.length) {
+ if (document.parmform.elements['set_'+identifier].value) {
+ possltic = ':'+possltic;
+ }
+ document.parmform.elements['set_'+identifier].value += possltic;
+ } else {
+ document.parmform.elements['set_'+identifier].value = '';
+ alert("A link type of 'course LTI launch' was selected but no course LTI launcher was selected.\nPlease select one, or choose a different supported link type.");
+ return false;
+ }
+ }
} else if (dlKeyRegExp.test(name)) {
var identifier = name.replace(dlKeyRegExp,'');
- if (isRadioSet('deeplink_urls_'+identifier,'key')) {
+ if (isRadioSet('deeplink_protect_'+identifier,'key')) {
var posskey = document.parmform.elements[i].value;
posskey = posskey.replace(/^\s+|\s+$/g,'');
var origlength = posskey.length;
@@ -1478,32 +1495,44 @@
if (document.getElementById('deeplink_key_'+item+'_'+key)) {
keybox = document.getElementById('deeplink_key_'+item+'_'+key);
}
- var divoption;
- if (item == 'urls') {
- divoption = 'lti'
+ var divoptions = new Array();
+ if (item == 'protect') {
+ divoptions = ['ltic','ltid'];
} else {
if (item == 'menus') {
- divoption = 'colls';
+ divoptions = ['colls'];
}
}
- var seldiv;
- if (document.getElementById('deeplinkdiv_'+divoption+'_'+item+'_'+key)) {
- seldiv = document.getElementById('deeplinkdiv_'+divoption+'_'+item+'_'+key);
+ var seldivs = new Array();
+ if ((item == 'protect') || (item == 'menus')) {
+ for (var i=0; i<divoptions.length; i++) {
+ if (document.getElementById('deeplinkdiv_'+divoptions[i]+'_'+item+'_'+key)) {
+ seldivs[i] = document.getElementById('deeplinkdiv_'+divoptions[i]+'_'+item+'_'+key);
+ } else {
+ seldivs[i] = '';
+ }
+ }
}
for (var i=0; i<radios.length; i++) {
if (radios[i].checked) {
- if (radios[i].value == divoption) {
- seldiv.style.display = 'inline-block';
- if (item == 'urls') {
- keybox.type = 'hidden';
- keybox.value = '';
- }
- } else {
- if (seldiv != '') {
- seldiv.style.display = 'none';
- form['deeplink_'+divoption+'_'+key].selectedIndex = 0;
+ if ((item == 'protect') || (item == 'menus')) {
+ for (var j=0; j<seldivs.length; j++) {
+ if (radios[i].value == divoptions[j]) {
+ if (seldivs[j] != '') {
+ seldivs[j].style.display = 'inline-block';
+ }
+ if (item == 'protect') {
+ keybox.type = 'hidden';
+ keybox.value = '';
+ }
+ } else {
+ if (seldivs[j] != '') {
+ seldivs[j].style.display = 'none';
+ form['deeplink_'+divoptions[j]+'_'+key].selectedIndex = 0;
+ }
+ }
}
- if (item == 'urls') {
+ if (item == 'protect') {
if (radios[i].value == 'key') {
keybox.type = 'text';
} else {
@@ -1704,23 +1733,37 @@
$effparm_rec = 1;
}
if ($parmname eq 'deeplink') {
- my %posslti;
+ my ($domltistr,$crsltistr);
my %lti =
&Apache::lonnet::get_domain_lti($env{'course.'.$env{'request.course.id'}.'.domain'},
'provider');
- foreach my $item (keys(%lti)) {
- if (ref($lti{$item}) eq 'HASH') {
- unless ($lti{$item}{'requser'}) {
- $posslti{$item} = $lti{$item}{'consumer'};
+ if (keys(%lti)) {
+ foreach my $item (sort { $a <=> $b } (keys(%lti))) {
+ if (ref($lti{$item}) eq 'HASH') {
+ unless ($lti{$item}{'requser'}) {
+ $domltistr .= $item.':'.&escape(&escape($lti{$item}{'consumer'})).',';
+ }
}
}
+ $domltistr =~ s/,$//;
+ if ($domltistr) {
+ $extra = 'ltid_'.$domltistr;
+ }
}
- if (keys(%posslti)) {
- $extra = 'lti_';
- foreach my $lti (sort { $a <=> $b } keys(%posslti)) {
- $extra .= $lti.':'.&escape(&escape($posslti{$lti})).',';
+ my %courselti = &Apache::lonnet::get_course_lti($cnum,$cdom);
+ if (keys(%courselti)) {
+ foreach my $item (sort { $a <=> $b } keys(%courselti)) {
+ if (($item =~ /^\d+$/) && (ref($courselti{$item}) eq 'HASH')) {
+ $crsltistr .= $item.':'.&escape(&escape($courselti{$item}{'name'})).',';
+ }
+ }
+ $crsltistr =~ s/,$//;
+ if ($crsltistr) {
+ if ($extra) {
+ $extra .= '&';
+ }
+ $extra .= 'ltic_'.$crsltistr;
}
- $extra =~ s/,$//;
}
if ($env{'course.'.$env{'request.course.id'}.'.menucollections'}) {
my @colls;
@@ -4982,21 +5025,30 @@
sub string_deeplink_selector {
my ($thiskey, $showval, $readonly) = @_;
my (@components,%values, at current,%titles,%options,%optiontext,%defaults,
- %selectnull,%posslti, at possmenus);
- @components = ('listing','scope','urls','menus');
+ %selectnull,%domlti,%crslti, at possmenus);
+ @components = ('state','others','listing','scope','protect','menus');
%titles = &Apache::lonlocal::texthash (
+ state => 'Access status',
+ others => 'Hide other resources',
listing => 'In Contents and/or Gradebook',
scope => 'Access scope for link',
- urls => 'Supported link types',
+ protect => 'Link protection',
menus => 'Menu Items Displayed',
);
%options = (
+ state => ['only','off','both'],
+ others => ['hide','unhide'],
listing => ['full','absent','grades','details','datestatus'],
scope => ['res','map','rec'],
- urls => ['any','only','key','lti'],
+ protect => ['none','key','ltid','ltic'],
menus => ['std','colls'],
);
%optiontext = &Apache::lonlocal::texthash (
+ only => 'deep only',
+ off => 'deeplink off',
+ both => 'regular + deep',
+ hide => 'Hidden',
+ unhide => 'Unhidden',
full => 'Listed (linked) in both',
absent => 'Not listed',
grades => 'Listed in grades only',
@@ -5005,41 +5057,54 @@
res => 'resource only',
map => 'enclosing map/folder',
rec => 'recursive map/folder',
- any => 'regular + deep',
- only => 'deep only',
- key => 'deep with key',
- lti => 'deep with LTI launch',
+ none => 'not in use',
+ key => 'key access',
+ ltic => 'LTI access (course)',
+ ltid => 'LTI access (domain)' ,
std => 'Standard (all menus)',
colls => 'Numbered collection',
);
%selectnull = &Apache::lonlocal::texthash (
- lti => 'Select Provider',
+ ltic => 'Select Launcher',
+ ltid => 'Select Launcher',
colls => 'Select',
);
if ($showval =~ /,/) {
%values=();
@current = split(/,/,$showval);
- ($values{'listing'}) = ($current[0] =~ /^(full|absent|grades|details|datestatus)$/);
- ($values{'scope'}) = ($current[1] =~ /^(res|map|rec)$/);
- ($values{'urls'}) = ($current[2] =~ /^(any|only|key:[a-zA-Z\d_.!\@#\$%^&*()+=-]+|lti:\d+)$/);
- ($values{'menus'}) = ($current[3] =~ /^(\d+)$/);
+ ($values{'state'}) = ($current[0] =~ /^(only|off|both)$/);
+ ($values{'others'}) = ($current[1] =~ /^(hide|unhide)$/);
+ ($values{'listing'}) = ($current[2] =~ /^(full|absent|grades|details|datestatus)$/);
+ ($values{'scope'}) = ($current[3] =~ /^(res|map|rec)$/);
+ ($values{'protect'}) = ($current[4] =~ /^(key:[a-zA-Z\d_.!\@#\$%^&*()+=-]+|ltic:\d+|ltid:\d+)$/);
+ ($values{'menus'}) = ($current[5] =~ /^(\d+)$/);
} else {
+ $defaults{'state'} = 'off',
+ $defaults{'others'} = 'unhide',
$defaults{'listing'} = 'full';
$defaults{'scope'} = 'res';
- $defaults{'urls'} = 'any';
+ $defaults{'protect'} = 'none';
$defaults{'menus'} = '0';
}
my $disabled;
if ($readonly) {
$disabled=' disabled="disabled"';
}
- my %lti =
+ my %courselti =
+ &Apache::lonnet::get_course_lti($env{'course.'.$env{'request.course.id'}.'.num'},
+ $env{'course.'.$env{'request.course.id'}.'.domain'});
+ foreach my $item (keys(%courselti)) {
+ if (ref($courselti{$item}) eq 'HASH') {
+ $crslti{$item} = $courselti{$item}{'name'};
+ }
+ }
+ my %lti =
&Apache::lonnet::get_domain_lti($env{'course.'.$env{'request.course.id'}.'.domain'},
'provider');
foreach my $item (keys(%lti)) {
if (ref($lti{$item}) eq 'HASH') {
unless ($lti{$item}{'requser'}) {
- $posslti{$item} = $lti{$item}{'consumer'};
+ $domlti{$item} = $lti{$item}{'consumer'};
}
}
}
@@ -5059,11 +5124,15 @@
$output .= '</tr><tr>';
foreach my $item (@components) {
$output .= '<td>';
- if (($item eq 'urls') || ($item eq 'menus')) {
+ if (($item eq 'protect') || ($item eq 'menus')) {
my $selected = $values{$item};
foreach my $option (@{$options{$item}}) {
- if (($item eq 'urls') && ($option eq 'lti')) {
- next unless (keys(%posslti));
+ if ($item eq 'protect') {
+ if ($option eq 'ltid') {
+ next unless (keys(%domlti));
+ } elsif ($option eq 'ltic') {
+ next unless (keys(%crslti));
+ }
} elsif (($item eq 'menus') && ($option eq 'colls')) {
next unless (@possmenus);
}
@@ -5088,7 +5157,7 @@
$output .= '<span class="LC_nobreak"><label>'.
'<input type="radio" name="deeplink_'.$item.'_'.$thiskey.'" value="'.$option.'"'.$onclick.$disabled.$checked.' />'."\n".
$optiontext{$option}.'</label>';
- if (($item eq 'urls') && ($option eq 'key')) {
+ if (($item eq 'protect') && ($option eq 'key')) {
my $visibility="hidden";
my $currkey;
if ($checked) {
@@ -5097,12 +5166,12 @@
}
$output .= ' '.
'<input type="'.$visibility.'" name="deeplink_'.$option.'_'.$thiskey.'" id="deeplink_'.$option.'_'.$item.'_'.$thiskey.'" value="'.$currkey.'" size="10"'.$disabled.' />';
- } elsif (($option eq 'lti') || ($option eq 'colls')) {
+ } elsif (($option eq 'ltic') || ($option eq 'ltid') || ($option eq 'colls')) {
my $display="none";
my ($current,$blankcheck, at possibles);
if ($checked) {
$display = 'inline-block';
- if ($option eq 'lti') {
+ if (($option eq 'ltic') || ($option eq 'ltid')) {
$current = (split(/\:/,$selected))[1];
} else {
$current = $selected;
@@ -5110,8 +5179,10 @@
} else {
$blankcheck = ' selected="selected"';
}
- if ($option eq 'lti') {
- @possibles = keys(%posslti);
+ if ($option eq 'ltid') {
+ @possibles = keys(%domlti);
+ } elsif ($option eq 'ltic') {
+ @possibles = keys(%crslti);
} else {
@possibles = @possmenus;
}
@@ -5128,8 +5199,10 @@
$selected = ' selected="selected"';
}
my $shown = $poss;
- if ($option eq 'lti') {
- $shown = $posslti{$poss};
+ if ($option eq 'ltid') {
+ $shown = $domlti{$poss};
+ } elsif ($option eq 'ltic') {
+ $shown = $crslti{$poss};
}
$output .= '<option value="'.$poss.'"'.$selected.'>'.$shown.'</option>';
}
Index: loncom/loncapa_apache.conf
diff -u loncom/loncapa_apache.conf:1.274 loncom/loncapa_apache.conf:1.275
--- loncom/loncapa_apache.conf:1.274 Sat Sep 12 16:26:01 2020
+++ loncom/loncapa_apache.conf Wed Aug 4 19:59:11 2021
@@ -2,7 +2,7 @@
## loncapa_apache.conf -- Apache HTTP LON-CAPA configuration file
##
-# $Id: loncapa_apache.conf,v 1.274 2020/09/12 16:26:01 raeburn Exp $
+# $Id: loncapa_apache.conf,v 1.275 2021/08/04 19:59:11 raeburn Exp $
#
# LON-CAPA Section (extensions to httpd.conf daemon configuration)
@@ -770,6 +770,11 @@
PerlHandler Apache::lonlogin
</Location>
+<LocationMatch "^/+adm/launch/tiny/[\w.-]+/\w+">
+SetHandler perl-script
+PerlHandler Apache::ltiauth
+</LocationMatch>
+
<LocationMatch "^/+adm/lti($|/)">
SetHandler perl-script
PerlHandler Apache::ltiauth
Index: loncom/lti/ltiauth.pm
diff -u loncom/lti/ltiauth.pm:1.19 loncom/lti/ltiauth.pm:1.20
--- loncom/lti/ltiauth.pm:1.19 Thu Jul 18 18:28:46 2019
+++ loncom/lti/ltiauth.pm Wed Aug 4 19:59:11 2021
@@ -1,7 +1,7 @@
# The LearningOnline Network
# Basic LTI Authentication Module
#
-# $Id: ltiauth.pm,v 1.19 2019/07/18 18:28:46 raeburn Exp $
+# $Id: ltiauth.pm,v 1.20 2021/08/04 19:59:11 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -42,6 +42,7 @@
sub handler {
my $r = shift;
my $requri = $r->uri;
+ my $hostname = $r->hostname;
#
# Check for existing session, and temporarily delete any form items
# in %env, if session exists
@@ -57,7 +58,7 @@
}
}
#
-# Retrieve data POSTed by LTI Consumer on launch
+# Retrieve data POSTed by LTI launch
#
&Apache::lonacc::get_posted_cgi($r);
my $params = {};
@@ -97,12 +98,131 @@
# Retrieve "internet domains" for all this institution's LON-CAPA
# nodes.
#
- my ($udom,$uname,$uhome,$cdom,$cnum,$symb,$mapurl, at intdoms);
+ my @intdoms;
my $lonhost = $r->dir_config('lonHostID');
my $internet_names = &Apache::lonnet::get_internet_names($lonhost);
if (ref($internet_names) eq 'ARRAY') {
@intdoms = @{$internet_names};
}
+#
+# Determine course's domain in LON-CAPA
+# for basic launch using key and secret managed
+# in LON-CAPA course (i.e., uri begins /adm/launch)
+#
+
+ my ($cdom,$cnum);
+
+# Note: "internet domain" for course's domain must be one of the
+# internet domains for the institution's LON-CAPA servers.
+#
+ if ($requri =~ m{^/adm/launch(|/.*)$}) {
+ my $tail = $1;
+ if ($tail =~ m{^/tiny/($match_domain)/(\w+)$}) {
+ my ($urlcdom,$urlcnum) = &course_from_tinyurl($tail);
+ if (($urlcdom ne '') && ($urlcnum ne '')) {
+ $cdom = $urlcdom;
+ $cnum = $urlcnum;
+ my $primary_id = &Apache::lonnet::domain($cdom,'primary');
+ if ($primary_id ne '') {
+ my $intdom = &Apache::lonnet::internet_dom($primary_id);
+ if (($intdom ne '') && (grep(/^\Q$intdom\E$/, at intdoms))) {
+#
+# Retrieve information for LTI link protectors in course
+# where url was /adm/launch/tiny/$cdom/$uniqueid
+#
+ my (%crslti,%crslti_by_key,$itemid,$ltitype);
+ %crslti = &Apache::lonnet::get_course_lti($cnum,$cdom,'provider');
+ if (keys(%crslti)) {
+ foreach my $id (keys(%crslti)) {
+ if (ref($crslti{$id}) eq 'HASH') {
+ my $key = $crslti{$id}{'key'};
+ push(@{$crslti_by_key{$key}},$id);
+ }
+ }
+ }
+#
+# Verify the signed request using the secret for LTI link
+# protectors for which the key in the POSTed data matches
+# keys in the course configuration.
+#
+# Request is invalid if the signed request could not be verified
+# for the key and secret from LON-CAPA course configuration for
+# LTI link protectors or from LON-CAPA configuration for the
+# course's domain if there are LTI Providers which may be used.
+#
+# Determine if nonce in POSTed data has expired.
+# If unexpired, confirm it has not already been used.
+#
+ if (keys(%crslti)) {
+ $itemid = &get_lti_itemid($requri,$hostname,$params,\%crslti,\%crslti_by_key);
+ }
+ if (($itemid) && (ref($crslti{$itemid}) eq 'HASH')) {
+ $ltitype = 'c';
+ unless (&LONCAPA::ltiutils::check_nonce($params->{'oauth_nonce'},$params->{'oauth_timestamp'},
+ $crslti{$itemid}{'lifetime'},$cdom,$r->dir_config('lonLTIDir'))) {
+ &invalid_request($r,3);
+ return OK;
+ }
+ } else {
+ my %lti = &Apache::lonnet::get_domain_lti($cdom,'provider');
+ unless (keys(%lti) > 0) {
+ &invalid_request($r,4);
+ return OK;
+ }
+ my (%domlti_by_key,%domlti);
+ foreach my $id (keys(%lti)) {
+ if (ref($lti{$id}) eq 'HASH') {
+ my $key = $lti{$id}{'key'};
+ if (!$lti{$itemid}{'requser'}) {
+ push(@{$domlti_by_key{$key}},$id);
+ $domlti{$id} = $lti{$id};
+ }
+ }
+ }
+ if (keys(%domlti)) {
+ $itemid = &get_lti_itemid($requri,$hostname,$params,\%domlti,\%domlti_by_key);
+ }
+ if (($itemid) && (ref($domlti{$itemid}) eq 'HASH')) {
+ $ltitype = 'd';
+ unless (&LONCAPA::ltiutils::check_nonce($params->{'oauth_nonce'},$params->{'oauth_timestamp'},
+ $domlti{$itemid}{'lifetime'},$cdom,
+ $r->dir_config('lonLTIDir'))) {
+ &invalid_request($r,5);
+ return OK;
+ }
+ }
+ }
+ if ($itemid) {
+ foreach my $key (%{$params}) {
+ delete($env{'form.'.$key});
+ }
+ my $ltoken = &Apache::lonnet::tmpput({'linkprot' => $itemid.$ltitype.':'.$tail},
+ $lonhost);
+ if ($ltoken) {
+ $r->internal_redirect($tail.'?ltoken='.$ltoken);
+ $r->set_handlers('PerlHandler'=> undef);
+ } else {
+ &invalid_request($r,6);
+ }
+ } else {
+ &invalid_request($r,7);
+ }
+ } else {
+ &invalid_request($r,8);
+ }
+ } else {
+ &invalid_request($r,9);
+ }
+ } else {
+ &invalid_request($r,10);
+ }
+ } else {
+ &invalid_request($r,11);
+ }
+ return OK;
+ }
+
+ my ($udom,$uname,$uhome,$symb,$mapurl);
#
# For user who launched LTI in Consumer, determine user's domain in
@@ -198,7 +318,7 @@
if ($tail =~ m{^/uploaded/($match_domain)/($match_courseid)/(?:default|supplemental)(?:|_\d+)\.(?:sequence|page)(|___\d+___.+)$}) {
($urlcdom,$urlcnum,my $rest) = ($1,$2,$3);
if (($cdom ne '') && ($cdom ne $urlcdom)) {
- &invalid_request($r,3);
+ &invalid_request($r,12);
return OK;
}
if ($rest eq '') {
@@ -217,30 +337,15 @@
} elsif ($tail =~ m{^/($match_domain)/($match_courseid)$}) {
($urlcdom,$urlcnum) = ($1,$2);
if (($cdom ne '') && ($cdom ne $urlcdom)) {
- &invalid_request($r,4);
+ &invalid_request($r,13);
return OK;
}
} elsif ($tail =~ m{^/tiny/($match_domain)/(\w+)$}) {
- ($urlcdom,my $key) = ($1,$2);
- if (($cdom ne '') && ($cdom ne $urlcdom)) {
- &invalid_request($r,5);
+ ($urlcdom,$urlcnum) = &course_from_tinyurl($tail);
+ if (($urlcdom eq '') || ($urlcnum eq '')) {
+ &invalid_request($r,14);
return OK;
}
- my $tinyurl;
- my ($result,$cached)=&Apache::lonnet::is_cached_new('tiny',$urlcdom."\0".$key);
- if (defined($cached)) {
- $tinyurl = $result;
- } else {
- my $configuname = &Apache::lonnet::get_domainconfiguser($urlcdom);
- my %currtiny = &Apache::lonnet::get('tiny',[$key],$urlcdom,$configuname);
- if ($currtiny{$key} ne '') {
- $tinyurl = $currtiny{$key};
- &Apache::lonnet::do_cache_new('tiny',$urlcdom."\0".$key,$currtiny{$key},600);
- }
- }
- if ($tinyurl ne '') {
- $urlcnum = (split(/\&/,$tinyurl))[0];
- }
}
if (($cdom eq '') && ($urlcdom ne '')) {
my $cprimary_id = &Apache::lonnet::domain($urlcdom,'primary');
@@ -263,14 +368,14 @@
}
#
-# Retrieve information for LTI Consumers in course domain
+# Retrieve information for LTI Consumers in course's domain
# and populate hash -- %lti_by_key -- for which keys
# are those defined in domain configuration for LTI.
#
my %lti = &Apache::lonnet::get_domain_lti($cdom,'provider');
unless (keys(%lti) > 0) {
- &invalid_request($r,6);
+ &invalid_request($r,15);
return OK;
}
my %lti_by_key;
@@ -286,37 +391,11 @@
#
# Verify the signed request using the secret for those
# Consumers for which the key in the POSTed data matches
-# keys in the domain configuration for LTI.
+# keys in the course configuration or the domain configuration
+# for LTI.
#
- my $hostname = $r->hostname;
- my $protocol = 'http';
- if ($ENV{'SERVER_PORT'} == 443) {
- $protocol = 'https';
- }
- if (exists($params->{'oauth_callback'})) {
- $Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0A;
- } else {
- $Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0;
- }
-
- my ($itemid,$consumer_key,$secret);
- $consumer_key = $params->{'oauth_consumer_key'};
- if (ref($lti_by_key{$consumer_key}) eq 'ARRAY') {
- foreach my $id (@{$lti_by_key{$consumer_key}}) {
- if (ref($lti{$id}) eq 'HASH') {
- $secret = $lti{$id}{'secret'};
- my $request = Net::OAuth->request('request token')->from_hash($params,
- request_url => $protocol.'://'.$hostname.$requri,
- request_method => $env{'request.method'},
- consumer_secret => $secret,);
- if ($request->verify()) {
- $itemid = $id;
- last;
- }
- }
- }
- }
+ my $itemid = &get_lti_itemid($requri,$hostname,$params,\%lti,\%lti_by_key);
#
# Request is invalid if the signed request could not be verified
@@ -324,7 +403,7 @@
# configuration in LON-CAPA for that LTI Consumer.
#
unless (($itemid) && (ref($lti{$itemid}) eq 'HASH')) {
- &invalid_request($r,7);
+ &invalid_request($r,16);
return OK;
}
@@ -334,7 +413,7 @@
#
unless (&LONCAPA::ltiutils::check_nonce($params->{'oauth_nonce'},$params->{'oauth_timestamp'},
$lti{$itemid}{'lifetime'},$cdom,$r->dir_config('lonLTIDir'))) {
- &invalid_request($r,8);
+ &invalid_request($r,17);
return OK;
}
@@ -345,19 +424,20 @@
if (!$lti{$itemid}{'requser'}) {
if ($tail =~ m{^/tiny/($match_domain)/(\w+)$}) {
+ my $ltitype = 'd';
foreach my $key (%{$params}) {
delete($env{'form.'.$key});
}
- my $ltoken = &Apache::lonnet::tmpput({'linkprot' => $itemid.':'.$tail},
+ my $ltoken = &Apache::lonnet::tmpput({'linkprot' => $itemid.$ltitype.':'.$tail},
$lonhost);
if ($ltoken) {
$r->internal_redirect($tail.'?ltoken='.$ltoken);
$r->set_handlers('PerlHandler'=> undef);
} else {
- &invalid_request($r,9);
+ &invalid_request($r,18);
}
} else {
- &invalid_request($r,10);
+ &invalid_request($r,19);
}
return OK;
}
@@ -418,7 +498,7 @@
if ($consumers{$sourcecrs} =~ /^$match_courseid$/) {
my $crshome = &Apache::lonnet::homeserver($consumers{$sourcecrs},$cdom);
if ($crshome =~ /(con_lost|no_host|no_such_host)/) {
- &invalid_request($r,11);
+ &invalid_request($r,20);
return OK;
} else {
$posscnum = $consumers{$sourcecrs};
@@ -430,7 +510,7 @@
if ($urlcnum ne '') {
if ($posscnum ne '') {
if ($posscnum ne $urlcnum) {
- &invalid_request($r,12);
+ &invalid_request($r,21);
return OK;
} else {
$cnum = $posscnum;
@@ -438,7 +518,7 @@
} else {
my $crshome = &Apache::lonnet::homeserver($urlcnum,$cdom);
if ($crshome =~ /(con_lost|no_host|no_such_host)/) {
- &invalid_request($r,13);
+ &invalid_request($r,22);
return OK;
} else {
$cnum = $urlcnum;
@@ -503,7 +583,7 @@
$domdesc,\%data,\%alerts,\%rulematch,
\%inst_results,\%curr_rules,%got_rules);
if ($result eq 'notallowed') {
- &invalid_request($r,14);
+ &invalid_request($r,23);
} elsif ($result eq 'ok') {
if (($ltiroles[0] eq 'Instructor') && ($lcroles[0] eq 'cc') && ($lti{$itemid}{'mapcrs'}) &&
($lti{$itemid}{'makecrs'})) {
@@ -512,16 +592,16 @@
}
}
} else {
- &invalid_request($r,15);
+ &invalid_request($r,24);
return OK;
}
} else {
- &invalid_request($r,16);
+ &invalid_request($r,25);
return OK;
}
}
} else {
- &invalid_request($r,17);
+ &invalid_request($r,26);
return OK;
}
@@ -543,10 +623,10 @@
$symb,$cdom,$cnum,$params,\@ltiroles,$lti{$itemid},\@lcroles,
$reqcrs,$sourcecrs);
} else {
- &invalid_request($r,18);
+ &invalid_request($r,27);
}
} else {
- &invalid_request($r,19);
+ &invalid_request($r,28);
}
return OK;
}
@@ -632,7 +712,7 @@
}
}
if ($reqrole eq '') {
- &invalid_request($r,20);
+ &invalid_request($r,29);
return OK;
} else {
unless (%crsenv) {
@@ -642,10 +722,10 @@
my $default_enrollment_end_date = $crsenv{'default_enrollment_end_date'};
my $now = time;
if ($default_enrollment_end_date && $default_enrollment_end_date <= $now) {
- &invalid_request($r,21);
+ &invalid_request($r,30);
return OK;
} elsif ($default_enrollment_start_date && $default_enrollment_start_date >$now) {
- &invalid_request($r,22);
+ &invalid_request($r,31);
return OK;
} else {
$selfenrollrole = $reqrole.'./'.$cdom.'/'.$cnum;
@@ -676,6 +756,41 @@
return OK;
}
+sub get_lti_itemid {
+ my ($requri,$hostname,$params,$lti,$lti_by_key) = @_;
+ return unless ((ref($params) eq 'HASH') && (ref($lti) eq 'HASH') && (ref($lti_by_key) eq 'HASH'));
+
+ if (exists($params->{'oauth_callback'})) {
+ $Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0A;
+ } else {
+ $Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0;
+ }
+
+ my $protocol = 'http';
+ if ($ENV{'SERVER_PORT'} == 443) {
+ $protocol = 'https';
+ }
+
+ my ($itemid,$consumer_key,$secret);
+ my $consumer_key = $params->{'oauth_consumer_key'};
+ if (ref($lti_by_key->{$consumer_key}) eq 'ARRAY') {
+ foreach my $id (@{$lti_by_key->{$consumer_key}}) {
+ if (ref($lti->{$id}) eq 'HASH') {
+ $secret = $lti->{$id}{'secret'};
+ my $request = Net::OAuth->request('request token')->from_hash($params,
+ request_url => $protocol.'://'.$hostname.$requri,
+ request_method => $env{'request.method'},
+ consumer_secret => $secret,);
+ if ($request->verify()) {
+ $itemid = $id;
+ last;
+ }
+ }
+ }
+ }
+ return $itemid;
+}
+
sub lti_enroll {
my ($uname,$udom,$selfenrollrole) = @_;
my $enrollresult;
@@ -917,4 +1032,28 @@
return;
}
+sub course_from_tinyurl {
+ my ($tail) = @_;
+ my ($urlcdom,$urlcnum);
+ if ($tail =~ m{^/tiny/($match_domain)/(\w+)$}) {
+ my ($urlcdom,$key) = ($1,$2);
+ my $tinyurl;
+ my ($result,$cached)=&Apache::lonnet::is_cached_new('tiny',$urlcdom."\0".$key);
+ if (defined($cached)) {
+ $tinyurl = $result;
+ } else {
+ my $configuname = &Apache::lonnet::get_domainconfiguser($urlcdom);
+ my %currtiny = &Apache::lonnet::get('tiny',[$key],$urlcdom,$configuname);
+ if ($currtiny{$key} ne '') {
+ $tinyurl = $currtiny{$key};
+ &Apache::lonnet::do_cache_new('tiny',$urlcdom."\0".$key,$currtiny{$key},600);
+ }
+ }
+ if ($tinyurl ne '') {
+ $urlcnum = (split(/\&/,$tinyurl))[0];
+ }
+ }
+ return ($urlcdom,$urlcnum);
+}
+
1;
Index: rat/client/parameter.html
diff -u rat/client/parameter.html:1.85 rat/client/parameter.html:1.86
--- rat/client/parameter.html:1.85 Wed Dec 23 21:20:36 2020
+++ rat/client/parameter.html Wed Aug 4 19:59:12 2021
@@ -5,7 +5,7 @@
The LearningOnline Network with CAPA
Parameter Input Window
//
-// $Id: parameter.html,v 1.85 2020/12/23 21:20:36 raeburn Exp $
+// $Id: parameter.html,v 1.86 2021/08/04 19:59:12 raeburn Exp $
//
// Copyright Michigan State University Board of Trustees
//
@@ -416,15 +416,17 @@
function validateDeepLink() {
var sform=choices.document.forms.sch;
- svalue = sform.deeplinklisted.options[sform.deeplinklisted.selectedIndex].value+',';
+ svalue = sform.deeplinkstate.options[sform.deeplinkstate.selectedIndex].value+',';
+ svalue += sform.deeplinkothers.options[sform.deeplinkothers.selectedIndex].value+',';
+ svalue += sform.deeplinklisted.options[sform.deeplinklisted.selectedIndex].value+',';
svalue += sform.deeplinkacc.options[sform.deeplinkacc.selectedIndex].value+',';
var keyRegExp = /^[a-zA-Z\d_.!@#$%^&*()+=-]+$/;
var numRegExp = /^\d+$/;
- if (sform.deeplinktypes.length) {
- for (var i=0; i<sform.deeplinktypes.length; i++) {
- if (sform.deeplinktypes[i].checked) {
- svalue += sform.deeplinktypes[i].value;
- if (sform.deeplinktypes[i].value == 'key') {
+ if (sform.deeplinkprotect.length) {
+ for (var i=0; i<sform.deeplinkprotect.length; i++) {
+ if (sform.deeplinkprotect[i].checked) {
+ svalue += sform.deeplinkprotect[i].value;
+ if (sform.deeplinkprotect[i].value == 'key') {
var posskey = sform.deeplinkkey.value;
posskey = posskey.replace(/^\s+|\s+$/g,'');
if (keyRegExp.test(posskey)) {
@@ -434,10 +436,18 @@
'or choose a different supported link type.');
return;
}
- } else if (sform.deeplinktypes[i].value == 'lti') {
- var posslti = sform.linkposslti.options[sform.linkposslti.selectedIndex].value;
- if ((numRegExp.test(posslti)) && (posslti > 0)) {
- svalue += ':'+posslti;
+ } else if (sform.deeplinkprotect[i].value == 'ltic') {
+ var possltic = sform.linkpossltic.options[sform.linkpossltic.selectedIndex].value;
+ if ((numRegExp.test(possltic)) && (possltic > 0)) {
+ svalue += ':'+possltic;
+ } else {
+ alert('Please select an LTI launcher, or choose a different supported link type.');
+ return;
+ }
+ } else if (sform.deeplinkprotect[i].value == 'ltid') {
+ var possltid = sform.linkpossltid.options[sform.linkpossltid.selectedIndex].value;
+ if ((numRegExp.test(possltid)) && (possltid > 0)) {
+ svalue += ':'+possltid;
} else {
alert('Please select an LTI launcher, or choose a different supported link type.');
return;
@@ -471,27 +481,29 @@
function toggleDeepLink(caller) {
var sform=choices.document.forms.sch;
- if ((caller == 'types') && (sform.deeplinktypes.length)) {
+ if ((caller == 'protect') && (sform.deeplinkprotect.length)) {
var frame = window.frames["choices"];
- for (var i=0; i<sform.deeplinktypes.length; i++) {
- if (sform.deeplinktypes[i].checked) {
- if (sform.deeplinktypes[i].value == 'key') {
- if (frame.document.getElementById('deeplinkkey')) {
+ for (var i=0; i<sform.deeplinkprotect.length; i++) {
+ if (sform.deeplinkprotect[i].checked) {
+ if (frame.document.getElementById('deeplinkkey')) {
+ if (sform.deeplinkprotect[i].value == 'key') {
frame.document.getElementById('deeplinkkey').type='text';
- }
- if (frame.document.getElementById('deeplinkltidiv')) {
- frame.document.getElementById('deeplinkltidiv').style.display='none';
- }
- } else {
- if (frame.document.getElementById('deeplinkkey')) {
+ } else {
frame.document.getElementById('deeplinkkey').type='hidden';
}
- if (frame.document.getElementById('deeplinkltidiv')) {
- if (sform.deeplinktypes[i].value == 'lti') {
- frame.document.getElementById('deeplinkltidiv').style.display='block';
- } else {
- frame.document.getElementById('deeplinkltidiv').style.display='none';
- }
+ }
+ if (frame.document.getElementById('deeplinklticdiv')) {
+ if (sform.deeplinkprotect[i].value == 'ltic') {
+ frame.document.getElementById('deeplinklticdiv').style.display='block';
+ } else {
+ frame.document.getElementById('deeplinklticdiv').style.display='none';
+ }
+ }
+ if (frame.document.getElementById('deeplinkltiddiv')) {
+ if (sform.deeplinkprotect[i].value == 'ltid') {
+ frame.document.getElementById('deeplinkltiddiv').style.display='block';
+ } else {
+ frame.document.getElementById('deeplinkltiddiv').style.display='none';
}
}
break;
@@ -1167,30 +1179,41 @@
}
if (pscat=='deeplink') {
var deeplinkvals = new Array();
- var linktypeparts = new Array();
- var ltikeyRegExp = /^(lti|key):(\w+)$/;
+ var linkprotectparts = new Array();
+ var ltikeyRegExp = /^(ltic|ltid|key):(\w+)$/;
var dlinkkeysty = 'hidden';
var dlinkkeyval = '';
- var dlinkltidivsty = 'none';
+ var dlinklticdivsty = 'none';
+ var dlinkltiddivsty = 'none';
var dlinkmenusdivsty = 'none';
if ((svalue != '') && (svalue != null)) {
deeplinkvals = svalue.split(',');
- if (ltikeyRegExp.test(deeplinkvals[2])) {
- linktypeparts = deeplinkvals[2].split(':');
- deeplinkvals[2] = linktypeparts[0];
- if (linktypeparts[0] == 'key') {
+ if (ltikeyRegExp.test(deeplinkvals[4])) {
+ linkprotectparts = deeplinkvals[4].split(':');
+ deeplinkvals[4] = linkprotectparts[0];
+ if (linkprotectparts[0] == 'key') {
dlinkkeysty = 'text';
- dlinkkeyval = linktypeparts[1];
- } else if (linktypeparts[0] == 'lti') {
- dlinkltidivsty = 'block';
+ dlinkkeyval = linkprotectparts[1];
+ } else if (linkprotectparts[0] == 'ltic') {
+ dlinklticdivsty = 'block';
+ } else if (linkprotectparts[0] == 'ltid') {
+ dlinkltiddivsty = 'block';
}
}
- if (deeplinkvals[3] >= 1) {
+ if (deeplinkvals[5] >= 1) {
dlinkmenusdivsty = 'inline-block';
}
} else {
- deeplinkvals = ['full','res','any','0'];
+ deeplinkvals = ['off','unhide','full','res','','0'];
}
+ var deeplinkstate = new Array();
+ deeplinkstate = ['only','off','both'];
+ var deeplinkstatetxt = new Array();
+ deeplinkstatetxt = ['deep only','deeplink off','regular + deep'];
+ var deeplinkothers = new Array();
+ deeplinkothers = ['hide','unhide'];
+ var deeplinkotherstxt = new Array();
+ deeplinkotherstxt = ['Hidden','Unhidden'];
var deeplinklisting = new Array();
deeplinklisting = ['full','absent','grades','details','datestatus'];
var deeplinklisttxt = new Array();
@@ -1199,94 +1222,150 @@
deeplinkscopes = ['res','map','rec'];
var deeplinkscopetxt = new Array();
deeplinkscopetxt = ['resource only','enclosing map/folder','recursive map/folder'];
- var deeplinkurls = new Array();
- deeplinkurls = ['any','only','key','lti'];
+ var deeplinkprotect = new Array();
+ deeplinkprotect = ['none','key','ltic','ltid'];
tablestart('Deep-linked items');
+ choicewrite('<tr><td>Access status?</td><td>');
+ choicewrite('<select name="deeplinkstate">');
+ for (var i=0; i<deeplinkstate.length; i++) {
+ choicewrite('<option value="'+deeplinkstate[i]+'"');
+ if (deeplinkvals[0] == deeplinkstate[i]) {
+ choicewrite(' selected="selected"');
+ }
+ choicewrite('>'+deeplinkstatetxt[i]+'</option>');
+ }
+ choicewrite('</select></td></tr>');
+ choicewrite('<tr><td>Hide other resources?</td><td>');
+ choicewrite('<select name="deeplinkothers">');
+ for (var i=0; i<deeplinkothers.length; i++) {
+ choicewrite('<option value="'+deeplinkothers[i]+'"');
+ if (deeplinkvals[1] == deeplinkothers[i]) {
+ choicewrite(' selected="selected"');
+ }
+ choicewrite('>'+deeplinkotherstxt[i]+'</option>');
+ }
+ choicewrite('</select></td></tr>');
choicewrite('<tr><td>In Contents + Gradebook?</td><td>');
choicewrite('<select name="deeplinklisted">');
for (var i=0; i<deeplinklisting.length; i++) {
choicewrite('<option value="'+deeplinklisting[i]+'"');
- if (deeplinkvals[0] == deeplinklisting[i]) {
+ if (deeplinkvals[2] == deeplinklisting[i]) {
choicewrite(' selected="selected"');
}
choicewrite('>'+deeplinklisttxt[i]+'</option>');
}
choicewrite('</select></td></tr>');
- choicewrite('<tr><td>Access scope via deep-link</td><td>');
+ choicewrite('<tr><td>Access scope for link</td><td>');
choicewrite('<select name="deeplinkacc">');
for (var i=0; i<deeplinkscopes.length; i++) {
choicewrite('<option value="'+deeplinkscopes[i]+'"');
- if (deeplinkvals[1] == deeplinkscopes[i]) {
+ if (deeplinkvals[3] == deeplinkscopes[i]) {
choicewrite(' selected="selected"');
}
choicewrite('>'+deeplinkscopetxt[i]+'</option>');
}
choicewrite('</select></td></tr>');
- choicewrite('<tr><td>Supported Link Types</td><td>');
- choicewrite('<span style="white-space: nowrap;"><label>');
- choicewrite('<input name="deeplinktypes" value="any"'+
- ' type="radio" '+calldeeplink('types'));
- if (deeplinkvals[2]=='any') { choicewrite(' checked="checked"'); }
- choicewrite(' /> regular + deep</label></span><br />');
+ choicewrite('<tr><td>Link protection</td><td>');
choicewrite('<span style="white-space: nowrap;"><label>');
- choicewrite('<input name="deeplinktypes" value="only"'+
- ' type="radio" '+calldeeplink('types'));
- if (deeplinkvals[2]=='only') { choicewrite(' checked="checked"'); }
- choicewrite(' /> deep only</label></span><br />');
- choicewrite('<span style="white-space: nowrap;"><label>');
- choicewrite('<input name="deeplinktypes" value="key"'+
- ' type="radio" '+calldeeplink('types'));
- if (deeplinkvals[2]=='key') { choicewrite(' checked="checked"'); }
- choicewrite(' /> deep with key</label>');
+ choicewrite('<input name="deeplinkprotect" value="none"'+
+ ' type="radio" '+calldeeplink('protect'));
+ if (deeplinkvals[4]=='none') { choicewrite(' checked="checked"'); }
+ choicewrite(' /> not in use</label>');
+ choicewrite('<input name="deeplinkprotect" value="key"'+
+ ' type="radio" '+calldeeplink('protect'));
+ if (deeplinkvals[4]=='key') { choicewrite(' checked="checked"'); }
+ choicewrite(' /> key access</label>');
choicewrite('<input type="'+dlinkkeysty+'" name="deeplinkkey" id="deeplinkkey" value="'+dlinkkeyval+'" size="10" />');
choicewrite('</span><br />');
var possmenus = new Array();
if ((pextra != '') && (pextra != null)) {
- var ltiRegExp = /^lti_/;
+ var lticRegExp = /^ltic_/;
+ var ltidRegExp = /^ltid_/;
var menusRegExp = /^menus_/;
var extras = pextra.split('&');
for (var i=0; i<extras.length; i++) {
- if (ltiRegExp.test(extras[i])) {
- extras[i] = extras[i].replace(ltiRegExp,'');
- var posslti = extras[i].split(',');
- if (posslti.length >= 1) {
- var ltinums = new Array();
- var ltititles = new Array();
- for (var j=0; j<posslti.length; j++) {
- var entries = posslti[j].split(':');
- ltinums[j] = entries[0];
- ltititles[j] = decodeURIComponent(entries[1]);
+ if (lticRegExp.test(extras[i])) {
+ extras[i] = extras[i].replace(lticRegExp,'');
+ var possltic = extras[i].split(',');
+ if (possltic.length >= 1) {
+ var lticnums = new Array();
+ var ltictitles = new Array();
+ for (var j=0; j<possltic.length; j++) {
+ var entries = possltic[j].split(':');
+ lticnums[j] = entries[0];
+ ltictitles[j] = decodeURIComponent(entries[1]);
}
- if (ltinums.length) {
+ if (lticnums.length) {
choicewrite('<span style="white-space: nowrap;"><label>');
- choicewrite('<input name="deeplinktypes" value="lti"'+
- ' type="radio" '+calldeeplink('types'));
- if (deeplinkvals[2]=='lti') { choicewrite(' checked="checked"'); }
- choicewrite(' /> deep with LTI launch</label>');
- choicewrite('<div id="deeplinkltidiv" style="display:'+dlinkltidivsty+'">');
- choicewrite('<select name="linkposslti">');
+ choicewrite('<input name="deeplinkprotect" value="ltic"'+
+ ' type="radio" '+calldeeplink('protect'));
+ if (deeplinkvals[4]=='ltic') { choicewrite(' checked="checked"'); }
+ choicewrite(' /> course LTI launch</label>');
+ choicewrite('<div id="deeplinklticdiv" style="display:'+dlinklticdivsty+'">');
+ choicewrite('<select name="linkpossltic">');
var sel='';
- if (deeplinkvals[2]!='lti') {
+ if (deeplinkvals[4]!='ltic') {
sel = ' selected="selected"';
}
- if (ltinums.length > 1) {
+ if (lticnums.length > 1) {
choicewrite('<option value=""'+sel+'>Select</option>');
}
- for (var j=0; j<ltinums.length; j++) {
+ for (var j=0; j<lticnums.length; j++) {
sel = '';
- if (deeplinkvals[2]=='lti') {
- if (linktypeparts.length) {
- if (ltinums[j] == linktypeparts[1]) {
+ if (deeplinkvals[4]=='ltic') {
+ if (linkprotectparts.length) {
+ if (lticnums[j] == linkprotectparts[1]) {
sel = ' selected="selected"';
}
}
}
- choicewrite('<option value="'+ltinums[j]+'"'+sel+'>'+ltititles[j]+'</option>');
+ choicewrite('<option value="'+lticnums[j]+'"'+sel+'>'+ltictitles[j]+'</option>');
}
choicewrite('</select></div></span><br />');
}
}
+ } else if (ltidRegExp.test(extras[i])) {
+ extras[i] = extras[i].replace(ltidRegExp,'');
+ var possltid = extras[i].split(',');
+ if (possltid.length >= 1) {
+ var ltidnums = new Array();
+ var ltidtitles = new Array();
+ for (var j=0; j<possltid.length; j++) {
+ var entries = possltid[j].split(':');
+ ltidnums[j] = entries[0];
+ ltidtitles[j] = decodeURIComponent(entries[1]);
+ }
+ if (ltidnums.length) {
+ choicewrite('<span style="white-space: nowrap;"><label>');
+ choicewrite('<input name="deeplinkprotect" value="ltid"'+
+ ' type="radio" '+calldeeplink('protect'));
+ if (deeplinkvals[4]=='ltid') { choicewrite(' checked="checked"'); }
+ choicewrite(' />domain LTI launch</label>');
+ choicewrite('<div id="deeplinkltiddiv" style="display:'+dlinkltiddivsty+'">');
+ choicewrite('<select name="linkpossltid">');
+ var sel='';
+ if (deeplinkvals[4]!='ltid') {
+ sel = ' selected="selected"';
+ }
+ if (ltidnums.length > 1) {
+ choicewrite('<option value=""'+sel+'>Select</option>');
+ }
+ for (var j=0; j<ltidnums.length; j++) {
+ sel = '';
+ if (deeplinkvals[4]=='ltid') {
+ if (linkprotectparts.length) {
+ if (ltidnums[j] == linkprotectparts[1]) {
+ sel = ' selected="selected"';
+ }
+ }
+ }
+ choicewrite('<option value="'+ltidnums[j]+'"'+sel+'>'+ltidtitles[j]+'</option>');
+ }
+ choicewrite('</select></div></span><br />');
+ }
+ }
+
} else if (menusRegExp.test(extras[i])) {
extras[i] = extras[i].replace(menusRegExp,'');
possmenus = extras[i].split(',');
@@ -1297,7 +1376,7 @@
choicewrite('<span style="white-space: nowrap;"><label>');
choicewrite('<input name="deeplinkmenus" value="std"'+
' type="radio" '+calldeeplink('menus'));
- if (deeplinkvals[3] == 0) {
+ if (deeplinkvals[5] == 0) {
choicewrite(' checked="checked"');
}
choicewrite(' /> Standard (all menus)</label></span><br />');
@@ -1305,12 +1384,12 @@
choicewrite('<span style="white-space: nowrap;"><label>');
choicewrite('<input name="deeplinkmenus" value="collnum"'+
' type="radio" '+calldeeplink('menus'));
- if (deeplinkvals[3] > 0) { choicewrite(' checked="checked"'); }
+ if (deeplinkvals[5] > 0) { choicewrite(' checked="checked"'); }
choicewrite(' /> Numbered collection</label>');
choicewrite('<div id="deeplinkmenusdiv" style="display:'+dlinkmenusdivsty+'">');
choicewrite('<select name="linkpossmenu">');
var sel='';
- if (deeplinkvals[3] == 0) {
+ if (deeplinkvals[5] == 0) {
sel = ' selected="selected"';
}
if (possmenus.length > 1) {
@@ -1318,7 +1397,7 @@
}
for (var i=0; i<possmenus.length; i++) {
sel = '';
- if (deeplinkvals[3] == possmenus[i]) {
+ if (deeplinkvals[5] == possmenus[i]) {
sel = ' selected="selected"';
}
choicewrite('<option value="'+possmenus[i]+'"'+sel+'>'+possmenus[i]+'</option>');
More information about the LON-CAPA-cvs
mailing list