Annotation of loncom/interface/lonexttool.pm, revision 1.1

1.1     ! raeburn     1: # The LearningOnline Network with CAPA
        !             2: # Launch External Tool Provider (LTI)
        !             3: #
        !             4: # $Id: lonexttool.pm,v 1.1 2016/01/11 11:40:00 raeburn Exp $
        !             5: #
        !             6: # Copyright Michigan State University Board of Trustees
        !             7: #
        !             8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
        !             9: #
        !            10: # LON-CAPA is free software; you can redistribute it and/or modify
        !            11: # it under the terms of the GNU General Public License as published by
        !            12: # the Free Software Foundation; either version 2 of the License, or
        !            13: # (at your option) any later version.
        !            14: #
        !            15: # LON-CAPA is distributed in the hope that it will be useful,
        !            16: # but WITHOUT ANY WARRANTY; without even the implied warranty of
        !            17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        !            18: # GNU General Public License for more details.
        !            19: #
        !            20: # You should have received a copy of the GNU General Public License
        !            21: # along with LON-CAPA; if not, write to the Free Software
        !            22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
        !            23: #
        !            24: # /home/httpd/html/adm/gpl.txt
        !            25: #
        !            26: # http://www.lon-capa.org/
        !            27: #
        !            28: 
        !            29: =pod
        !            30: 
        !            31: =head1 NAME
        !            32: 
        !            33: Apache::lonexttool - Tool Provider launcher
        !            34: 
        !            35: =head1 SYNOPSIS
        !            36: 
        !            37: 
        !            38: =head1 OVERVIEW
        !            39: 
        !            40: =cut
        !            41: 
        !            42: package Apache::lonexttool;
        !            43: 
        !            44: use strict;
        !            45: use Apache::Constants qw(:common :http);
        !            46: use Net::OAuth;
        !            47: use Encode;
        !            48: use Digest::SHA;
        !            49: use HTML::Entities;
        !            50: use Apache::lonlocal;
        !            51: use Apache::lonnet;
        !            52: use Apache::loncommon;
        !            53: 
        !            54: sub handler {
        !            55:     my $r=shift;
        !            56:     &Apache::loncommon::content_type($r,'text/html');
        !            57:     $r->send_http_header;
        !            58: 
        !            59:     return OK if $r->header_only;
        !            60: 
        !            61:     my $target=$env{'form.grade_target'};
        !            62: # ------------------------------------------------------------ Print the screen
        !            63:     if ($target eq 'tex') {
        !            64:         $r->print(&Apache::lonprintout::print_latex_header($env{'form.latex_type'}));
        !            65:     }
        !            66: 
        !            67: # Is this even in a course?
        !            68:     unless ($env{'request.course.id'}) {
        !            69:         if ($target ne 'tex') {
        !            70:             &Apache::loncommon::simple_error_page($r,'','Not in a course');
        !            71:         } else {
        !            72:             $r->print('\textbf{Not in a course}\end{document}');
        !            73:         }
        !            74:         return OK;
        !            75:     }
        !            76: 
        !            77:     my $marker = (split(m{/},$r->uri))[4];
        !            78:     $marker=~s/\D//g;
        !            79: 
        !            80:     if (!$marker) {
        !            81:         if ($target ne 'tex') {
        !            82:             &Apache::loncommon::simple_error_page($r,'Invalid Call',
        !            83:                                                   'Invalid Call');
        !            84:         } else {
        !            85:             $r->print('\textbf{Invalid call}\end{document}');
        !            86:         }
        !            87:         return OK;
        !            88:     }
        !            89: 
        !            90:     my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
        !            91:     my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
        !            92:     my $chome = $env{'course.'.$env{'request.course.id'}.'.home'};
        !            93: 
        !            94:     if ($r->uri eq "/adm/$cdom/$cnum/$marker/exttool") {
        !            95:         my %toolhash=&Apache::lonnet::dump('exttool_'.$marker,$cdom,$cnum);
        !            96:         if ($target eq 'tex') {
        !            97:             $r->print(&mt('External Tool'));
        !            98:         } else {
        !            99:             if (($toolhash{'key'} ne '') && ($toolhash{'secret'} ne '') && ($toolhash{'url'} ne '')) {
        !           100:                 my %lti = &lti_params($r,\%toolhash);
        !           101:                 $r->print(&launch_html($toolhash{'url'},$toolhash{'key'},
        !           102:                                        $toolhash{'secret'},$toolhash{'title'},\%lti));
        !           103:             } else {
        !           104:                 &Apache::loncommon::simple_error_page($r,'External Tool Unavailable',
        !           105:                                                       'External Tool Unavailable');
        !           106:             }
        !           107:         }
        !           108:     } else {
        !           109:         if ($target ne 'tex') {
        !           110:             &Apache::loncommon::simple_error_page($r,'Invalid Call',
        !           111:                                                   'Invalid Call');
        !           112:         } else {
        !           113:             $r->print('\textbf{Invalid call}\end{document}');
        !           114:         }
        !           115:         return OK;
        !           116:     }
        !           117: 
        !           118:     &print_end_page($r,$target);
        !           119:     return OK;
        !           120: }
        !           121: 
        !           122: sub print_end_page {
        !           123:     my ($r,$target) = @_;
        !           124:     if ($target ne 'tex') {
        !           125:         $r->print(&Apache::loncommon::end_page());
        !           126:     } else {
        !           127:         $r->print('\end{document}');
        !           128:     }
        !           129: }
        !           130: 
        !           131: sub lti_params {
        !           132:     my ($r,$toolsref) = @_;
        !           133:     my ($version,$context_type,$msgtype,$toolname,$passback,$roster,$locale,
        !           134:         %fields,%rolesmap,%display,%custom,@userlangs);
        !           135:     if (ref($toolsref) eq 'HASH') {
        !           136:         $version = $toolsref->{'version'};
        !           137:         $toolname = $toolsref->{'title'};
        !           138:         $passback = $toolsref->{'passback'};
        !           139:         $roster = $toolsref->{'roster'};
        !           140:         $msgtype = $toolsref->{'messagetype'};
        !           141:         if (ref($toolsref->{'fields'}) eq 'HASH') {
        !           142:             %fields = %{$toolsref->{'fields'}};
        !           143:         }
        !           144:         if (ref($toolsref->{'roles'}) eq 'HASH') {
        !           145:             %rolesmap = %{$toolsref->{'roles'}};
        !           146:         }
        !           147:         if (ref($toolsref->{'display'}) eq 'HASH') {
        !           148:             %display = %{$toolsref->{'display'}};
        !           149:         }
        !           150:         if (ref($toolsref->{'custom'}) eq 'HASH') {
        !           151:             %custom = %{$toolsref->{'custom'}};
        !           152:         }
        !           153:     }
        !           154:     if ($version eq '') {
        !           155:         $version = 'LTI-1p0';
        !           156:     }
        !           157:     if ($context_type eq '') {
        !           158:         $context_type = 'CourseSection';
        !           159:     }
        !           160:     if ($msgtype eq '') {
        !           161:         $msgtype = 'basic-lti-launch-request';
        !           162:     }
        !           163:     my $lonhost = $r->dir_config('lonHostID');
        !           164:     my $loncaparev = $r->dir_config('lonVersion');
        !           165:     my $uname = $env{'user.name'};
        !           166:     my $udom = $env{'user.domain'};
        !           167:     my @possroles = qw(Instructor ContentDeveloper TeachingAssistant Learner);
        !           168:     my $ltirole = $rolesmap{$env{'request.role'}};
        !           169:     unless (grep(/^\Q$ltirole\E$/,@possroles)) {
        !           170:         $ltirole = 'Learner';
        !           171:     }
        !           172:     my $digest_user = &Encode::decode_utf8($uname.':'.$udom);
        !           173:     $digest_user = &Digest::SHA::sha1_hex($digest_user);
        !           174:     if ($env{'course.'.$env{'request.course.id'}.'.languages'} ne '') {
        !           175:         @userlangs=(@userlangs,split(/\s*(\,|\;|\:)\s*/,
        !           176:                     $env{'course.'.$env{'request.course.id'}.'.languages'}));
        !           177:     } else {
        !           178:         my %langhash = &getlangs($uname,$udom);
        !           179:         if ($langhash{'languages'} ne '') {
        !           180:             @userlangs = split(/\s*(\,|\;|\:)\s*/,$langhash{'languages'});
        !           181:         } else {
        !           182:             my %domdefs = &Apache::lonnet::get_domain_defaults($udom);
        !           183:             if ($domdefs{'lang_def'} ne '') {
        !           184:                 @userlangs = ($domdefs{'lang_def'});
        !           185:             }
        !           186:         }
        !           187:     }
        !           188:     if (scalar(@userlangs) == 1) {
        !           189:         $locale = $userlangs[0];
        !           190:     }
        !           191:     my ($title,$digest_symb);
        !           192:     my ($symb) = &Apache::lonnet::whichuser();
        !           193:     if ($symb) {
        !           194:         $digest_symb = &Encode::decode_utf8($symb);
        !           195:         $digest_symb = &Digest::SHA::sha1_hex($digest_symb);
        !           196:         my $navmap = Apache::lonnavmaps::navmap->new();
        !           197:         if (ref($navmap)) {
        !           198:             my $res = $navmap->getBySymb($symb);
        !           199:             if (ref($res)) {
        !           200:                 $title = $res->compTitle();
        !           201:             }
        !           202:         }
        !           203:     }
        !           204:     my %ltiparams = (
        !           205:         lti_version                            => $version,
        !           206:         lti_message_type                       => $msgtype,
        !           207:         resource_link_title                    => $title,
        !           208:         resource_link_id                       => $digest_symb,
        !           209:         tool_consumer_instance_guid            => $lonhost,
        !           210:         tool_consumer_info_product_family_code => 'loncapa',
        !           211:         tool_consumer_info_version             => $loncaparev,
        !           212:         user_id                                => $digest_user,
        !           213:         lis_person_sourcedid                   => $uname.':'.$udom,
        !           214:         roles                                  => $ltirole,
        !           215:         context_id                             => $env{'request.course.id'},
        !           216:         context_type                           => $context_type,
        !           217:         context_label                          => $env{'course.'.$env{'request.course.id'}.'.internal.coursecode'},
        !           218:         context_title                          => $env{'course.'.$env{'request.course.id'}.'.description'},
        !           219:         launch_presentation_locale             => $locale,
        !           220:     );
        !           221:     my $crshostname = $env{'course.'.$env{'request.course.id'}.'.home'};
        !           222:     my $crsprotocol = $Apache::lonnet::protocol{$crshostname};
        !           223:     if ($crshostname) {
        !           224:         my $crsprotocol = $Apache::lonnet::protocol{$crshostname};
        !           225:         unless ($crsprotocol eq 'https') {
        !           226:             $crsprotocol = 'http';
        !           227:         } 
        !           228:         if ($passback) {
        !           229:             if ($ltirole eq 'Learner') {
        !           230:                 $ltiparams{'lis_outcome_service_url'} = $crsprotocol.'//'.$crshostname.'/adm/ltipassback';
        !           231:                 $ltiparams{'ext_ims_lis_basic_outcome_url'} = $ltiparams{'lis_outcome_service_url'};
        !           232:                 $ltiparams{'lis_result_sourcedid'} = ''; #FIXME
        !           233:             }
        !           234:         }
        !           235:         if ($roster) {
        !           236:             if (&Apache::lonnet::allowed('opa',$env{'request.course.id'})) {
        !           237:                 $ltiparams{'ext_ims_lis_memberships_url'} = $crsprotocol.'//'.$crshostname.'/adm/ltiroster';
        !           238:                 $ltiparams{'ext_ims_lis_memberships_id'} = ''; #FIXME
        !           239:             }
        !           240:         }
        !           241:     }
        !           242:     if ($display{'target'}) {
        !           243:         $ltiparams{'launch_presentation_document_target'} = $display{'target'};
        !           244:     }
        !           245:     if ($display{'width'}) {
        !           246:         $ltiparams{'launch_presentation_width'} = $display{'width'};
        !           247:     }
        !           248:     if ($display{'height'}) {
        !           249:         $ltiparams{'launch_presentation_height'} = $display{'height'};
        !           250:     }
        !           251:     if ($fields{'firstname'}) {
        !           252:         $ltiparams{'lis_person_name_given'} = $env{'environment.firstname'};
        !           253:     }
        !           254:     if ($fields{'lastname'}) {
        !           255:         $ltiparams{'lis_person_name_family'} = $env{'environment.lastname'};
        !           256:     }
        !           257:     if ($fields{'fullname'}) {
        !           258:         $ltiparams{'lis_person_name_full'} = &Apache::loncommon::plainname($uname,$udom);
        !           259:     }
        !           260:     if ($fields{'email'}) {
        !           261:         my %emails = &Apache::loncommon::getemails($uname,$udom);
        !           262:         my $contact_email;
        !           263:         foreach my $email ('permanentemail','critnotification','notification') {
        !           264:             if ($email =~ /\@/) {
        !           265:                 $contact_email = $email;
        !           266:                 last;
        !           267:             }
        !           268:         }
        !           269:         $ltiparams{'lis_person_contact_email_primary'} = &contact_email;
        !           270:     }
        !           271:     if (keys(%custom)) {
        !           272:         foreach my $key (keys(%custom)) {
        !           273:             $ltiparams{'custom_'.$key} = $custom{$key};
        !           274:         }
        !           275:     }
        !           276:     foreach my $key (keys(%ltiparams)) {
        !           277:         $ltiparams{$key} = &Encode::decode_utf8($ltiparams{$key});
        !           278:     }
        !           279:     return %ltiparams;
        !           280: }
        !           281: 
        !           282: sub launch_html {
        !           283:     my ($url,$key,$secret,$toolname,$paramsref) = @_;
        !           284:     my $hashref = &sign_params($url,$key,$secret,$paramsref);
        !           285:     my $submittext = &mt('Launch [_1]',$toolname);
        !           286:     my $form = <<"END";
        !           287: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
        !           288: <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
        !           289: <body>
        !           290: <div id="LCltiLaunch">
        !           291: <form name="LCltiLaunchForm" action="$url" method="post" encType="application/x-www-form-urlencoded">
        !           292: <input type="submit" name="LCbasicltiSubmit" value="$submittext" />
        !           293: END
        !           294:     if (ref($hashref) eq 'HASH') {
        !           295:         foreach my $item (keys(%{$hashref})) {
        !           296:             $form .= '<input type="hidden" name="'.$item.'" value="'.$hashref->{$item}.'" id="id_'.$item.'" />'."\n";
        !           297:         }
        !           298:     }
        !           299:     $form .= "</form></div>\n";
        !           300:     $form .= <<"ENDJS";
        !           301: <script type="text/javascript">
        !           302:     document.getElementById("LCltiLaunch").style.display = "none";
        !           303:     nei = document.createElement('input');
        !           304:     nei.setAttribute('type','hidden');
        !           305:     nei.setAttribute('name','LCbasicltiSubmit');
        !           306:     nei.setAttribute('value','$submittext');
        !           307:     document.getElementById("LCltiLaunchForm").appendChild(nei);
        !           308:     document.LCltiLaunchForm.submit();
        !           309:  </script>
        !           310: ENDJS
        !           311:     $form .= "</body></html>\n";
        !           312:     return $form;
        !           313: }
        !           314: 
        !           315: sub sign_params {
        !           316:     my ($url,$key,$secret,$paramsref) = @_;
        !           317:     my $nonce = Digest::SHA::sha1_hex(sprintf("%06x%06x",rand(0xfffff0),rand(0xfffff0)));
        !           318:     my $request = Net::OAuth->request("request token")->new(
        !           319:             consumer_key => $key,
        !           320:             consumer_secret => $secret,
        !           321:             request_url => $url,
        !           322:             request_method => 'POST',
        !           323:             signature_method => 'HMAC-SHA1',
        !           324:             timestamp => time,
        !           325:             nonce => $nonce,
        !           326:             callback => 'about:blank',
        !           327:             extra_params => $paramsref,
        !           328:             version      => '1.0',
        !           329:             );
        !           330:     $request->sign;
        !           331:     return $request->to_hash();
        !           332: }
        !           333: 
        !           334: 1;

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>