[LON-CAPA-cvs] cvs: loncom / Lond.pm lond /interface courseprefs.pm domainprefs.pm loncommon.pm londocs.pm lonextresedit.pm lonexttool.pm lonparmset.pm /lonnet/perl lonnet.pm
raeburn
raeburn at source.lon-capa.org
Mon May 22 17:10:56 EDT 2023
raeburn Mon May 22 21:10:56 2023 EDT
Modified files:
/loncom/interface courseprefs.pm domainprefs.pm loncommon.pm
londocs.pm lonextresedit.pm lonexttool.pm
lonparmset.pm
/loncom/lonnet/perl lonnet.pm
/loncom Lond.pm lond
Log:
- Bug 6754
- Can select from tool definitions set in course's domain or in course
itself when using External > "External Tool" in Course Editor.
- DC's configuration for allowed use of esternal tools within course types
(can be overridden for specific course(s)) determines which External Tool
types (if any) may be added to a course.
- Signing of LTI payload used to launch an external tool now carried out
on course's home server.
-------------- next part --------------
Index: loncom/interface/courseprefs.pm
diff -u loncom/interface/courseprefs.pm:1.119 loncom/interface/courseprefs.pm:1.120
--- loncom/interface/courseprefs.pm:1.119 Thu Apr 13 15:21:00 2023
+++ loncom/interface/courseprefs.pm Mon May 22 21:10:55 2023
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Handler to set configuration settings for a course
#
-# $Id: courseprefs.pm,v 1.119 2023/04/13 15:21:00 raeburn Exp $
+# $Id: courseprefs.pm,v 1.120 2023/05/22 21:10:55 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -1883,7 +1883,7 @@
my @allfields = ('fullname','firstname','lastname','email','user','roles');
map { $possfield{$_} = 1; } @allfields;
- my ($dest,$privnum,$cipher,$errors,%ltitools);
+ my ($dest,$privnum,$cipher);
($cipher,$privnum) = &get_credentials($cdom,$cnum,'ltitools',$context);
if ($context eq 'domain') {
@@ -3223,7 +3223,7 @@
&Apache::lonnet::devalidate_cache_new('courseltitools',$hashid);
unless (($home eq 'no_host') || ($home eq '')) {
if (grep(/^\Q$home\E$/, at ids)) {
- &Apache::lonnet::devalidate_cache_new('courseltitoolsenc',$hashid);
+ &Apache::lonnet::devalidate_cache_new('crsltitoolsenc',$hashid);
}
}
} else {
@@ -6310,8 +6310,35 @@
sub print_ltitools {
my ($cdom,$cnum,$settings,$rowtotal,$crstype,$noedit,$context) = @_;
my ($datatable,$disabled,$css_class,$dest);
- my %lt = <itools_names();
my $itemcount = 1;
+ unless ($context eq 'domain') {
+ my %tooltypes = &Apache::loncommon::usable_exttools();
+ unless ($tooltypes{'crs'}) {
+ my $showtype = 'course';
+ if ($crstype eq 'Community') {
+ $showtype = lc($crstype);
+ }
+ $css_class = $itemcount%2?' class="LC_odd_row"':'';
+ $datatable = '<tr '.$css_class.'><td colspan="2">'.
+ &mt("Definition of external tools is not enabled for this $showtype.").'<br />';
+ if ($tooltypes{'dom'}) {
+ $datatable .= &mt("Contact an administrator for the $showtype domain ([_1]) to request this feature be enabled.",
+ '<i>'.$cdom.'</i>').
+ '<br /><br />'.
+ &mt("Use of external tools defined at a domain level is enabled, so the $showtype editor can be used to add tool(s), if any have been defined.");
+ } else {
+ $datatable .= &mt("Use of external tools defined at a domain level is not enabled, either, for this $showtype.").
+ '<br /><br />'.
+ &mt("Contact an administrator for the $showtype domain ([_1]) to request changes.",
+ '<i>'.$cdom.'</i>');
+
+ }
+ $datatable .= '</tr>';
+ $itemcount ++;
+ return $datatable;
+ }
+ }
+ my %lt = <itools_names();
my $maxnum = 0;
my %ordered;
if (ref($settings) eq 'HASH') {
@@ -6393,7 +6420,7 @@
'<option value="HMAC-SHA1"'.$sigsel{'HMAC-SHA1'}.'>HMAC-SHA1</option>'.
'<option value="HMAC-SHA256"'.$sigsel{'HMAC-SHA256'}.'>HMAC-SHA256</option></select></span>'.
'<br /><br />'.
- '<span class="LC_nobreak">'.$lt{'url'}.':<input type="text" size="40" name="ltitools_url_'.$i.'"'.
+ '<span class="LC_nobreak">'.$lt{'url'}.':<input type="text" size="60" name="ltitools_url_'.$i.'"'.
' value="'.$url.'" /></span>'.
(' 'x2).
'<span class="LC_nobreak">'.$lt{'lifetime'}.':'.
@@ -6671,7 +6698,7 @@
'<option value="HMAC-SHA1" selected="selected">HMAC-SHA1</option>'.
'<option value="HMAC-SHA256">HMAC-SHA256</option></select></span>'.
'<br />'.
- '<span class="LC_nobreak">'.$lt{'url'}.':<input type="text" size="40" name="ltitools_add_url" value="" /></span> '."\n".
+ '<span class="LC_nobreak">'.$lt{'url'}.':<input type="text" size="60" name="ltitools_add_url" value="" /></span> '."\n".
(' 'x2).
'<span class="LC_nobreak">'.$lt{'lifetime'}.':<input type="text" size="5" name="ltitools_add_lifetime" value="300" /></span><br />';
if ($switchserver) {
@@ -8224,7 +8251,7 @@
my %servers = &Apache::lonnet::internet_dom_servers($cdom);
my %thismachine;
map { $thismachine{$_} = 1; } &Apache::lonnet::current_machine_ids();
- my @posscached = ('courselti');
+ my @posscached = ('courselti','courseltitools');
if (keys(%servers)) {
foreach my $server (keys(%servers)) {
next if ($thismachine{$server});
Index: loncom/interface/domainprefs.pm
diff -u loncom/interface/domainprefs.pm:1.422 loncom/interface/domainprefs.pm:1.423
--- loncom/interface/domainprefs.pm:1.422 Tue Apr 11 20:35:19 2023
+++ loncom/interface/domainprefs.pm Mon May 22 21:10:55 2023
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Handler to set domain-wide configuration settings
#
-# $Id: domainprefs.pm,v 1.422 2023/04/11 20:35:19 raeburn Exp $
+# $Id: domainprefs.pm,v 1.423 2023/05/22 21:10:55 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -227,10 +227,21 @@
&Apache::lonnet::get_dom('encconfig',['ltitools','lti','proctoring','linkprot'],$dom,undef,1);
if (ref($domconfig{'ltitools'}) eq 'HASH') {
if (ref($encconfig{'ltitools'}) eq 'HASH') {
+ my $is_home;
+ my $home = &Apache::lonnet::domain($dom,'primary');
+ unless (($home eq 'no_host') || ($home eq '')) {
+ my @ids=&Apache::lonnet::current_machine_ids();
+ if (grep(/^\Q$home\E$/, at ids)) {
+ $is_home = 1;
+ }
+ }
foreach my $id (keys(%{$domconfig{'ltitools'}})) {
if ((ref($domconfig{'ltitools'}{$id}) eq 'HASH') &&
(ref($encconfig{'ltitools'}{$id}) eq 'HASH')) {
$domconfig{'ltitools'}{$id}{'key'} = $encconfig{'ltitools'}{$id}{'key'};
+ if (($is_home) && ($phase eq 'process')) {
+ $domconfig{'ltitools'}{$id}{'secret'} = $encconfig{'ltitools'}{$id}{'secret'};
+ }
}
}
}
@@ -14245,15 +14256,22 @@
$action => { %newtoolsenc }
);
&Apache::lonnet::put_dom('encconfig',\%toolsenchash,$dom,undef,1);
+ my $cachetime = 24*60*60;
+ &Apache::lonnet::do_cache_new('ltitoolsenc',$dom,\%newtoolsenc,$cachetime);
&store_security($dom,'ltitools',\%secchanges,\%newkeyset,\%keystore,$lastactref);
}
$resulttext = &mt('Changes made:').'<ul>';
if (keys(%secchanges) > 0) {
- $resulttext .= <i_security_results('ltitools',\%secchanges,\%newtoolsec,\%newkeyset,\%keystore);
+ $resulttext .= <i_security_results($dom,'ltitools',\%secchanges,\%newtoolsec,\%newkeyset,\%keystore);
}
if (keys(%ltitoolschg) > 0) {
$resulttext .= $ltitoolsoutput;
}
+ my $cachetime = 24*60*60;
+ &Apache::lonnet::do_cache_new('ltitools',$dom,\%newltitools,$cachetime);
+ if (ref($lastactref) eq 'HASH') {
+ $lastactref->{'ltitools'} = 1;
+ }
} else {
$errors .= '<li><span class="LC_error">'.&mt('Failed to save changes').'</span></li>';
}
@@ -14382,10 +14400,13 @@
}
sub lti_security_results {
- my ($context,$secchanges,$newsec,$newkeyset,$keystore) = @_;
+ my ($dom,$context,$secchanges,$newsec,$newkeyset,$keystore) = @_;
my $output;
+ my %domdefaults = &Apache::lonnet::get_domain_defaults($dom);
+ my $needs_update;
foreach my $item (keys(%{$secchanges})) {
if ($item eq 'encrypt') {
+ $needs_update = 1;
my %encrypted;
if ($context eq 'lti') {
%encrypted = (
@@ -14417,12 +14438,29 @@
}
my @types= ('crs','dom');
if ($context eq 'lti') {
+ foreach my $type (@types) {
+ undef($domdefaults{'linkprotenc_'.$type});
+ }
push(@types,'consumers');
+ undef($domdefaults{'ltienc_consumers'});
+ } elsif ($context eq 'ltitools') {
+ foreach my $type (@types) {
+ undef($domdefaults{'toolenc_'.$type});
+ }
}
foreach my $type (@types) {
my $shown = $encrypted{$type}{'off'};
if (ref($newsec->{$item}) eq 'HASH') {
if ($newsec->{$item}{$type}) {
+ if ($context eq 'lti') {
+ if ($type eq 'consumers') {
+ $domdefaults{'ltienc_consumers'} = 1;
+ } else {
+ $domdefaults{'linkprotenc_'.$type} = 1;
+ }
+ } elsif ($context eq 'ltitools') {
+ $domdefaults{'toolenc_'.$type} = 1;
+ }
$shown = $encrypted{$type}{'on'};
}
}
@@ -14466,10 +14504,27 @@
$output .= '<li>'.&mt('[_1] set to none',$titles{'chars'}).'</li>';
}
} elsif ($item eq 'private') {
+ $needs_update = 1;
+ if ($context eq 'lti') {
+ undef($domdefaults{'ltiprivhosts'});
+ } elsif ($context eq 'ltitools') {
+ undef($domdefaults{'toolprivhosts'});
+ }
if (keys(%{$newkeyset})) {
+ my @privhosts;
foreach my $hostid (sort(keys(%{$newkeyset}))) {
if ($keystore->{$hostid} eq 'ok') {
$output .= '<li>'.&mt('Encryption key for storage of shared secrets saved for [_1]',$hostid).'</li>';
+ unless (grep(/^\Q$hostid\E$/, at privhosts)) {
+ push(@privhosts,$hostid);
+ }
+ }
+ }
+ if (@privhosts) {
+ if ($context eq 'lti') {
+ $domdefaults{'ltiprivhosts'} = \@privhosts;
+ } elsif ($context eq 'ltitools') {
+ $domdefaults{'toolprivhosts'} = \@privhosts;
}
}
}
@@ -14477,6 +14532,10 @@
next;
}
}
+ if ($needs_update) {
+ my $cachetime = 24*60*60;
+ &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime);
+ }
return $output;
}
@@ -15423,7 +15482,7 @@
}
$resulttext = &mt('Changes made:').'<ul>';
if (keys(%secchanges) > 0) {
- $resulttext .= <i_security_results('lti',\%secchanges,\%newltisec,\%newkeyset,\%keystore);
+ $resulttext .= <i_security_results($dom,'lti',\%secchanges,\%newltisec,\%newkeyset,\%keystore);
if (exists($secchanges{'linkprot'})) {
$resulttext .= $linkprotoutput;
}
Index: loncom/interface/loncommon.pm
diff -u loncom/interface/loncommon.pm:1.1404 loncom/interface/loncommon.pm:1.1405
--- loncom/interface/loncommon.pm:1.1404 Sun Apr 2 03:16:27 2023
+++ loncom/interface/loncommon.pm Mon May 22 21:10:55 2023
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# a pile of common routines
#
-# $Id: loncommon.pm,v 1.1404 2023/04/02 03:16:27 raeburn Exp $
+# $Id: loncommon.pm,v 1.1405 2023/05/22 21:10:55 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -9764,6 +9764,50 @@
}
}
+sub usable_exttools {
+ my %tooltypes;
+ if ($env{'request.course.id'}) {
+ if ($env{'course.'.$env{'request.course.id'}.'.internal.exttool'}) {
+ if ($env{'course.'.$env{'request.course.id'}.'.internal.exttool'} eq 'both') {
+ %tooltypes = (
+ crs => 1,
+ dom => 1,
+ );
+ } elsif ($env{'course.'.$env{'request.course.id'}.'.internal.exttool'} eq 'crs') {
+ $tooltypes{'crs'} = 1;
+ } elsif ($env{'course.'.$env{'request.course.id'}.'.internal.exttool'} eq 'dom') {
+ $tooltypes{'dom'} = 1;
+ }
+ } else {
+ my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+ my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+ my $crstype = lc($env{'course.'.$env{'request.course.id'}.'.type'});
+ if ($crstype eq '') {
+ $crstype = 'course';
+ }
+ if ($crstype eq 'course') {
+ if ($env{'course.'.$env{'request.course.id'}.'internal.coursecode'}) {
+ $crstype = 'official';
+ } elsif ($env{'course.'.$env{'request.course.id'}.'.internal.textbook'}) {
+ $crstype = 'textbook';
+ } elsif ($env{'course.'.$env{'request.course.id'}.'.internal.lti'}) {
+ $crstype = 'lti';
+ } else {
+ $crstype = 'unofficial';
+ }
+ }
+ my %domdefaults = &Apache::lonnet::get_domain_defaults($cdom);
+ if ($domdefaults{$crstype.'domexttool'}) {
+ $tooltypes{'dom'} = 1;
+ }
+ if ($domdefaults{$crstype.'exttool'}) {
+ $tooltypes{'crs'} = 1;
+ }
+ }
+ }
+ return %tooltypes;
+}
+
sub wishlist_window {
return(<<'ENDWISHLIST');
<script type="text/javascript">
Index: loncom/interface/londocs.pm
diff -u loncom/interface/londocs.pm:1.698 loncom/interface/londocs.pm:1.699
--- loncom/interface/londocs.pm:1.698 Mon Mar 27 18:41:04 2023
+++ loncom/interface/londocs.pm Mon May 22 21:10:55 2023
@@ -1,7 +1,7 @@
# The LearningOnline Network
# Documents
#
-# $Id: londocs.pm,v 1.698 2023/03/27 18:41:04 raeburn Exp $
+# $Id: londocs.pm,v 1.699 2023/05/22 21:10:55 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -719,7 +719,7 @@
$url = $1;
my $marker = $2;
my $info = $3;
- my ($toolid,%toolhash,%toolsettings);
+ my ($toolid,$toolprefix,$tooltype,%toolhash,%toolsettings);
my @extras = ('linktext','explanation','crslabel','crstitle','crsappend');
my @toolinfo = split(/:/,$info);
if ($residx) {
@@ -728,6 +728,12 @@
} else {
$toolid = shift(@toolinfo);
}
+ if ($toolid =~ /^c/) {
+ $tooltype = 'crs';
+ $toolprefix = 'c';
+ } else {
+ $tooltype = 'dom';
+ }
$toolid =~ s/\D//g;
($toolhash{'target'},$toolhash{'width'},$toolhash{'height'},
$toolhash{'linktext'},$toolhash{'explanation'},$toolhash{'crslabel'},
@@ -741,127 +747,130 @@
$toolhash{'gradable'} =~ s/\D+//g;
}
if (ref($ltitoolsref) eq 'HASH') {
- if (ref($ltitoolsref->{$toolid}) eq 'HASH') {
- my @deleted;
- $toolhash{'id'} = $toolid;
- if (($toolhash{'target'} eq 'iframe') || ($toolhash{'target'} eq 'tab') ||
- ($toolhash{'target'} eq 'window')) {
- if ($toolhash{'target'} eq 'window') {
- foreach my $item ('width','height') {
- $toolhash{$item} =~ s/^\s+//;
- $toolhash{$item} =~ s/\s+$//;
- if ($toolhash{$item} =~ /\D/) {
- delete($toolhash{$item});
- if ($residx) {
- if ($toolsettings{$item}) {
- push(@deleted,$item);
+ if (ref($ltitoolsref->{$tooltype}) eq 'HASH') {
+ if (ref($ltitoolsref->{$tooltype}->{$toolid}) eq 'HASH') {
+ my %tools = %{$ltitoolsref->{$tooltype}->{$toolid}};
+ my @deleted;
+ $toolhash{'id'} = $toolprefix.$toolid;
+ if (($toolhash{'target'} eq 'iframe') || ($toolhash{'target'} eq 'tab') ||
+ ($toolhash{'target'} eq 'window')) {
+ if ($toolhash{'target'} eq 'window') {
+ foreach my $item ('width','height') {
+ $toolhash{$item} =~ s/^\s+//;
+ $toolhash{$item} =~ s/\s+$//;
+ if ($toolhash{$item} =~ /\D/) {
+ delete($toolhash{$item});
+ if ($residx) {
+ if ($toolsettings{$item}) {
+ push(@deleted,$item);
+ }
}
}
}
}
- }
- } elsif ($residx) {
- $toolhash{'target'} = $toolsettings{'target'};
- if ($toolhash{'target'} eq 'window') {
- foreach my $item ('width','height') {
- $toolhash{$item} = $toolsettings{$item};
+ } elsif ($residx) {
+ $toolhash{'target'} = $toolsettings{'target'};
+ if ($toolhash{'target'} eq 'window') {
+ foreach my $item ('width','height') {
+ $toolhash{$item} = $toolsettings{$item};
+ }
+ }
+ } elsif (ref($tools{'display'}) eq 'HASH') {
+ $toolhash{'target'} = $tools{'display'}{'target'};
+ if ($toolhash{'target'} eq 'window') {
+ $toolhash{'width'} = $tools{'display'}{'width'};
+ $toolhash{'height'} = $tools{'display'}{'height'};
}
}
- } elsif (ref($ltitoolsref->{$toolid}->{'display'}) eq 'HASH') {
- $toolhash{'target'} = $ltitoolsref->{$toolid}->{'display'}->{'target'};
- if ($toolhash{'target'} eq 'window') {
- $toolhash{'width'} = $ltitoolsref->{$toolid}->{'display'}->{'width'};
- $toolhash{'height'} = $ltitoolsref->{$toolid}->{'display'}->{'height'};
- }
- }
- if ($toolhash{'target'} eq 'iframe') {
- foreach my $item ('width','height','linktext','explanation') {
- delete($toolhash{$item});
- if ($residx) {
- if ($toolsettings{$item}) {
- push(@deleted,$item);
+ if ($toolhash{'target'} eq 'iframe') {
+ foreach my $item ('width','height','linktext','explanation') {
+ delete($toolhash{$item});
+ if ($residx) {
+ if ($toolsettings{$item}) {
+ push(@deleted,$item);
+ }
}
}
- }
- } elsif ($toolhash{'target'} eq 'tab') {
- foreach my $item ('width','height') {
- delete($toolhash{$item});
- if ($residx) {
- if ($toolsettings{$item}) {
- push(@deleted,$item);
+ } elsif ($toolhash{'target'} eq 'tab') {
+ foreach my $item ('width','height') {
+ delete($toolhash{$item});
+ if ($residx) {
+ if ($toolsettings{$item}) {
+ push(@deleted,$item);
+ }
}
}
}
- }
- if (ref($ltitoolsref->{$toolid}->{'crsconf'}) eq 'HASH') {
- foreach my $item ('label','title','linktext','explanation') {
- my $crsitem;
- if (($item eq 'label') || ($item eq 'title')) {
- $crsitem = 'crs'.$item;
- } else {
- $crsitem = $item;
- }
- if ($ltitoolsref->{$toolid}->{'crsconf'}->{$item}) {
- $toolhash{$crsitem} =~ s/^\s+//;
- $toolhash{$crsitem} =~ s/\s+$//;
- if ($toolhash{$crsitem} eq '') {
+ if (ref($tools{'crsconf'}) eq 'HASH') {
+ foreach my $item ('label','title','linktext','explanation') {
+ my $crsitem;
+ if (($item eq 'label') || ($item eq 'title')) {
+ $crsitem = 'crs'.$item;
+ } else {
+ $crsitem = $item;
+ }
+ if ($tools{'crsconf'}{$item}) {
+ $toolhash{$crsitem} =~ s/^\s+//;
+ $toolhash{$crsitem} =~ s/\s+$//;
+ if ($toolhash{$crsitem} eq '') {
+ delete($toolhash{$crsitem});
+ }
+ } else {
delete($toolhash{$crsitem});
}
- } else {
- delete($toolhash{$crsitem});
- }
- if (($residx) && (exists($toolsettings{$crsitem}))) {
- unless (exists($toolhash{$crsitem})) {
- push(@deleted,$crsitem);
+ if (($residx) && (exists($toolsettings{$crsitem}))) {
+ unless (exists($toolhash{$crsitem})) {
+ push(@deleted,$crsitem);
+ }
}
}
}
- }
- if ($toolhash{'passback'}) {
- my $gradesecret = UUID::Tiny::create_uuid_as_string(UUID_V4);
- $toolhash{'gradesecret'} = $gradesecret;
- $toolhash{'gradesecretdate'} = time;
- }
- if ($toolhash{'roster'}) {
- my $rostersecret = UUID::Tiny::create_uuid_as_string(UUID_V4);
- $toolhash{'rostersecret'} = $rostersecret;
- $toolhash{'rostersecretdate'} = time;
- }
- my $changegradable;
- if (($residx) && ($folder =~ /^default/)) {
- if ($toolsettings{'gradable'}) {
- unless (($toolhash{'gradable'}) || (defined($LONCAPA::map::zombies[$residx]))) {
- push(@deleted,'gradable');
- $changegradable = 1;
- }
- } elsif ($toolhash{'gradable'}) {
- $changegradable = 1;
+ if ($toolhash{'passback'}) {
+ my $gradesecret = UUID::Tiny::create_uuid_as_string(UUID_V4);
+ $toolhash{'gradesecret'} = $gradesecret;
+ $toolhash{'gradesecretdate'} = time;
+ }
+ if ($toolhash{'roster'}) {
+ my $rostersecret = UUID::Tiny::create_uuid_as_string(UUID_V4);
+ $toolhash{'rostersecret'} = $rostersecret;
+ $toolhash{'rostersecretdate'} = time;
}
- if (($caller eq 'londocs') && (defined($LONCAPA::map::zombies[$residx]))) {
- $changegradable = 1;
+ my $changegradable;
+ if (($residx) && ($folder =~ /^default/)) {
if ($toolsettings{'gradable'}) {
- $toolhash{'gradable'} = 1;
+ unless (($toolhash{'gradable'}) || (defined($LONCAPA::map::zombies[$residx]))) {
+ push(@deleted,'gradable');
+ $changegradable = 1;
+ }
+ } elsif ($toolhash{'gradable'}) {
+ $changegradable = 1;
+ }
+ if (($caller eq 'londocs') && (defined($LONCAPA::map::zombies[$residx]))) {
+ $changegradable = 1;
+ if ($toolsettings{'gradable'}) {
+ $toolhash{'gradable'} = 1;
+ }
}
}
- }
- my $putres = &Apache::lonnet::put('exttool_'.$marker,\%toolhash,$coursedom,$coursenum);
- if ($putres eq 'ok') {
- if (@deleted) {
- &Apache::lonnet::del('exttool_'.$marker,\@deleted,$coursedom,$coursenum);
- }
- if (($changegradable) && ($folder =~ /^default/)) {
- my $val;
- if ($toolhash{'gradable'}) {
- $val = 'yes';
- } else {
- $val = 'no';
+ my $putres = &Apache::lonnet::put('exttool_'.$marker,\%toolhash,$coursedom,$coursenum);
+ if ($putres eq 'ok') {
+ if (@deleted) {
+ &Apache::lonnet::del('exttool_'.$marker,\@deleted,$coursedom,$coursenum);
}
- &LONCAPA::map::storeparameter($residx,'parameter_0_gradable',$val,
- 'string_yesno');
- &remember_parms($residx,'gradable','set',$val);
+ if (($changegradable) && ($folder =~ /^default/)) {
+ my $val;
+ if ($toolhash{'gradable'}) {
+ $val = 'yes';
+ } else {
+ $val = 'no';
+ }
+ &LONCAPA::map::storeparameter($residx,'parameter_0_gradable',$val,
+ 'string_yesno');
+ &remember_parms($residx,'gradable','set',$val);
+ }
+ } else {
+ return (&mt('Failed to save update to external tool.'),1);
}
- } else {
- return (&mt('Failed to save update to external tool.'),1);
}
}
}
@@ -5725,6 +5734,7 @@
my $containertag;
my $pathitem;
my %ltitools;
+ my $posslti;
my $hiddentop;
my $navmap;
my $filterFunc = sub { my $res = shift; return (!$res->randomout() && !$res->is_map()) };
@@ -5958,8 +5968,19 @@
}
}
my $tabidstr = join("','", at tabids);
- %ltitools = &Apache::lonnet::get_domain_lti($coursedom,'consumer');
- my $posslti = keys(%ltitools);
+ my (%domtools,%crstools);
+ my %tooltypes = &Apache::loncommon::usable_exttools();
+ if ($tooltypes{'dom'}) {
+ %domtools = &Apache::lonnet::get_domain_lti($coursedom,'consumer');
+ }
+ if ($tooltypes{'crs'}) {
+ %crstools = &Apache::lonnet::get_course_lti($coursenum,$coursedom,'consumer');
+ }
+ %ltitools = (
+ dom => \%domtools,
+ crs => \%crstools,
+ );
+ $posslti = scalar(keys(%domtools)) + scalar(keys(%crstools));
my $hostname = $r->hostname();
$script .= &editing_js($udom,$uname,$supplementalflag,$coursedom,$coursenum,$posslti,
$londocroot,$canedit,$hostname,\$navmap).
@@ -6316,7 +6337,7 @@
<input type="hidden" name="active" value="bb" />
$pickfile
<p>
- $lt{'title'}: <input type="textbox" name="crsrestitle" value="" $disabled />
+ $lt{'title'}: <input type="text" name="crsrestitle" value="" $disabled />
</p>
<input type="hidden" name="importdetail" value="" />
<input type="submit" name="crsres" value="$lt{'impo'}" $disabled /><br />
@@ -6660,6 +6681,7 @@
<label><input type="radio" name="newsubdir" value="1" onclick="toggleNewsubdir(this.form);" $disabled />Yes</label>
</span><span id="newsubdir"></span>
<input type="hidden" name="newsubdirname" id="newsubdirname" value="" autocomplete="off" />
+ </div>
</p>
$lt{'fnam'}
<input type="text" size="20" name="newresourcename" autocomplete="off" $disabled />
@@ -6770,7 +6792,7 @@
my @external = (
{'<img class="LC_noBorder LC_middle" src="/res/adm/pages/extres.png" alt="'.$lt{extr}.'" onclick="toggleExternal(\'ext\');" />'=>$extresourcesform}
);
- if (keys(%ltitools)) {
+ if ($posslti) {
push(@external,
{'<img class="LC_noBorder LC_middle" src="/res/adm/pages/exttool.png" alt="'.$lt{extt}.'" onclick="toggleExternal(\'tool\');" />'=>$exttoolform},
);
@@ -6953,7 +6975,7 @@
my @supexternal = (
{'<img class="LC_noBorder LC_middle" src="/res/adm/pages/extres.png" alt="'.$lt{extr}.'" onclick="javascript:toggleExternal(\'suppext\')" />'
=>$supextform});
- if (keys(%ltitools)) {
+ if ($posslti) {
push(@supexternal,
{'<img class="LC_noBorder LC_middle" src="/res/adm/pages/exttool.png" alt="'.$lt{extt}.'" onclick="javascript:toggleExternal(\'supptool\')" />'
=>$supexttoolform});
Index: loncom/interface/lonextresedit.pm
diff -u loncom/interface/lonextresedit.pm:1.30 loncom/interface/lonextresedit.pm:1.31
--- loncom/interface/lonextresedit.pm:1.30 Fri Sep 9 14:24:30 2022
+++ loncom/interface/lonextresedit.pm Mon May 22 21:10:55 2023
@@ -1,7 +1,7 @@
# The LearningOnline Network
# Documents
#
-# $Id: lonextresedit.pm,v 1.30 2022/09/09 14:24:30 raeburn Exp $
+# $Id: lonextresedit.pm,v 1.31 2023/05/22 21:10:55 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -98,7 +98,18 @@
}
my %ltitools;
if ($type eq 'tool') {
- %ltitools = &Apache::lonnet::get_domain_lti($cdom,'consumer');
+ my (%domtools,%crstools);
+ my %tooltypes = &Apache::loncommon::usable_exttools();
+ if ($tooltypes{'dom'}) {
+ %domtools = &Apache::lonnet::get_domain_lti($cdom,'consumer');
+ }
+ if ($tooltypes{'crs'}) {
+ %crstools = &Apache::lonnet::get_course_lti($cnum,$cdom,'consumer');
+ }
+ %ltitools = (
+ dom => \%domtools,
+ crs => \%crstools,
+ );
}
my $js = &Apache::lonhtmlcommon::scripttag(&extedit_javascript());
my $pathitem = '<input type="hidden" name="folderpath" value="'.
@@ -375,15 +386,17 @@
ti => 'Title',
al => 'Add Link',
at => 'Add Tool',
+ dd => 'Defined in domain',
+ dc => 'Defined in course',
);
my $tabid = 'aa';
my $size = 60;
if ($supplementalflag) {
$tabid = 'ee';
}
- my ($formname,$formid,$toggle,$fieldsetid,$urlid,$dispdivstyle,$dimendivstyle,
+ my ($formname,$formid,$toggle,$fieldsetid,$urlid,$subdivid,$dispdivstyle,$dimendivstyle,
$windivstyle,$linktextstyle,$explanationstyle,$labelstyle,$titlestyle,
- $appendstyle,$gradablestyle,$legend,$urlelem,$toolelem,%toolattr);
+ $appendstyle,$gradablestyle,$subdivstyle,$legend,$urlelem,$toolelem,%toolattr);
$formname = 'new'.$type;
$toggle = $type;
$fieldsetid = 'external'.$type.'form';
@@ -402,6 +415,7 @@
$titlestyle = 'display:none';
$appendstyle = 'display:none';
$gradablestyle = 'display:none';
+ $subdivstyle = 'display:block';
if ($supplementalflag) {
$formname = 'newsupp'.$type;
$toggle = 'supp'.$type;
@@ -490,95 +504,157 @@
if ($orig_url =~ m{^/adm/$cdom/$cnum/(\d+)/ext\.tool$}) {
my $marker = $1;
my %toolhash=&Apache::lonnet::dump('exttool_'.$marker,$cdom,$cnum);
- if ($toolhash{'id'}) {
+ my ($tooltype,$tool,$ltihash);
+ if ($toolhash{'id'} =~/^c(\d+)$/) {
+ $tool = $1;
+ $tooltype = 'crs';
if (ref($ltitools) eq 'HASH') {
- if (keys(%{$ltitools})) {
- if (ref($ltitools->{$toolhash{'id'}}) eq 'HASH') {
- my $tooltitle = $ltitools->{$toolhash{'id'}}->{'title'};
- my $icon = $ltitools->{$toolhash{'id'}}->{'image'};
- my $image;
- if ($icon) {
- $image = '<img src="'.$icon.'" alt="'.$tooltitle.'" />';
- }
- if ($ltitools->{$toolhash{'id'}}->{'url'} =~ m{://}) {
- (my $prot,my $host,$providerurl) = ($ltitools->{$toolhash{'id'}}->{'url'} =~ m{^([^/]+)://([^/]+)(|/.+)$});
- } else {
- $providerurl = $ltitools->{$toolhash{'id'}}->{'url'};
- }
- $tooltarget = $toolhash{'target'};
- if ($tooltarget eq 'window') {
- $dimendivstyle = 'display:block';
- $windivstyle = 'display:block';
- $chkstate{'window'} = 'checked="checked" ';
- } elsif ($tooltarget eq 'tab') {
- $windivstyle = 'display:block';
- $chkstate{'tab'} = 'checked="checked" ';
- } else {
- $chkstate{'iframe'} = 'checked="checked" ';
- }
- $width = $toolhash{'width'};
- $height = $toolhash{'height'};
- $linktext = $toolhash{'linktext'};
- $explanation = $toolhash{'explanation'};
- if ($toolhash{'gradable'}) {
- $chkgrd = ' checked="checked"';
- } else {
- $chknogrd = ' checked="checked"';
- }
- if (ref($ltitools->{$toolhash{'id'}}->{'crsconf'}) eq 'HASH') {
- if ($ltitools->{$toolhash{'id'}}->{'crsconf'}->{'title'}) {
- $crstitle = $toolhash{'crstitle'};
- $titlestyle = 'display:inline';
- }
- if ($ltitools->{$toolhash{'id'}}->{'crsconf'}->{'label'}) {
- $crslabel = $toolhash{'crslabel'};
- $labelstyle = 'display:inline';
- }
- if ($ltitools->{$toolhash{'id'}}->{'crsconf'}->{'append'}) {
- $crsappend = $toolhash{'crsappend'};
- $appendstyle = 'display:inline';
- }
- if ($ltitools->{$toolhash{'id'}}->{'crsconf'}->{'target'}) {
- $dispdivstyle = 'display:block';
- }
- if ($ltitools->{$toolhash{'id'}}->{'crsconf'}->{'linktext'}) {
- $linktextstyle = 'padding:0;display:inline';
- }
- if ($ltitools->{$toolhash{'id'}}->{'crsconf'}->{'explanation'}) {
- $explanationstyle = 'padding:0;display:inline';
- }
- }
- $toolelem = '<span class="LC_nobreak">'.$image.' '.$tooltitle.'</span><br />';
- $gradablestyle = 'display:inline';
- }
+ if (ref($ltitools->{'crs'}) eq 'HASH') {
+ $ltihash = $ltitools->{'crs'}->{$tool};
+ }
+ }
+ } elsif ($toolhash{'id'} =~/^\d+$/) {
+ $tooltype = 'dom';
+ $tool = $toolhash{'id'};
+ if (ref($ltitools) eq 'HASH') {
+ if (ref($ltitools->{'dom'}) eq 'HASH') {
+ $ltihash = $ltitools->{'dom'}->{$tool};
+ }
+ }
+ }
+ if (($tool ne '') && (ref($ltihash) eq 'HASH')) {
+ my $tooltitle = $ltihash->{'title'};
+ my $icon = $ltihash->{'image'};
+ my $image;
+ if ($icon) {
+ $image = '<img src="'.$icon.'" alt="'.$tooltitle.'" />';
+ }
+ if ($ltihash->{'url'} =~ m{://}) {
+ (my $prot,my $host,$providerurl) = ($ltihash->{'url'} =~ m{^([^/]+)://([^/]+)(|/.+)$});
+ } else {
+ $providerurl = $ltihash->{'url'};
+ }
+ $tooltarget = $toolhash{'target'};
+ if ($tooltarget eq 'window') {
+ $dimendivstyle = 'display:block';
+ $windivstyle = 'display:block';
+ $chkstate{'window'} = 'checked="checked" ';
+ } elsif ($tooltarget eq 'tab') {
+ $windivstyle = 'display:block';
+ $chkstate{'tab'} = 'checked="checked" ';
+ } else {
+ $chkstate{'iframe'} = 'checked="checked" ';
+ }
+ $width = $toolhash{'width'};
+ $height = $toolhash{'height'};
+ $linktext = $toolhash{'linktext'};
+ $explanation = $toolhash{'explanation'};
+ if ($toolhash{'gradable'}) {
+ $chkgrd = ' checked="checked"';
+ } else {
+ $chknogrd = ' checked="checked"';
+ }
+ if (ref($ltihash->{'crsconf'}) eq 'HASH') {
+ if ($ltihash->{'crsconf'}->{'title'}) {
+ $crstitle = $toolhash{'crstitle'};
+ $titlestyle = 'display:inline';
+ }
+ if ($ltihash->{'crsconf'}->{'label'}) {
+ $crslabel = $toolhash{'crslabel'};
+ $labelstyle = 'display:inline';
+ }
+ if ($ltihash->{'crsconf'}->{'append'}) {
+ $crsappend = $toolhash{'crsappend'};
+ $appendstyle = 'display:inline';
+ }
+ if ($ltihash->{'crsconf'}->{'target'}) {
+ $dispdivstyle = 'display:block';
+ }
+ if ($ltihash->{'crsconf'}->{'linktext'}) {
+ $linktextstyle = 'padding:0;display:inline';
+ }
+ if ($ltihash->{'crsconf'}->{'explanation'}) {
+ $explanationstyle = 'padding:0;display:inline';
}
}
+ $toolelem = '<span class="LC_nobreak">'.$image.' '.$tooltitle.'</span><br />';
+ $gradablestyle = 'display:inline';
}
}
} else {
- $toolelem = '<span class="LC_docs_ext_edit">'."\n".
- '<select name="exttoolid" id="LC_exttoolid" onchange="javascript:updateExttool(this,'.
- 'this.form,'."'$supplementalflag'".');"'.$disabled.'>'."\n".
- '<option value="" selected="selected">'.&mt('Select').'</option>';
- my %bynum;
+ $subdivstyle = 'display:none';
+ my $toolradio = 'exttooltype';
+ my $exttypeon = 'LC_exttoolon';
+ my $exttypeoff = 'LC_exttooloff';
+ my $exttypeonsty = 'display:none';
+ my $exttypeoffsty = 'display:none';
+ my $exttypeofftext;
+ if ($supplementalflag) {
+ $toolradio = 'suppexttooltype';
+ $exttypeon = 'LC_exttoolonsupp';
+ $exttypeoff = 'LC_exttooloffsupp';
+ }
+ my ($numcrstools,$numdomtools,$typeclick,%defcheck,%typedesc);
+ %typedesc = (
+ crs => 'Defined in course',
+ dom => 'Defined in domain',
+ );
+#FIXME need crstype
+ my $seloptions;
+ $subdivid = 'LC_addtool';
+ if ($supplementalflag) {
+ $subdivid = 'LC_addtoolsupp';
+ }
if (ref($ltitools) eq 'HASH') {
- foreach my $id (keys(%{$ltitools})) {
- if (ref($ltitools->{$id}) eq 'HASH') {
- my $order = $ltitools->{$id}->{'order'};
- $bynum{$order} = [$id,$ltitools->{$id}];
- }
+ if (ref($ltitools->{'crs'}) eq 'HASH') {
+ $numcrstools = scalar(keys(%{$ltitools->{'crs'}}));
}
- }
- foreach my $item (sort { $a <=> $b } keys(%bynum)) {
- if (ref($bynum{$item}) eq 'ARRAY') {
- if (ref($bynum{$item}->[1]) eq 'HASH') {
- my $tooltitle = $bynum{$item}->[1]->{'title'};
- my $icon = $bynum{$item}->[1]->{'image'};
- $toolelem .= '<option value="'.$bynum{$item}->[0].'">'.$tooltitle.'</option>';
+ if (ref($ltitools->{'dom'}) eq 'HASH') {
+ $numdomtools = scalar(keys(%{$ltitools->{'dom'}}));
+ }
+ if ($numcrstools || $numdomtools) {
+ $typeclick = ' onclick="'.
+ 'javascript:updateExttoolSel(this.form,'."'$toolradio','$supplementalflag'".');"';
+ } else {
+ $exttypeoffsty = 'display:block';
+ $exttypeofftext = &mt('No external tools defined in either the domain or the course are available for selection.');
+ }
+ if ($numcrstools && !$numdomtools) {
+ $defcheck{'crs'} = ' checked="checked"';
+ $subdivstyle = 'display:block';
+ $exttypeonsty = 'display:block';
+ my $firstoption = '<option value="" selected="selected">'.&mt('Select').'</option>';
+ $seloptions = &ordered_tooloptions($ltitools->{'crs'});
+ if ($seloptions) {
+ $seloptions = "$firstoption\n$seloptions";
+ }
+ $exttypeofftext = &mt('No external tools defined in the domain are available for selection.');
+ } elsif (!$numcrstools && $numdomtools) {
+ $defcheck{'dom'} = ' checked="checked"';
+ $subdivstyle = 'display:none';
+ $exttypeonsty = 'display:block';
+ my $firstoption = '<option value="" selected="selected">'.&mt('Select').'</option>';
+ $seloptions = &ordered_tooloptions($ltitools->{'dom'});
+ if ($seloptions) {
+ $seloptions = "$firstoption\n$seloptions";
}
+#FIXME need crstype
+ $exttypeofftext = &mt('No external tools defined in the course are available for selection.');
}
}
- $toolelem .= '</select></span><br />';
+ foreach my $type ('crs','dom') {
+ $toolelem .= '<span class="LC_nobreak"> <label>'.
+ '<input type="radio" name="'.$toolradio.'" value="'.$type.'"'.$defcheck{$type}.
+ $typeclick.$disabled.' />'.$typedesc{$type}.'</label></span> '."\n";
+ }
+ $toolelem .= '<div id="'.$exttypeon.'" style="'.$exttypeonsty.'">'.
+ '<select name="exttoolid" onchange="javascript:updateExttool(this,'.
+ 'this.form,'."'$supplementalflag'".');"'.$disabled.'>'."\n".
+ $seloptions.
+ '</select><br /></div>'."\n".
+ '<div id="'.$exttypeoff.'" style="'.$exttypeoffsty.'">'.
+ $exttypeofftext.
+ '<br /></div>'."\n";
$crslabel = $env{'course.'.$cdom.'_'.$cnum.'.internal.coursecode'};
$crstitle = $env{'course.'.$cdom.'_'.$cnum.'.description'};
$crsappend = '';
@@ -646,7 +722,7 @@
$legend
$active
$chooser
-<div>
+<div id="$subdivid" style="$subdivstyle">
<span$srcclass>
$title
<input type="text" size="$size" name="exttitle" value="$orig_title" $disabled />
@@ -666,6 +742,35 @@
}
}
+sub ordered_tooloptions {
+ my ($toolsref) = @_;
+ my ($seloptions, at ids, at titles);
+ if (ref($toolsref) eq 'HASH') {
+ my %bynum;
+ foreach my $id (keys(%{$toolsref})) {
+ if (ref($toolsref->{$id}) eq 'HASH') {
+ my $order = $toolsref->{$id}->{'order'};
+ $bynum{$order} = [$id,$toolsref->{$id}];
+ }
+ }
+ foreach my $item (sort { $a <=> $b } keys(%bynum)) {
+ if (ref($bynum{$item}) eq 'ARRAY') {
+ if (ref($bynum{$item}->[1]) eq 'HASH') {
+ my $tooltitle = $bynum{$item}->[1]->{'title'};
+ push(@titles,$tooltitle);
+ push(@ids,$bynum{$item}->[0]);
+ $seloptions .= '<option value="'.$bynum{$item}->[0].'">'.$tooltitle.'</option>'."\n";
+ }
+ }
+ }
+ }
+ if (wantarray) {
+ return (\@ids,\@titles);
+ } else {
+ return $seloptions;
+ }
+}
+
sub display_editor {
my ($url,$folderpath,$symb,$idx,$type,$cdom,$cnum,$hostname) = @_;
my ($residx,$supplementalflag,$title,$pathitem,$output,$js,$navmap);
@@ -681,9 +786,20 @@
my $path = &Apache::loncommon::symb_to_docspath($symb,\$navmap);
$pathitem = '<input type="hidden" name="folderpath" value="'.&HTML::Entities::encode($path,'<>&"').'" />';
}
- my %ltitools;
+ my (%ltitools,%tooltypes);
if ($type eq 'tool') {
- %ltitools = &Apache::lonnet::get_domain_lti($cdom,'consumer');
+ my (%domtools,%crstools);
+ %tooltypes = &Apache::loncommon::usable_exttools();
+ if ($tooltypes{'dom'}) {
+ %domtools = &Apache::lonnet::get_domain_lti($cdom,'consumer');
+ }
+ if ($tooltypes{'crs'}) {
+ %crstools = &Apache::lonnet::get_course_lti($cnum,$cdom,'consumer');
+ }
+ %ltitools = (
+ dom => \%domtools,
+ crs => \%crstools,
+ );
}
$js = &Apache::lonhtmlcommon::scripttag(&extedit_javascript());
my $args = { 'force_register' => $env{'form.register'} };
@@ -704,63 +820,99 @@
sub extedit_javascript {
my ($toolsref) = @_;
- my $toolsjs;
+ my ($toolsjs,$exttoolnums,$exttooloptions);
if (ref($toolsref) eq 'HASH') {
- my $num = scalar(keys(%{$toolsref}));
- $toolsjs = " var ltitools = new Array($num);\n".
- " var ltitoolsUrl = new Array($num);\n".
- " var ltitoolsTarget = new Array($num);\n".
- " var ltitoolsWidth = new Array($num);\n".
- " var ltitoolsHeight = new Array($num);\n".
- " var ltitoolsLinkDef = new Array($num);\n".
- " var ltitoolsExplainDef = new Array($num);\n".
- " var ltitoolsDisplay = new Array($num);\n".
- " var ltitoolsLink = new Array($num);\n".
- " var ltitoolsExplain = new Array($num);\n".
- " var ltitoolsLabel = new Array($num);\n".
- " var ltitoolsTitle = new Array($num);\n".
- " var ltitoolsAppend = new Array($num);\n";
- my $i = 0;
- foreach my $key (sort { $a <=> $b } keys(%{$toolsref})) {
- if (ref($toolsref->{$key}) eq 'HASH') {
- if (ref($toolsref->{$key}->{'display'}) eq 'HASH') {
- my $target = $toolsref->{$key}->{'display'}->{'target'};
- my $width = $toolsref->{$key}->{'display'}->{'width'};
- my $height = $toolsref->{$key}->{'display'}->{'height'};
- my $linkdef = $toolsref->{$key}->{'display'}->{'linktext'};
- my $explaindef = $toolsref->{$key}->{'display'}->{'explanation'};
- my $providerurl;
- if ($toolsref->{$key}->{'url'} =~ m{://}) {
- (my $prot,my $host,$providerurl) = ($toolsref->{$key}->{'url'} =~ m{^([^/]+)://([^/]+)(|/.+)$});
- } else {
- $providerurl = $toolsref->{$key}->{'url'};
+ $toolsjs = " var ltitools = new Array();\n".
+ " var ltitoolsUrl = new Array();\n".
+ " var ltitoolsTarget = new Array();\n".
+ " var ltitoolsWidth = new Array();\n".
+ " var ltitoolsHeight = new Array();\n".
+ " var ltitoolsLinkDef = new Array();\n".
+ " var ltitoolsExplainDef = new Array();\n".
+ " var ltitoolsDisplay = new Array();\n".
+ " var ltitoolsLink = new Array();\n".
+ " var ltitoolsExplain = new Array();\n".
+ " var ltitoolsLabel = new Array();\n".
+ " var ltitoolsTitle = new Array();\n".
+ " var ltitoolsAppend = new Array();\n";
+ $exttoolnums = " var ltitoolsnum = new Array();\n".
+ " var tooloptval = new Array();\n".
+ " var toolopttxt = new Array();\n";
+ my $idx = 0;
+ foreach my $type ('crs','dom') {
+ if (ref($toolsref->{$type}) eq 'HASH') {
+ my $num = scalar(keys(%{$toolsref->{$type}}));
+ $toolsjs .= " ltitools[$idx] = new Array($num);\n".
+ " ltitoolsUrl[$idx] = new Array($num);\n".
+ " ltitoolsTarget[$idx] = new Array($num);\n".
+ " ltitoolsWidth[$idx] = new Array($num);\n".
+ " ltitoolsHeight[$idx] = new Array($num);\n".
+ " ltitoolsLinkDef[$idx] = new Array($num);\n".
+ " ltitoolsExplainDef[$idx] = new Array($num);\n".
+ " ltitoolsDisplay[$idx] = new Array($num);\n".
+ " ltitoolsLink[$idx] = new Array($num);\n".
+ " ltitoolsExplain[$idx] = new Array($num);\n".
+ " ltitoolsLabel[$idx] = new Array($num);\n".
+ " ltitoolsTitle[$idx] = new Array($num);\n".
+ " ltitoolsAppend[$idx] = new Array($num);\n";
+ my $i=0;
+ foreach my $key (sort { $a <=> $b } keys(%{$toolsref->{$type}})) {
+ if (ref($toolsref->{$type}->{$key}) eq 'HASH') {
+ if (ref($toolsref->{$type}->{$key}->{'display'}) eq 'HASH') {
+ my $target = $toolsref->{$type}->{$key}->{'display'}->{'target'};
+ my $width = $toolsref->{$type}->{$key}->{'display'}->{'width'};
+ my $height = $toolsref->{$type}->{$key}->{'display'}->{'height'};
+ my $linkdef = $toolsref->{$type}->{$key}->{'display'}->{'linktext'};
+ my $explaindef = $toolsref->{$type}->{$key}->{'display'}->{'explanation'};
+ my $providerurl;
+ if ($toolsref->{$type}->{$key}->{'url'} =~ m{://}) {
+ (my $prot,my $host,$providerurl) =
+ ($toolsref->{$type}->{$key}->{'url'} =~ m{^([^/]+)://([^/]+)(|/.+)$});
+ } else {
+ $providerurl = $toolsref->{$type}->{$key}->{'url'};
+ }
+ $providerurl = &LONCAPA::map::qtunescape($providerurl);
+ $toolsjs .= " ltitools[$idx][$i] = '$key';\n".
+ " ltitoolsTarget[$idx][$i] = '$target';\n".
+ " ltitoolsWidth[$idx][$i] = '$width';\n".
+ " ltitoolsHeight[$idx][$i] = '$height';\n".
+ " ltitoolsLinkDef[$idx][$i] = '$linkdef';\n".
+ " ltitoolsExplainDef[$idx][$i] = '$explaindef';\n".
+ " ltitoolsUrl[$idx][$i] = '$providerurl';\n";
+ }
+ if (ref($toolsref->{$type}->{$key}->{'crsconf'}) eq 'HASH') {
+ my $display = $toolsref->{$type}->{$key}->{'crsconf'}->{'target'};
+ $toolsjs .= " ltitoolsDisplay[$idx][$i] = '$display';\n";
+ my $linktext = $toolsref->{$type}->{$key}->{'crsconf'}->{'linktext'};
+ $toolsjs .= " ltitoolsLink[$idx][$i] = '$linktext';\n";
+ my $explanation = $toolsref->{$type}->{$key}->{'crsconf'}->{'explanation'};
+ $toolsjs .= " ltitoolsExplain[$idx][$i] = '$explanation';\n";
+ my $label = $toolsref->{$type}->{$key}->{'crsconf'}->{'label'};
+ $toolsjs .= " ltitoolsLabel[$idx][$i] = '$label';\n";
+ my $title = $toolsref->{$type}->{$key}->{'crsconf'}->{'title'};
+ $toolsjs .= " ltitoolsTitle[$idx][$i] = '$title';\n";
+ my $append = $toolsref->{$type}->{$key}->{'crsconf'}->{'append'};
+ $toolsjs .= " ltitoolsAppend[$idx][$i] = '$append';\n";
+ }
}
- $providerurl = &LONCAPA::map::qtunescape($providerurl);
- $toolsjs .= ' ltitools['.$i.'] = '."'$key';\n".
- ' ltitoolsTarget['.$i.'] = '."'$target';\n".
- ' ltitoolsWidth['.$i.'] = '."'$width';\n".
- ' ltitoolsHeight['.$i.'] = '."'$height';\n".
- ' ltitoolsLinkDef['.$i.'] = '."'$linkdef';\n".
- ' ltitoolsExplainDef['.$i.'] = '."'$explaindef';\n".
- ' ltitoolsUrl['.$i.'] = '."'$providerurl';\n";
- }
- if (ref($toolsref->{$key}->{'crsconf'}) eq 'HASH') {
- my $display = $toolsref->{$key}->{'crsconf'}->{'target'};
- $toolsjs .= ' ltitoolsDisplay['.$i.'] = '."'$display';\n";
- my $linktext = $toolsref->{$key}->{'crsconf'}->{'linktext'};
- $toolsjs .= ' ltitoolsLink['.$i.'] = '."'$linktext';\n";
- my $explanation = $toolsref->{$key}->{'crsconf'}->{'explanation'};
- $toolsjs .= ' ltitoolsExplain['.$i.'] = '."'$explanation';\n";
- my $label = $toolsref->{$key}->{'crsconf'}->{'label'};
- $toolsjs .= ' ltitoolsLabel['.$i.'] = '."'$label';\n";
- my $title = $toolsref->{$key}->{'crsconf'}->{'title'};
- $toolsjs .= ' ltitoolsTitle['.$i.'] = '."'$title';\n";
- my $append = $toolsref->{$key}->{'crsconf'}->{'append'};
- $toolsjs .= ' ltitoolsAppend['.$i.'] = '."'$append';\n";
+ $i++;
}
-
- $i++;
+ my $firstoption = '<option value="" selected="selected">'.&mt('Select').'</option>';
+ my ($idsref,$titlesref) = &ordered_tooloptions($toolsref->{$type});
+ if ((ref($idsref) eq 'ARRAY') && (ref($titlesref) eq 'ARRAY')) {
+ my $count = scalar(@{$idsref});
+ $exttooloptions .= " tooloptval[$idx] = new Array($count);\n".
+ " toolopttxt[$idx] = new Array($count);\n";
+ for (my $n=0; $n<@{$idsref}; $n++) {
+ my $id = $idsref->[$n];
+ my $text = $titlesref->[$n];
+ $exttooloptions .= " tooloptval[$idx][$n] = '$id';\n".
+ " toolopttxt[$idx][$n] = '$text';\n";
+ }
+ }
+ $exttoolnums .= " ltitoolsnum[$idx] = $i;\n";
}
+ $idx ++;
}
}
my %js_lt = &Apache::lonlocal::texthash(
@@ -773,6 +925,7 @@
nopopup => 'Pop-up blocked',
nopriv => 'Insufficient privileges to use preview',
badurl => 'URL is not: http://hostname/path or https://hostname/path',
+ sele => 'Select',
);
&js_escape(\%js_lt);
@@ -811,17 +964,29 @@
} else {
title = escape(title);
var info = exttoolurl;
+ var prefix = '';
+ if (supplementalflag == 1) {
+ prefix = 'supp';
+ }
if (residx == 0) {
var toolid = parseInt(extform.exttoolid.options[extform.exttoolid.selectedIndex].value);
if (isNaN(toolid)) {
alert("$js_lt{'invtool'}");
return;
}
- info += ':'+toolid;
- }
- var prefix = '';
- if (supplementalflag == 1) {
- prefix = 'supp';
+ var typeelem = extform.elements[prefix+'exttooltype'];
+ if (typeelem.length) {
+ for (var i=0; i<typeelem.length; i++) {
+ if (typeelem[i].checked) {
+ tooltype = typeelem[i].value;
+ }
+ }
+ }
+ if (tooltype == 'crs') {
+ info += ':c'+toolid;
+ } else {
+ info += ':'+toolid;
+ }
}
var dispdiv = prefix+'tooldispdiv';
var windiv = prefix+'toolwindiv';
@@ -1035,6 +1200,77 @@
}
}
+function updateExttoolSel(form,radioname,supplementalflag) {
+ var prefix = '';
+ var typepick;
+ var radelem = form.elements[radioname];
+ if (radelem.length) {
+ for (var i=0; i<radelem.length; i++) {
+ if (radelem[i].checked) {
+ if (radelem[i].value == 'crs') {
+ typepick = 0;
+ } else if (radelem[i].value == 'dom') {
+ typepick = 1;
+ }
+ break;
+ }
+ }
+ }
+ if (supplementalflag == 1) {
+ prefix = 'supp';
+ }
+ $exttoolnums
+ $exttooloptions
+ if ((typepick == 0) || (typepick == 1)) {
+ var selelem = form.elements['exttoolid'];
+ var i, numopts = selelem.options.length -1;
+ if (numopts >=0) {
+ for (i = numopts; i >= 0; i--) {
+ selelem.remove(i);
+ }
+ }
+ if (ltitoolsnum[typepick]) {
+ if ((Array.isArray(tooloptval[typepick])) && (Array.isArray(toolopttxt[typepick]))) {
+ var len = tooloptval[typepick].length;
+ if (len) {
+ selelem.options[selelem.options.length] = new Option('$js_lt{sele}','',1,1);
+ var j;
+ for (j=0; j<len; j++) {
+ selelem.options[selelem.options.length] = new Option(toolopttxt[typepick][j],tooloptval[typepick][j]);
+ }
+ selelem.selectedIndex = 0;
+ }
+ }
+ if (document.getElementById('LC_exttoolon'+prefix)) {
+ document.getElementById('LC_exttoolon'+prefix).style.display = 'block';
+ }
+ if (document.getElementById('LC_exttooloff'+prefix)) {
+ document.getElementById('LC_exttooloff'+prefix).style.display = 'none';
+ }
+ if (document.getElementById('LC_addtool'+prefix)) {
+ document.getElementById('LC_addtool'+prefix).style.display = 'block';
+ }
+ } else {
+ if (document.getElementById('LC_exttoolon'+prefix)) {
+ document.getElementById('LC_exttoolon'+prefix).style.display = 'none';
+ }
+ if (document.getElementById('LC_exttooloff'+prefix)) {
+ document.getElementById('LC_exttooloff'+prefix).style.display = 'block';
+ }
+ if (document.getElementById('LC_addtool'+prefix)) {
+ document.getElementById('LC_addtool'+prefix).style.display = 'none';
+ }
+ }
+ if (selelem.options.length == 0) {
+ selelem.options[selelem.options.length] = new Option('','');
+ selelem.selectedIndex = 0;
+ }
+ updateExttool(selelem,form,supplementalflag);
+ resize_scrollbox('contentscroll','1','1');
+ }
+ return;
+}
+
function updateExttool(caller,form,supplementalflag) {
var prefix = '';
if (supplementalflag == 1) {
@@ -1089,109 +1325,124 @@
document.getElementById(gradablediv).style.display = 'none';
}
} else {
- if (ltitools.length > 0) {
- for (var j=0; j<ltitools.length; j++) {
- if (ltitools[j] == toolpick) {
- if (document.getElementById(dispdiv)) {
- if (ltitoolsDisplay[j]) {
- document.getElementById(dispdiv).style.display = 'block';
- if (form.exttooltarget.length) {
- for (var k=0; k<form.exttooltarget.length; k++) {
- if (form.exttooltarget[k].value == ltitoolsTarget[j]) {
- form.exttooltarget[k].checked = true;
- break;
+ var tooltype = '';
+ var typeelem = form.elements[prefix+'exttooltype'];
+ if (typeelem.length) {
+ for (var i=0; i<typeelem.length; i++) {
+ if (typeelem[i].checked) {
+ tooltype = typeelem[i].value;
+ }
+ }
+ }
+ if ((tooltype == 'crs') || (tooltype == 'dom')) {
+ var i = 0;
+ if (tooltype == 'dom') {
+ i = 1;
+ }
+ if (ltitools[i].length > 0) {
+ for (var j=0; j<ltitools[i].length; j++) {
+ if (ltitools[i][j] == toolpick) {
+ if (document.getElementById(dispdiv)) {
+ if (ltitoolsDisplay[i][j]) {
+ document.getElementById(dispdiv).style.display = 'block';
+ if (form.exttooltarget.length) {
+ for (var k=0; k<form.exttooltarget.length; k++) {
+ if (form.exttooltarget[k].value == ltitoolsTarget[i][j]) {
+ form.exttooltarget[k].checked = true;
+ break;
+ }
}
}
}
- }
- var dimen = 'none';
- var dimenwidth = '';
- var dimenheight = '';
- if ((ltitoolsDisplay[j]) && (ltitoolsTarget[j] == 'window')) {
- dimen = 'block';
- dimenwidth = ltitoolsWidth[j];
- dimenheight = ltitoolsHeight[j];
- }
- if (document.getElementById(dimendiv)) {
- document.getElementById(dimendiv).style.display = dimen;
- }
- if (document.getElementById(widthinput)) {
- document.getElementById(widthinput).value = dimenwidth;
- }
- if (document.getElementById(heightinput)) {
- document.getElementById(heightinput).value = dimenheight;
- }
- }
- if (document.getElementById(windiv)) {
- if ((ltitoolsTarget[j] == 'window') || (ltitoolsTarget[j] == 'tab')) {
- document.getElementById(windiv).style.display = 'block';
- } else {
- document.getElementById(windiv).style.display = 'none';
- }
- if (document.getElementById(linktextdiv)) {
- if (ltitoolsLink[j]) {
- document.getElementById(linktextdiv).style.display = 'inline';
- } else {
- document.getElementById(linktextdiv).style.display = 'none';
+ var dimen = 'none';
+ var dimenwidth = '';
+ var dimenheight = '';
+ if ((ltitoolsDisplay[i][j]) && (ltitoolsTarget[i][j] == 'window')) {
+ dimen = 'block';
+ dimenwidth = ltitoolsWidth[i][j];
+ dimenheight = ltitoolsHeight[i][j];
+ }
+ if (document.getElementById(dimendiv)) {
+ document.getElementById(dimendiv).style.display = dimen;
+ }
+ if (document.getElementById(widthinput)) {
+ document.getElementById(widthinput).value = dimenwidth;
+ }
+ if (document.getElementById(heightinput)) {
+ document.getElementById(heightinput).value = dimenheight;
}
}
- if (document.getElementById(linktextinput)) {
- if (ltitoolsLink[j]) {
- document.getElementById(linktextinput).value = ltitoolsLinkDef[j];
+ if (document.getElementById(windiv)) {
+ if ((ltitoolsTarget[i][j] == 'window') || (ltitoolsTarget[i][j] == 'tab')) {
+ document.getElementById(windiv).style.display = 'block';
} else {
- document.getElementById(linktextinput).value = '';
+ document.getElementById(windiv).style.display = 'none';
+ }
+ if (document.getElementById(linktextdiv)) {
+ if (ltitoolsLink[i][j]) {
+ document.getElementById(linktextdiv).style.display = 'inline';
+ } else {
+ document.getElementById(linktextdiv).style.display = 'none';
+ }
+ }
+ if (document.getElementById(linktextinput)) {
+ if (ltitoolsLink[i][j]) {
+ document.getElementById(linktextinput).value = ltitoolsLinkDef[i][j];
+ } else {
+ document.getElementById(linktextinput).value = '';
+ }
+ }
+ if (document.getElementById(explanationdiv)) {
+ if (ltitoolsExplain[i][j]) {
+ document.getElementById(explanationdiv).style.display = 'inline';
+ } else {
+ document.getElementById(explanationdiv).style.display = 'none';
+ }
+ }
+ if (document.getElementById(explanationinput)) {
+ if (ltitoolsExplain[i][j]) {
+ document.getElementById(explanationinput).value = ltitoolsExplainDef[i][j];
+ } else {
+ document.getElementById(explananationinput).value = '';
+ }
}
}
- if (document.getElementById(explanationdiv)) {
- if (ltitoolsExplain[j]) {
- document.getElementById(explanationdiv).style.display = 'inline';
+ if (document.getElementById(labeldiv)) {
+ if (ltitoolsLabel[i][j]) {
+ document.getElementById(labeldiv).style.display = 'inline';
} else {
- document.getElementById(explanationdiv).style.display = 'none';
+ document.getElementById(labeldiv).style.display = 'none';
}
}
- if (document.getElementById(explanationinput)) {
- if (ltitoolsExplain[j]) {
- document.getElementById(explanationinput).value = ltitoolsExplainDef[j];
+ if (document.getElementById(titlediv)) {
+ if (ltitoolsTitle[i][j]) {
+ document.getElementById(titlediv).style.display = 'inline';
} else {
- document.getElementById(explananationinput).value = '';
+ document.getElementById(titlediv).style.display = 'none';
}
}
- }
- if (document.getElementById(labeldiv)) {
- if (ltitoolsLabel[j]) {
- document.getElementById(labeldiv).style.display = 'inline';
- } else {
- document.getElementById(labeldiv).style.display = 'none';
- }
- }
- if (document.getElementById(titlediv)) {
- if (ltitoolsTitle[j]) {
- document.getElementById(titlediv).style.display = 'inline';
- } else {
- document.getElementById(titlediv).style.display = 'none';
- }
- }
- if (document.getElementById(appenddiv)) {
- if (ltitoolsAppend[j]) {
- document.getElementById(appenddiv).style.display = 'inline';
- if (document.getElementById(providerurl)) {
- if ((ltitoolsUrl[j] != '') && (ltitoolsUrl[j] != null)) {
- document.getElementById(providerurl).innerHTML = ' ('+ltitoolsUrl[j]+')<br />';
+ if (document.getElementById(appenddiv)) {
+ if (ltitoolsAppend[i][j]) {
+ document.getElementById(appenddiv).style.display = 'inline';
+ if (document.getElementById(providerurl)) {
+ if ((ltitoolsUrl[i][j] != '') && (ltitoolsUrl[i][j] != null)) {
+ document.getElementById(providerurl).innerHTML = ' ('+ltitoolsUrl[i][j]+')<br />';
+ }
+ }
+ } else {
+ document.getElementById(appenddiv).style.display = 'none';
+ if (document.getElementById(providerurl)) {
+ document.getElementById(providerurl).innerHTML = '';
}
- }
- } else {
- document.getElementById(appenddiv).style.display = 'none';
- if (document.getElementById(providerurl)) {
- document.getElementById(providerurl).innerHTML = '';
}
}
- }
- if (document.getElementById(gradablediv)) {
- if (supplementalflag != 1) {
- document.getElementById(gradablediv).style.display = 'inline';
+ if (document.getElementById(gradablediv)) {
+ if (supplementalflag != 1) {
+ document.getElementById(gradablediv).style.display = 'inline';
+ }
}
+ break;
}
- break;
}
}
}
Index: loncom/interface/lonexttool.pm
diff -u loncom/interface/lonexttool.pm:1.23 loncom/interface/lonexttool.pm:1.24
--- loncom/interface/lonexttool.pm:1.23 Tue Mar 29 20:12:46 2022
+++ loncom/interface/lonexttool.pm Mon May 22 21:10:55 2023
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Launch External Tool Provider (LTI)
#
-# $Id: lonexttool.pm,v 1.23 2022/03/29 20:12:46 raeburn Exp $
+# $Id: lonexttool.pm,v 1.24 2023/05/22 21:10:55 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -96,13 +96,20 @@
my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
my $chome = $env{'course.'.$env{'request.course.id'}.'.home'};
- my ($idx,$is_tool,%toolhash,%toolsettings);
+ my ($idx,$crstool,$is_tool,%toolhash,%toolsettings);
if ($r->uri eq "/adm/$cdom/$cnum/$marker/$exttool") {
%toolsettings=&Apache::lonnet::dump('exttool_'.$marker,$cdom,$cnum);
if ($toolsettings{'id'}) {
- $idx = $toolsettings{'id'};
- my %ltitools = &Apache::lonnet::get_domain_lti($cdom,'consumer');
+ my %ltitools;
+ if ($toolsettings{'id'} =~ /^c(\d+)$/) {
+ $idx = $1;
+ $crstool = 1;
+ %ltitools = &Apache::lonnet::get_course_lti($cnum,$cdom,'consumer');
+ } else {
+ $idx = $toolsettings{'id'};
+ %ltitools = &Apache::lonnet::get_domain_lti($cdom,'consumer');
+ }
if (ref($ltitools{$idx}) eq 'HASH') {
%toolhash = %{$ltitools{$idx}};
$toolhash{'display'} = {
@@ -204,17 +211,19 @@
}
}
my $submittext = &mt('Launch [_1]',$toolhash{'title'});
- if (($toolhash{'key'} ne '') && ($toolhash{'secret'} ne '') &&
- ($toolhash{'url'} ne '') && ($launchok)) {
+ if (($toolhash{'url'} ne '') && ($launchok)) {
my %lti = <i_params($r,$cnum,$cdom,$idx,$submittext,\%toolhash);
my $url = $toolhash{'url'};
if ($toolhash{'crsappend'} ne '') {
$url .= $toolhash{'crsappend'};
}
- $r->print(&launch_html($url,$toolhash{'key'},$toolhash{'secret'},
- $toolhash{'sigmethod'},$submittext,\%lti));
+ my %info = (
+ method => $toolhash{'sigmethod'},
+ );
+ $r->print(&launch_html($cdom,$cnum,$crstool,$url,$idx,
+ $toolhash{'cipher'},$submittext,\%lti,\%info));
} else {
- $r->print('<div>'.&mt('External Tool Unavailable').'</div>');
+ $r->print('<div class="LC_warning">'.&mt('External Tool Unavailable').'</div>');
}
}
return OK;
@@ -434,8 +443,13 @@
}
sub launch_html {
- my ($url,$key,$secret,$sigmethod,$submittext,$paramsref) = @_;
- my $hashref = &LONCAPA::ltiutils::sign_params($url,$key,$secret,$paramsref,$sigmethod);
+ my ($cdom,$cnum,$crstool,$url,$idx,$keynum,$submittext,$paramsref,$inforef) = @_;
+ my ($status,$hashref) =
+ &Apache::lonnet::sign_lti($cdom,$cnum,$crstool,$url,$idx,$keynum,
+ '',$paramsref,$inforef);
+ unless ($status eq 'ok') {
+ return '<div class="LC_warning">'.&mt('External Tool Unavailable').'</div>';
+ }
my $action = &HTML::Entities::encode($url,'<>&"');
my $form = <<"END";
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
Index: loncom/interface/lonparmset.pm
diff -u loncom/interface/lonparmset.pm:1.619 loncom/interface/lonparmset.pm:1.620
--- loncom/interface/lonparmset.pm:1.619 Mon Apr 3 19:53:30 2023
+++ loncom/interface/lonparmset.pm Mon May 22 21:10:55 2023
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Handler to set parameters for assessments
#
-# $Id: lonparmset.pm,v 1.619 2023/04/03 19:53:30 raeburn Exp $
+# $Id: lonparmset.pm,v 1.620 2023/05/22 21:10:55 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -1849,7 +1849,7 @@
$extra = 'ltid_'.$domltistr;
}
}
- my %courselti = &Apache::lonnet::get_course_lti($cnum,$cdom);
+ my %courselti = &Apache::lonnet::get_course_lti($cnum,$cdom,'provider');
if (keys(%courselti)) {
foreach my $item (sort { $a <=> $b } keys(%courselti)) {
if (($item =~ /^\d+$/) && (ref($courselti{$item}) eq 'HASH')) {
@@ -5345,7 +5345,8 @@
}
my %courselti =
&Apache::lonnet::get_course_lti($env{'course.'.$env{'request.course.id'}.'.num'},
- $env{'course.'.$env{'request.course.id'}.'.domain'});
+ $env{'course.'.$env{'request.course.id'}.'.domain'},
+ 'provider');
foreach my $item (keys(%courselti)) {
if (ref($courselti{$item}) eq 'HASH') {
$crslti{$item} = $courselti{$item}{'name'};
Index: loncom/lonnet/perl/lonnet.pm
diff -u loncom/lonnet/perl/lonnet.pm:1.1509 loncom/lonnet/perl/lonnet.pm:1.1510
--- loncom/lonnet/perl/lonnet.pm:1.1509 Mon May 22 15:09:15 2023
+++ loncom/lonnet/perl/lonnet.pm Mon May 22 21:10:55 2023
@@ -1,7 +1,7 @@
# The LearningOnline Network
# TCP networking package
#
-# $Id: lonnet.pm,v 1.1509 2023/05/22 15:09:15 raeburn Exp $
+# $Id: lonnet.pm,v 1.1510 2023/05/22 21:10:55 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -415,6 +415,60 @@
return $response;
}
+sub sign_lti {
+ my ($cdom,$cnum,$crstool,$url,$idx,$keynum,$post,$paramsref,$inforef) = @_;
+ my $chome;
+ if (&domain($cdom) ne '') {
+ if ($crstool) {
+ $chome = &homeserver($cnum,$cdom);
+ } else {
+ $chome = &domain($cdom,'primary');
+ }
+ }
+ if ($cdom && $chome && ($chome ne 'no_host')) {
+ if ((ref($paramsref) eq 'HASH') &&
+ (ref($inforef) eq 'HASH')) {
+ my $rep;
+ if (grep { $_ eq $chome } ¤t_machine_ids()) {
+ # domain information is hosted on this machine
+ $rep =
+ &LONCAPA::Lond::sign_params($cdom,$cnum,$crstool,$url,
+ $idx,$keynum,$post,
+ $perlvar{'lonVersion'},
+ $paramsref,$inforef);
+ if ($rep ne '') {
+ return ('ok',$rep);
+ }
+ } else {
+ my ($escurl,$params,$info);
+ $escurl = &escape($url);
+ if (ref($paramsref) eq 'HASH') {
+ $params = &freeze_escape($paramsref);
+ }
+ if (ref($inforef) eq 'HASH') {
+ $info = &freeze_escape($inforef);
+ }
+ $rep=&reply("encrypt:signlti:$cdom:$cnum:$crstool:$escurl:$idx:$keynum:$post:$params:$info",$chome);
+ }
+ if (($rep eq '') || ($rep =~ /^con_lost|error|no_such_host|unknown_cmd/i)) {
+ return ();
+ } else {
+ my %returnhash;
+ foreach my $item (split(/\&/,$rep)) {
+ my ($name,$value)=split(/\=/,$item);
+ $returnhash{&unescape($name)}=&thaw_unescape($value);
+ }
+ return('ok',\%returnhash);
+ }
+ } else {
+ return ();
+ }
+ } else {
+ return ();
+ &logthis("sign_lti failed - no homeserver and/or domain ($cdom) ($chome)");
+ }
+}
+
# -------------------------------------------------- Non-critical communication
sub subreply {
my ($cmd,$server)=@_;
@@ -2700,7 +2754,7 @@
'coursecategories','ssl','autoenroll',
'trust','helpsettings','wafproxy',
'ltisec','toolsec','domexttool',
- 'exttool',],$domain);
+ 'exttool'],$domain);
my @coursetypes = ('official','unofficial','community','textbook','placement');
if (ref($domconfig{'defaults'}) eq 'HASH') {
$domdefaults{'lang_def'} = $domconfig{'defaults'}{'lang_def'};
@@ -12538,20 +12592,29 @@
}
sub get_course_lti {
- my ($cnum,$cdom) = @_;
+ my ($cnum,$cdom,$context) = @_;
+ my ($name,$cachename,%lti);
+ if ($context eq 'consumer') {
+ $name = 'ltitools';
+ $cachename = 'courseltitools';
+ } elsif ($context eq 'provider') {
+ $name = 'lti';
+ $cachename = 'courselti';
+ } else {
+ return %lti;
+ }
my $hashid=$cdom.'_'.$cnum;
- my %courselti;
- my ($result,$cached)=&is_cached_new('courselti',$hashid);
+ my ($result,$cached)=&is_cached_new($cachename,$hashid);
if (defined($cached)) {
if (ref($result) eq 'HASH') {
- %courselti = %{$result};
+ %lti = %{$result};
}
} else {
- %courselti = &dump('lti',$cdom,$cnum,undef,undef,undef,1);
+ %lti = &dump($name,$cdom,$cnum,undef,undef,undef,1);
my $cachetime = 24*60*60;
- &do_cache_new('courselti',$hashid,\%courselti,$cachetime);
+ &do_cache_new($cachename,$hashid,\%lti,$cachetime);
}
- return %courselti;
+ return %lti;
}
sub courselti_itemid {
Index: loncom/Lond.pm
diff -u loncom/Lond.pm:1.21 loncom/Lond.pm:1.22
--- loncom/Lond.pm:1.21 Thu Feb 17 22:35:50 2022
+++ loncom/Lond.pm Mon May 22 21:10:56 2023
@@ -1,6 +1,6 @@
# The LearningOnline Network
#
-# $Id: Lond.pm,v 1.21 2022/02/17 22:35:50 raeburn Exp $
+# $Id: Lond.pm,v 1.22 2023/05/22 21:10:56 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -43,6 +43,9 @@
use Crypt::PKCS10;
use Net::OAuth;
use Crypt::CBC;
+use Net::OAuth;
+use Digest::SHA;
+use Digest::MD5 qw(md5_hex);
sub dump_with_regexp {
my ( $tail, $clientversion ) = @_;
@@ -1254,6 +1257,122 @@
return $itemid;
}
+sub sign_params {
+ my ($cdom,$cnum,$crstool,$url,$idx,$keynum,$post,$loncaparev,$paramsref,$inforef) = @_;
+ return unless (ref($paramsref) eq 'HASH');
+ my ($sigmethod,$type,$callback);
+ if (ref($inforef) eq 'HASH') {
+ if (exists($inforef->{'method'})) {
+ $sigmethod = $inforef->{'method'};
+ }
+ if (exists($inforef->{'cb'})) {
+ $callback = $inforef->{'cb'};
+ }
+ if (exists($inforef->{'type'})) {
+ $type = $inforef->{'type'};
+ }
+ }
+ my ($cachename,$hashid,$key,$secret,%ltitoolsenc);
+ if ($crstool) {
+ $cachename = 'crsltitoolsenc';
+ $hashid = $cdom.'_'.$cnum;
+ } else {
+ $cachename = 'ltitoolsenc';
+ $hashid = $cdom;
+ }
+ my ($encresult,$enccached)=&Apache::lonnet::is_cached_new($cachename,$hashid);
+ if (defined($enccached)) {
+ if (ref($encresult) eq 'HASH') {
+ %ltitoolsenc = %{$encresult};
+ }
+ } else {
+ if ($crstool) {
+ my $reply = &dump_with_regexp(join(":",($cdom,$cnum,'nohist_toolsenc','','')),$loncaparev);
+ %ltitoolsenc = %{&Apache::lonnet::unserialize($reply)};
+ } else {
+ my $reply = &get_dom("getdom:$cdom:encconfig:ltitools");
+ my $ltitoolsencref = &Apache::lonnet::thaw_unescape($reply);
+ if (ref($ltitoolsencref) eq 'HASH') {
+ %ltitoolsenc = %{$ltitoolsencref};
+ }
+ }
+ my $cachetime = 24*60*60;
+ &Apache::lonnet::do_cache_new($cachename,$hashid,\%ltitoolsenc,$cachetime);
+ }
+ if (!keys(%ltitoolsenc)) {
+ return;
+ } elsif (exists($ltitoolsenc{$idx})) {
+ if (ref($ltitoolsenc{$idx}) eq 'HASH') {
+ if (exists($ltitoolsenc{$idx}{'key'})) {
+ $key = $ltitoolsenc{$idx}{'key'};
+ }
+ if (exists($ltitoolsenc{$idx}{'secret'})) {
+ $secret = $ltitoolsenc{$idx}{'secret'};
+ my $privhost;
+ if ($keynum =~ /^\d+$/) {
+ if ($crstool) {
+ my $primary = &Apache::lonnet::domain($cdom,'primary');
+ my @ids = &Apache::lonnet::current_machine_ids();
+ unless (grep(/^\Q$primary\E$/, at ids)) {
+ $privhost = $primary;
+ my ($result,$plainsecret) = &decrypt_secret($privhost,$secret,$keynum,'ltitools');
+ if ($result eq 'ok') {
+ $secret = $plainsecret;
+ } else {
+ undef($secret);
+ }
+ }
+ }
+ unless ($privhost) {
+ my $privkey = &get_dom("getdom:$cdom:private:$keynum:ltitools:key");
+ if (($privkey ne '') && ($secret ne '')) {
+ my $cipher = new Crypt::CBC($privkey);
+ $secret = $cipher->decrypt_hex($secret);
+ } else {
+ undef($secret);
+ }
+ }
+ }
+ }
+ }
+ }
+ return if (($key eq '') || ($secret eq ''));
+ if ($sigmethod eq '') {
+ $sigmethod = 'HMAC-SHA1';
+ }
+ if ($type eq '') {
+ $type = 'request token';
+ }
+ if ($callback eq '') {
+ $callback = 'about:blank',
+ }
+ srand( time() ^ ($$ + ($$ << 15)) ); # Seed rand.
+ my $nonce = Digest::SHA::sha1_hex(sprintf("%06x%06x",rand(0xfffff0),rand(0xfffff0)));
+ my $request = Net::OAuth->request($type)->new(
+ consumer_key => $key,
+ consumer_secret => $secret,
+ request_url => $url,
+ request_method => 'POST',
+ signature_method => $sigmethod,
+ timestamp => time,
+ nonce => $nonce,
+ callback => $callback,
+ extra_params => $paramsref,
+ version => '1.0',
+ );
+ $request->sign();
+ if ($post) {
+ return $request->to_post_body();
+ } else {
+ return $request->to_hash();
+ }
+}
+
+sub decrypt_secret {
+ my ($privhost,$secret,$keynum,$type) = @_;
+ return;
+}
+
1;
__END__
Index: loncom/lond
diff -u loncom/lond:1.576 loncom/lond:1.577
--- loncom/lond:1.576 Mon Jul 25 23:31:40 2022
+++ loncom/lond Mon May 22 21:10:56 2023
@@ -2,7 +2,7 @@
# The LearningOnline Network
# lond "LON Daemon" Server (port "LOND" 5663)
#
-# $Id: lond,v 1.576 2022/07/25 23:31:40 raeburn Exp $
+# $Id: lond,v 1.577 2023/05/22 21:10:56 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -65,7 +65,7 @@
my $status='';
my $lastlog='';
-my $VERSION='$Revision: 1.576 $'; #' stupid emacs
+my $VERSION='$Revision: 1.577 $'; #' stupid emacs
my $remoteVERSION;
my $currenthostid="default";
my $currentdomainid;
@@ -306,6 +306,7 @@
servertimezone => {remote => 1, enroll => 1},
setannounce => {remote => 1, domroles => 1},
sethost => {anywhere => 1},
+ signlti => {remote => 1},
store => {remote => 1, enroll => 1, reqcrs => 1,},
studentphoto => {remote => 1, enroll => 1},
sub => {content => 1,},
@@ -5307,6 +5308,76 @@
®ister_handler("lti", \<i_handler, 1, 1, 0);
#
+# LTI data for launch payload (received encrypted) are unencrypted and
+# then signed with the appropriate key and secret, before re-encrypting
+# for sending as the signed payload to the client (caller lonnet::sign_lti()).
+#
+# Parameters:
+# $cmd - Command request keyword (signlti).
+# $tail - Tail of the command. This is a colon-separated list
+# consisting of the domain, coursenum (if for an External
+# Tool defined in a course), crstool (true if defined in
+# a course), escaped launch URL, numeric ID of external tool
+# version number for encryption key (if tool's LTI secret was
+# encrypted before storing), post (true if signed data are
+# to be returned from Net::OAuth, as a post_body),
+# a frozen hash of LTI launch parameters, and a frozen hash
+# of LTI config data (i.e., method => signature method).
+# $client - File descriptor open on the client.
+# Returns:
+# 1 - Continue processing.
+# 0 - Exit.
+# Side effects:
+# The reply will contain the LTI payload, as & separated key=value pairs,
+# where value is itself a frozen hash, if the required key and secret
+# for the apecific tool ID are available. The payload data are retrived from
+# a call to Lond::sign_params(), and the reply is encrypted before being
+# written to $client.
+#
+sub sign_lti_handler {
+ my ($cmd, $tail, $client) = @_;
+
+ my $userinput = "$cmd:$tail";
+
+ my ($cdom,$cnum,$crstool,$escurl,$idx,$keynum,$post,$paramsref,$inforef) = split(/:/,$tail);
+ my $url = &unescape($escurl);
+ my $params = &Apache::lonnet::thaw_unescape($paramsref);
+ my $info = &Apache::lonnet::thaw_unescape($inforef);
+ my $res =
+ &LONCAPA::Lond::sign_params($cdom,$cnum,$crstool,$url,$idx,$keynum,
+ $post,$perlvar{'lonVersion'},$params,$info);
+ my $result;
+ if (ref($res) eq 'HASH') {
+ foreach my $key (keys(%{$res})) {
+ $result .= &escape($key).'='.&Apache::lonnet::freeze_escape($res->{$key}).'&';
+ }
+ $result =~ s/\&$//;
+ } else {
+ $result = $res;
+ }
+ if ($result =~ /^error:/) {
+ &Failure($client, \$result, $userinput);
+ } else {
+ if ($cipher) {
+ my $cmdlength=length($result);
+ $result.=" ";
+ my $encres='';
+ for (my $encidx=0;$encidx<=$cmdlength;$encidx+=8) {
+ $encres.= unpack("H16",
+ $cipher->encrypt(substr($result,
+ $encidx,
+ 8)));
+ }
+ &Reply( $client,"enc:$cmdlength:$encres\n",$userinput);
+ } else {
+ &Failure( $client, "error:no_key\n",$userinput);
+ }
+ }
+ return 1;
+}
+®ister_handler("signlti", \&sign_lti_handler, 1, 1, 0);
+
+#
# Puts an id to a domains id database.
#
# Parameters:
More information about the LON-CAPA-cvs
mailing list