File:  [LON-CAPA] / loncom / imspackages / imsexport.pm
Revision 1.11: download - view: text, annotated - select for diffs
Tue Nov 12 04:54:57 2013 UTC (10 years, 5 months ago) by raeburn
Branches: MAIN
CVS tags: version_2_11_0_RC3, version_2_11_0_RC2, version_2_11_0, HEAD
- Conversion of simpleproblems to regular LON-CAPA .problem files.
  - Correct detection of string question type
  - Include initial text block in all question types
  - Add support for numerical question type.

    1: # The LearningOnline Network
    2: #
    3: # $Id: imsexport.pm,v 1.11 2013/11/12 04:54:57 raeburn Exp $
    4: #
    5: # Copyright Michigan State University Board of Trustees
    6: #
    7: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
    8: #
    9: # LON-CAPA is free software; you can redistribute it and/or modify
   10: # it under the terms of the GNU General Public License as published by
   11: # the Free Software Foundation; either version 2 of the License, or
   12: # (at your option) any later version.
   13: #
   14: # LON-CAPA is distributed in the hope that it will be useful,
   15: # but WITHOUT ANY WARRANTY; without even the implied warranty of
   16: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   17: # GNU General Public License for more details.
   18: #
   19: # You should have received a copy of the GNU General Public License
   20: # along with LON-CAPA; if not, write to the Free Software
   21: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   22: #
   23: # /home/httpd/html/adm/gpl.txt
   24: #
   25: # http://www.lon-capa.org/
   26: #
   27: 
   28: package Apache::imsexport;
   29: 
   30: use strict;
   31: use Apache::lonnet;
   32: use Apache::loncommon;
   33: use Apache::lonhtmlcommon;
   34: use Apache::lonnavmaps;
   35: use Apache::loncourserespicker;
   36: use Apache::londocs;
   37: use Apache::lonlocal;
   38: use Cwd;
   39: use LONCAPA qw(:DEFAULT :match);
   40: 
   41: sub exportcourse {
   42:     my $r=shift;
   43:     my $crstype = &Apache::loncommon::course_type();
   44:     my ($navmap,$errormsg) = 
   45:         &Apache::loncourserespicker::get_navmap_object($crstype,'imsexport'); 
   46:     unless (ref($navmap)) {
   47:         $r->print($errormsg);
   48:         return;
   49:     }
   50:     &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
   51:                                             ['finishexport']);
   52:     if ($env{'form.finishexport'}) {
   53:         &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
   54:                                             ['archive','discussion']);
   55:         my $outcome; 
   56:         my $format = $env{'form.format'};
   57:         my @exportitems = &Apache::loncommon::get_env_multiple('form.archive');
   58:         my @discussions = &Apache::loncommon::get_env_multiple('form.discussion');
   59:         if (@exportitems == 0 && @discussions == 0) {
   60:             $outcome =
   61:                 '<p class="LC_warning">'
   62:                .&mt('As you did not select any content items or discussions'
   63:                    .' for export, an IMS package has not been created.')
   64:                .'</p>'
   65:                .'<p>'
   66:                .&mt('Please [_1]go back[_2] to select either content items'
   67:                    .' or discussions for export.'
   68:                        ,'<a href="javascript:history.go(-1)">'
   69:                        ,'</a>')
   70:                .'</p>';
   71:         } else {
   72:             my $now = time;
   73:             my %symbs;
   74:             my $manifestok = 0;
   75:             my $imsresources;
   76:             my $tempexport;
   77:             my $copyresult;
   78:             my $testbank;
   79:             my $ims_manifest = &create_ims_store($now,\$manifestok,\$outcome,\$tempexport,$format,\$testbank);
   80:             if ($manifestok) {
   81:                 &build_package($now,$navmap,\@exportitems,\@discussions,\$outcome,$tempexport,\$copyresult,$ims_manifest,$format,$testbank);
   82:                 close($ims_manifest);
   83: 
   84: #Create zip file in prtspool
   85:                 my $imszipfile = '/prtspool/'.
   86:                 $env{'user.name'}.'_'.$env{'user.domain'}.'_'.
   87:                    time.'_'.rand(1000000000).'.zip';
   88:                 my $cwd = &Cwd::getcwd();
   89:                 my $imszip = '/home/httpd/'.$imszipfile;
   90:                 chdir $tempexport;
   91:                 open(OUTPUT, "zip -r $imszip *  2> /dev/null |");
   92:                 close(OUTPUT);
   93:                 chdir $cwd;
   94:                 $outcome .= '<p>'
   95:                            .&mt('[_1]Your IMS package[_2] is ready for download.'
   96:                                ,'<a href="'.$imszipfile.'">','</a>')
   97:                            .'</p>';
   98:                 if ($copyresult) {
   99:                     $outcome .= '<p class="LC_error">'
  100:                                .&mt('The following errors occurred during export - [_1]'
  101:                                    ,$copyresult)
  102:                                .'</p>';
  103:                 }
  104:             } else {
  105:                 $outcome = '<p class="LC_error">'
  106:                           .&mt('Unfortunately you will not be able to retrieve'
  107:                               .' an IMS archive of your course at this time,'
  108:                               .' because there was a problem creating a'
  109:                               .' manifest file.')
  110:                           .'</p>'
  111:                           .'<p><a href="javascript:history.go(-1)">'
  112:                           .&mt('Go Back')
  113:                           .'</a></p>';
  114:             }
  115:         }
  116:         $r->print(&Apache::loncommon::start_page('Export '.$crstype.' to IMS Package'));
  117:         $r->print(&Apache::lonhtmlcommon::breadcrumbs('IMS Export'));
  118:         $r->print(&Apache::londocs::startContentScreen('tools')); 
  119:         $r->print($outcome);
  120:         $r->print(&Apache::londocs::endContentScreen());
  121:         $r->print(&Apache::loncommon::end_page());
  122:     } else {
  123:         $r->print(&Apache::loncourserespicker::create_picker($navmap,'imsexport',
  124:                                                              'exportdoc',$crstype));
  125:     }
  126:     return;
  127: }
  128: 
  129: sub create_ims_store {
  130:     my ($now,$manifestok,$outcome,$tempexport,$format,$testbank) = @_;
  131:     $$tempexport = $Apache::lonnet::perlvar{'lonDaemons'}.'/tmp/ims_exports';
  132:     my $ims_manifest;
  133:     if (!-e $$tempexport) {
  134:         mkdir($$tempexport,0700);
  135:     }
  136:     $$tempexport .= '/'.$now;
  137:     if (!-e $$tempexport) {
  138:         mkdir($$tempexport,0700);
  139:     }
  140:     $$tempexport .= '/'.$env{'user.domain'}.'_'.$env{'user.name'};
  141:     if (!-e $$tempexport) {
  142:         mkdir($$tempexport,0700);
  143:     }
  144:     if (!-e "$$tempexport/resources") {
  145:         mkdir("$$tempexport/resources",0700);
  146:     }
  147: # open manifest file
  148:     my $manifest = '/imsmanifest.xml';
  149:     my $manifestfilename = $$tempexport.$manifest;
  150:     if ($ims_manifest = Apache::File->new('>'.$manifestfilename)) {
  151:         $$manifestok=1;
  152:         print $ims_manifest
  153: '<?xml version="1.0" encoding="UTF-8"?>'."\n".
  154: '<manifest xmlns="http://www.imsglobal.org/xsd/imscp_v1p1"'.
  155: ' xmlns:imsmd="http://www.imsglobal.org/xsd/imsmd_v1p2"'.
  156: ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'.
  157: ' identifier="MANIFEST-'.$env{'request.course.id'}.'-'.$now.'"'.
  158: '  xsi:schemaLocation="http://www.imsglobal.org/xsd/imscp_v1p1imscp_v1p1.xsd'.
  159: '  http://www.imsglobal.org/xsd/imsmd_v1p2 imsmd_v1p2p2.xsd">'."\n".
  160: '  <metadata>
  161:     <schema></schema>
  162:     <imsmd:lom>
  163:       <imsmd:general>
  164:         <imsmd:identifier>'.$env{'request.course.id'}.'</imsmd:identifier>
  165:         <imsmd:title>
  166:           <imsmd:langstring xml:lang="en">'.$env{'course.'.$env{'request.course.id'}.'.description'}.'</imsmd:langstring>
  167:         </imsmd:title>
  168:       </imsmd:general>
  169:     </imsmd:lom>
  170:   </metadata>'."\n".
  171: '  <organizations default="ORG-'.$env{'request.course.id'}.'-'.$now.'">'."\n".
  172: '    <organization identifier="ORG-'.$env{'request.course.id'}.'-'.$now.'"'.
  173: ' structure="hierarchical">'."\n".
  174: '      <title>'.$env{'course.'.$env{'request.course.id'}.'.description'}.'</title>';
  175:         if ($format eq 'plaintext') {
  176:             my $testbankfilename = $$tempexport.'/testbank.txt';
  177:             $$testbank = Apache::File->new('>'.$testbankfilename);
  178:         }
  179:     } else {
  180:         $$outcome .= 'An error occurred opening the IMS manifest file.<br />'
  181: ;
  182:     }
  183:     return $ims_manifest;
  184: }
  185: 
  186: sub build_package {
  187:     my ($now,$navmap,$exportitems,$discussions,$outcome,$tempexport,$copyresult,
  188:         $ims_manifest,$format,$testbank) = @_;
  189: # first iterator to look for dependencies
  190:     my $it = $navmap->getIterator(undef,undef,undef,1,undef,undef);
  191:     my $curRes;
  192:     my $count = 0;
  193:     my $depth = 0;
  194:     my $lastcontainer = 0;
  195:     my %parent = ();
  196:     my @dependencies = ();
  197:     my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
  198:     my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
  199:     while ($curRes = $it->next()) {
  200:         if (ref($curRes)) {
  201:             $count ++;
  202:         }
  203:         if ($curRes == $it->BEGIN_MAP()) {
  204:             $depth++;
  205:             $parent{$depth} = $lastcontainer;
  206:         }
  207:         if ($curRes == $it->END_MAP()) {
  208:             $depth--;
  209:             $lastcontainer = $parent{$depth};
  210:         }
  211:         if (ref($curRes)) {
  212:             if ($curRes->is_sequence() || $curRes->is_page()) {
  213:                 $lastcontainer = $count;
  214:             }
  215:             if (grep(/^$count$/,@$exportitems)) {
  216:                 &get_dependencies($exportitems,\%parent,$depth,\@dependencies);
  217:             }
  218:         }
  219:     }
  220: # second iterator to build manifest and store resources
  221:     $it = $navmap->getIterator(undef,undef,undef,1,undef,undef);
  222:     $depth = 0;
  223:     my $prevdepth;
  224:     $count = 0;
  225:     my $imsresources;
  226:     my $pkgdepth;
  227:     my $currdirpath = 'Top';
  228:     while ($curRes = $it->next()) {
  229:         if ($curRes == $it->BEGIN_MAP()) {
  230:             $prevdepth = $depth;
  231:             $depth++;
  232:         }
  233:         if ($curRes == $it->END_MAP()) {
  234:             $prevdepth = $depth;
  235:             $depth--;
  236:         }
  237: 
  238:         if (ref($curRes)) {
  239:             $count ++;
  240:             if ((grep(/^$count$/,@$exportitems)) || (grep(/^$count$/,@dependencies))) {
  241:                 my $symb = $curRes->symb();
  242:                 my $isvisible = 'true';
  243:                 my $resourceref;
  244:                 if ($curRes->randomout()) {
  245:                     $isvisible = 'false';
  246:                 }
  247:                 unless ($curRes->is_sequence()) {
  248:                     $resourceref = 'identifierref="RES-'.$env{'request.course.id'}.'-'.$count.'"';
  249:                 }
  250:                 my $step = $prevdepth - $depth;
  251:                 if (($step >= 0) && ($count > 1)) {
  252:                     while ($step >= 0) {
  253:                         print $ims_manifest "\n".'  </item>'."\n";
  254:                         $step --;
  255:                     }
  256:                 }
  257:                 $prevdepth = $depth;
  258: 
  259:                 my $itementry =
  260:               '<item identifier="ITEM-'.$env{'request.course.id'}.'-'.$count.
  261:               '" isvisible="'.$isvisible.'" '.$resourceref.'>'.
  262:               '<title>'.$curRes->title().'</title>';
  263:                 print $ims_manifest "\n".$itementry;
  264: 
  265:                 if ($curRes->is_sequence()) {
  266:                     $currdirpath = 'Top';
  267:                     my $pcslist = $curRes->map_hierarchy();
  268:                     if ($pcslist ne '') {
  269:                         foreach my $pc (split(/,/,$pcslist),$curRes->map_pc()) {
  270:                             next if ($pc <= 1);
  271:                             my $res = $navmap->getByMapPc($pc);
  272:                             if (ref($res)) {
  273:                                 my $encloser = $res->title();
  274:                                 if ($encloser) {
  275:                                     if ($currdirpath) {
  276:                                         $currdirpath .= ' -> ';
  277:                                     }
  278:                                     $currdirpath .= $encloser;
  279:                                 }
  280:                             }
  281:                         }
  282:                     }
  283:                 } else {
  284:                     my $content_file;
  285:                     my @hrefs = ();
  286:                     &process_content($count,$curRes,$cdom,$cnum,$symb,\$content_file,\@hrefs,$copyresult,$tempexport,$format,$currdirpath,$testbank);
  287:                     if ($content_file) {
  288:                         $imsresources .= "\n".
  289:                      '   <resource identifier="RES-'.$env{'request.course.id'}.'-'.$count.
  290:                      '" type="webcontent" href="'.$content_file.'">'."\n".
  291:                      '       <file href="'.$content_file.'" />'."\n";
  292:                         foreach my $item (@hrefs) {
  293:                             $imsresources .=
  294:                      '        <file href="'.$item.'" />'."\n";
  295:                         }
  296:                         if (grep(/^$count$/,@$discussions)) {
  297:                             my $ressymb = $symb;
  298:                             my $mode;
  299:                             if ($ressymb =~ m|adm/($match_domain)/($match_username)/(\d+)/bulletinboard$|) {
  300:                                 unless ($ressymb =~ m|adm/wrapper/adm|) {
  301:                                     $ressymb = 'bulletin___'.$3.'___adm/wrapper/adm/'.$1.'/'.$2.'/'.$3.'/bulletinboard';
  302:                                 }
  303:                                 $mode = 'board';
  304:                             }
  305:                             my %extras = (
  306:                                           caller => 'imsexport',
  307:                                           tempexport => $tempexport.'/resources',
  308:                                           count => $count
  309:                                          );
  310:                             my $discresult = &Apache::lonfeedback::list_discussion($mode,undef,$ressymb,\%extras);
  311:                         }
  312:                         $imsresources .= '    </resource>'."\n";
  313:                     }
  314:                 }
  315:                 $pkgdepth = $depth;
  316:             }
  317:         }
  318:     }
  319:     while ($pkgdepth > 0) {
  320:         print $ims_manifest "    </item>\n";
  321:         $pkgdepth --;
  322:     }
  323:     my $resource_text = qq|
  324:     </organization>
  325:   </organizations>
  326:   <resources>
  327:     $imsresources
  328:   </resources>
  329: </manifest>
  330:     |;
  331:     print $ims_manifest $resource_text;
  332: }
  333: 
  334: sub get_dependencies {
  335:     my ($exportitems,$parent,$depth,$dependencies) = @_;
  336:     if ($depth > 1) {
  337:         if ((!grep(/^$$parent{$depth}$/,@$exportitems)) && (!grep(/^$$parent{$depth}$/,@$dependencies))) {
  338:             push(@{$dependencies},$$parent{$depth});
  339:             if ($depth > 2) {
  340:                 &get_dependencies($exportitems,$parent,$depth-1,$dependencies);
  341:             }
  342:         }
  343:     }
  344: }
  345: 
  346: sub process_content {
  347:     my ($count,$curRes,$cdom,$cnum,$symb,$content_file,$href,$copyresult,$tempexport,$format,$currdirpath,$testbank) = @_;
  348:     my $content_type;
  349:     my $message;
  350:     my @uploads = ();
  351:     if ($curRes->is_sequence()) {
  352:         $content_type = 'sequence';
  353:     } elsif ($curRes->is_page()) {
  354:         $content_type = 'page'; # need to handle individual items in pages.
  355:     } elsif ($symb =~ m-public/$cdom/$cnum/syllabus$-) {
  356:         $content_type = 'syllabus';
  357:         my $contents = &templatedpage($content_type);
  358:         if ($contents) {
  359:             $$content_file = &store_template($contents,$tempexport,$count,$content_type);
  360:         }
  361:     } elsif ($symb =~ m-\.sequence___\d+___ext-) {
  362:         $content_type = 'external';
  363:         my $title = $curRes->title;
  364:         my $contents =  &external($symb,$title);
  365:         if ($contents) {
  366:             $$content_file = &store_template($contents,$tempexport,$count,$content_type);
  367:         }
  368:     } elsif ($symb =~ m-adm/navmaps$-) {
  369:         $content_type =  'navmap';
  370:     } elsif ($symb =~ m-adm/[^/]+/[^/]+/(\d+)/smppg$-) {
  371:         $content_type = 'simplepage';
  372:         my $contents = &templatedpage($content_type,$1,$count,\@uploads);
  373:         if ($contents) {
  374:             $$content_file = &store_template($contents,$tempexport,$count,$content_type);
  375:         }
  376:     } elsif ($symb =~ m-lib/templates/simpleproblem\.problem$-) {
  377:         $content_type = 'simpleproblem';
  378:         my $contents =  &simpleproblem($symb);
  379:         if ($contents) {
  380:             $$content_file = &store_template($contents,$tempexport,$count,$content_type);
  381:         }
  382:     } elsif ($symb =~ m-lib/templates/examupload\.problem$-) {
  383:         $content_type = 'examupload';
  384:     } elsif ($symb =~ m-adm/($match_domain)/($match_username)/(\d+)/bulletinboard$-) {
  385:         $content_type = 'bulletinboard';
  386:         my $contents =  &templatedpage($content_type,$3,$count,\@uploads,$1,$2);
  387:         if ($contents) {
  388:             $$content_file = &store_template($contents,$tempexport,$count,$content_type);
  389:         }
  390:     } elsif ($symb =~ m-adm/([^/]+)/([^/]+)/aboutme$-) {
  391:         $content_type = 'aboutme';
  392:         my $contents =  &templatedpage($content_type,undef,$count,\@uploads,$1,$2);
  393:         if ($contents) {
  394:             $$content_file = &store_template($contents,$tempexport,$count,$content_type);
  395:         }
  396:     } elsif ($symb =~ m-\.(sequence|page)___\d+___uploaded/$cdom/$cnum/-) {
  397:         $$content_file = &replicate_content($cdom,$cnum,$tempexport,$symb,$count,\$message,$href,'uploaded');
  398:     } elsif ($symb =~ m-\.(sequence|page)___\d+___([^/]+)/([^/]+)-) {
  399:         my $canedit = 0;
  400:         if ($2 eq $env{'user.domain'} && $3 eq $env{'user.name'})  {
  401:             $canedit= 1;
  402:         }
  403: # only include problem code where current user is author
  404:         if (($format eq 'html') || ($format eq 'plaintext')) {
  405:             my $title = $curRes->title;
  406:             $$content_file = &replicate_content($cdom,$cnum,$tempexport,$symb,$count,\$message,$href,$format,$currdirpath,$title,$testbank);
  407:         } elsif ($format eq 'xml') {
  408:             if ($canedit) {
  409:                 $$content_file = &replicate_content($cdom,$cnum,$tempexport,$symb,$count,\$message,$href,'resource');
  410:             } else {
  411:                 $$content_file = &replicate_content($cdom,$cnum,$tempexport,$symb,$count,\$message,$href,'noedit');
  412:             }
  413:         }
  414:     } elsif ($symb =~ m-uploaded/$cdom/$cnum-) {
  415:         $$content_file = &replicate_content($cdom,$cnum,$tempexport,$symb,$count,\$message,$href,'uploaded');
  416:     }
  417:     if (@uploads > 0) {
  418:         foreach my $item (@uploads) {
  419:             my $uploadmsg = '';
  420:             &replicate_content($cdom,$cnum,$tempexport,$item,$count,\$uploadmsg,$href,'templateupload');
  421:             if ($uploadmsg) {
  422:                 $$copyresult .= $uploadmsg."\n";
  423:             }
  424:         }
  425:     }
  426:     if ($message) {
  427:         $$copyresult .= $message."\n";
  428:     }
  429: }
  430: 
  431: sub replicate_content {
  432:     my ($cdom,$cnum,$tempexport,$symb,$count,$message,$href,$caller,$currdirpath,
  433:         $title,$testbank) = @_;
  434:     my ($map,$ind,$url);
  435:     if ($caller eq 'templateupload') {
  436:         $url = $symb;
  437:         $url =~ s#//#/#g;
  438:     } else {
  439:         ($map,$ind,$url)=&Apache::lonnet::decode_symb($symb);
  440:     }
  441:     my $content;
  442:     my $filename;
  443:     my $repstatus;
  444:     my $content_name;
  445:     if ($url =~ m-/([^/]+)$-) {
  446:         $filename = $1;
  447:         if (!-e $tempexport.'/resources') {
  448:             mkdir($tempexport.'/resources',0700);
  449:         }
  450:         if (!-e $tempexport.'/resources/'.$count) {
  451:             mkdir($tempexport.'/resources/'.$count,0700);
  452:         }
  453:         my $destination = $tempexport.'/resources/'.$count.'/'.$filename;
  454:         my $copiedfile;
  455:         if ($copiedfile = Apache::File->new('>'.$destination)) {
  456:             my $content;
  457:             if ($caller eq 'resource') {
  458:                 my $respath =  $Apache::lonnet::perlvar{'lonDocRoot'}.'/res';
  459:                 my $filepath = &Apache::lonnet::filelocation($respath,$url);
  460:                 $content = &Apache::lonnet::getfile($filepath);
  461:                 if ($content eq -1) {
  462:                     $$message = 'Could not copy file '.$filename;
  463:                 } else {
  464:                     &extract_media($url,$cdom,$cnum,\$content,$count,$tempexport,$href,$message,'resource');
  465:                     $repstatus = 'ok';
  466:                 }
  467:             } elsif ($caller eq 'uploaded' || $caller eq 'templateupload') {
  468:                 my $rtncode;
  469:                 $repstatus = &Apache::lonnet::getuploaded('GET',$url,$cdom,$cnum,\$content,$rtncode);
  470:                 if ($repstatus eq 'ok') {
  471:                     if ($url =~ /\.html?$/i) {
  472:                         &extract_media($url,$cdom,$cnum,\$content,$count,$tempexport,$href,$message,'uploaded');
  473:                     }
  474:                 } else {
  475:                     $$message = 'Could not render '.$url.' server message - '.$rtncode."<br />\n";
  476:                 }
  477:             } elsif (($caller eq 'noedit') || ($caller eq 'html') ||
  478:                      ($caller eq 'plaintext')) {
  479: # Need to render the resource without the LON-CAPA Internal header and the Post discussion footer, and then set $content equal to this.
  480:                 my %form = (
  481:                              grade_symb     => $symb,
  482:                              grade_courseid => $cdom.'_'.$cnum,
  483:                              grade_domain   => $env{'user.domain'},
  484:                              grade_username => $env{'user.name'},
  485:                              grade_imsexport => 1,
  486:                              instructor_comments => 'hide',
  487:                            );
  488:                 my $feedurl=&Apache::lonnet::clutter($url);
  489:                 my ($userview,$response)=&Apache::lonnet::ssi_body($feedurl,%form);
  490:                 if (ref($response)) {
  491:                     if ($response->is_success) {
  492:                         $content = $userview;
  493:                         $content =~ s/\Qonchange="javascript:setSubmittedPart('\E[^\']+\Q');"\E//g;
  494:                         $content =~ s/^\s*[\n\r]+$//;
  495:                         if ($caller eq 'plaintext') {
  496:                             my @lines = split(/[\n\r]+/,$content);
  497:                             my @tosave;
  498:                             my $foilcounter = 0;
  499:                             my @alphabet = ('a'..'z');
  500:                             my $mc_answer;
  501:                             foreach my $line (@lines) {
  502:                                 next if ($line =~ /^\s*$/);
  503:                                 if ($line =~ m{(|\Q<\label>\E)\Q<br />Incorrect:<label>\E}) {
  504:                                     $foilcounter ++;
  505:                                 } elsif ($line =~ m{(|\Q</label>\E)\Q<br />Correct:<b><label>\E}) {
  506:                                     $foilcounter ++;
  507:                                     $mc_answer = $alphabet[$foilcounter-1];
  508:                                 } elsif ($line !~ m{\Q</label>\E(|\Q</b>\E)\Q<br />\E}) {
  509:                                     $line =~ s/^(\s+|\s+)$//g;
  510:                                     $line =~ s{^\Q<b>\E([^<]+)\Q</b>\E$}{1};
  511:                                     $tosave[$foilcounter] .= $line.' ';
  512:                                 }
  513:                                 $content = join("\t",@tosave);
  514:                                 if ($mc_answer) {
  515:                                     $content .= "\t".$mc_answer."\n";
  516:                                 }
  517:                             }
  518:                             if (@tosave) {
  519:                                 my $qtype;
  520:                                 if ($mc_answer) {
  521:                                     $qtype = 'MC';
  522:                                 }
  523:                                 $content = $currdirpath."\t".$title."\t$qtype\t".join("\t",@tosave);
  524:                                 if ($mc_answer) {
  525:                                     $content .= "\t".$mc_answer;
  526:                                 }
  527:                                 $content .= "\n";
  528:                             }
  529:                         } else {
  530:                             $content = '<html><body>'.$content.'</body></html>';
  531:                         }
  532:                         if (($caller eq 'plaintext') && ($testbank)) {
  533:                             print $testbank $content;
  534:                         }
  535:                     } else {
  536:                         $content = 'Not the owner of this resource';
  537:                     }
  538:                 } else {
  539:                     $content = 'Not the owner of this resource';
  540:                 }
  541:                 $repstatus = 'ok';
  542:             }
  543:             if ($repstatus eq 'ok') {
  544:                 print $copiedfile $content;
  545:             }
  546:             close($copiedfile);
  547:         } else {
  548:             $$message = 'Could not open destination file for '.$filename."<br />\n";
  549:         }
  550:     } else {
  551:         $$message = 'Could not determine name of file for '.$symb."<br />\n";
  552:     }
  553:     if ($repstatus eq 'ok') {
  554:         $content_name = 'resources/'.$count.'/'.$filename;
  555:     }
  556:     return $content_name;
  557: }
  558: 
  559: sub extract_media {
  560:     my ($url,$cdom,$cnum,$content,$count,$tempexport,$href,$message,$caller) = @_;
  561:     my ($dirpath,$container);
  562:     my %allfiles = ();
  563:     my %codebase = ();
  564:     if ($url =~ m-(.*/)([^/]+)$-) {
  565:         $dirpath = $1;
  566:         $container = $2;
  567:     } else {
  568:         $dirpath = $url;
  569:         $container = '';
  570:     }
  571:     &Apache::lonnet::extract_embedded_items(undef,\%allfiles,\%codebase,$content);
  572:     foreach my $embed_file (keys(%allfiles)) {
  573:         my $filename;
  574:         if ($embed_file =~ m#([^/]+)$#) {
  575:             $filename = $1;
  576:         } else {
  577:             $filename = $embed_file;
  578:         }
  579:         my $newname = 'res/'.$filename;
  580:         my ($rtncode,$embed_content,$repstatus);
  581:         my $embed_url;
  582:         if ($embed_file =~ m-^/-) {
  583:             $embed_url = $embed_file;           # points to absolute path
  584:         } else {
  585:             if ($embed_file =~ m-https?://-) {
  586:                 next;                           # points to url
  587:             } else {
  588:                 $embed_url = $dirpath.$embed_file;  # points to relative path
  589:             }
  590:         }
  591:         if ($caller eq 'resource') {
  592:             my $respath =  $Apache::lonnet::perlvar{'lonDocRoot'}.'/res';
  593:             my $embed_path = &Apache::lonnet::filelocation($respath,$embed_url);
  594:             $embed_content = &Apache::lonnet::getfile($embed_path);
  595:             unless ($embed_content eq -1) {
  596:                 $repstatus = 'ok';
  597:             }
  598:         } elsif ($caller eq 'uploaded') {
  599:             $repstatus = &Apache::lonnet::getuploaded('GET',$embed_url,$cdom,$cnum,\$embed_content,$rtncode);
  600:         }
  601:         if ($repstatus eq 'ok') {
  602:             my $destination = $tempexport.'/resources/'.$count.'/res';
  603:             if (!-e "$destination") {
  604:                 mkdir($destination,0755);
  605:             }
  606:             $destination .= '/'.$filename;
  607:             my $copiedfile;
  608:             if ($copiedfile = Apache::File->new('>'.$destination)) {
  609:                 print $copiedfile $embed_content;
  610:                 push(@{$href},'resources/'.$count.'/res/'.$filename);
  611:                 my $attrib_regexp = '';
  612:                 if (@{$allfiles{$embed_file}} > 1) {
  613:                     $attrib_regexp = join('|',@{$allfiles{$embed_file}});
  614:                 } else {
  615:                     $attrib_regexp = $allfiles{$embed_file}[0];
  616:                 }
  617:                 $$content =~ s#($attrib_regexp\s*=\s*['"]?)\Q$embed_file\E(['"]?)#$1$newname$2#gi;
  618:                 if ($caller eq 'resource' && $container =~ /\.(problem|library)$/) {
  619:                     $$content =~ s#\Q$embed_file\E#$newname#gi;
  620:                 }
  621:             }
  622:         } else {
  623:             $$message .= 'replication of embedded file - '.$embed_file.' in '.$url.' failed, reason -'.$rtncode."<br />\n";
  624:         }
  625:     }
  626:     return;
  627: }
  628: 
  629: sub store_template {
  630:     my ($contents,$tempexport,$count,$content_type) = @_;
  631:     if ($contents) {
  632:         if ($tempexport) {
  633:             if (!-e $tempexport.'/resources') {
  634:                 mkdir($tempexport.'/resources',0700);
  635:             }
  636:             if (!-e $tempexport.'/resources/'.$count) {
  637:                 mkdir($tempexport.'/resources/'.$count,0700);
  638:             }
  639:             my $destination = $tempexport.'/resources/'.$count.'/'.$content_type.'.xml';
  640:             my $storetemplate;
  641:             if ($storetemplate = Apache::File->new('>'.$destination)) {
  642:                 print $storetemplate $contents;
  643:                 close($storetemplate);
  644:             }
  645:             if ($content_type eq 'external') {
  646:                 return 'resources/'.$count.'/'.$content_type.'.html';
  647:             } else {
  648:                 return 'resources/'.$count.'/'.$content_type.'.xml';
  649:             }
  650:         }
  651:     }
  652: }
  653: 
  654: sub simpleproblem  {
  655:     my ($symb) = @_;
  656:     my $output;
  657:     my %qparms = &Apache::lonnet::dump('resourcedata',
  658:                   $env{'course.'.$env{'request.course.id'}.'.domain'},
  659:                   $env{'course.'.$env{'request.course.id'}.'.num'},
  660:                   $env{'request.course.id'}.'.'.$symb);
  661:     if ($symb) {
  662:         my $prefix=$env{'request.course.id'}.'.'.$symb.'.0.';
  663:         my $qtype=$qparms{$prefix.'questiontype'};
  664:         my $qtext=$qparms{$prefix.'questiontext'};
  665:         my $hint=$qparms{$prefix.'hinttext'};
  666:         my %values = ();
  667:         my %foils = ();
  668:         if (($qtype eq 'radio') || ($qtype eq 'option')) {
  669:             my $maxfoils=$qparms{$prefix.'maxfoils'};
  670:             my $randomize=$qparms{$prefix.'randomize'};
  671:             if ($qtype eq 'option') {
  672:                 my $options=$qparms{$prefix.'options'};
  673:                 %values = &evaloptionhash($options);
  674:                 $output .= qq|
  675: <problem>
  676:   <startouttext />$qtext<endouttext />
  677:   <optionresponse max="$maxfoils" randomize="$randomize">
  678:     <foilgroup options="$options">
  679: |;
  680:                 for (my $k=0; $k<10; $k++) {
  681:                     my $iter = $k+1;
  682:                     $output .= '   <foil name="foil'.$k.'" value="'.$qparms{$prefix.'value'.$iter}.'"';
  683:                     $output .= ' location="'.$qparms{$prefix.'position'.$iter}.'" ';
  684:                     $output .= '><startouttext />'.$qparms{$prefix.'text'.$iter}.'<endouttext /></foil>'."\n";
  685:                 }
  686:                 chomp($output);
  687:                 $output .= qq|
  688:     </foilgroup>
  689: |;
  690:                 if ($hint) {
  691:                     $output .= '
  692:     <hintgroup>
  693:      <hintpart on="default">
  694:       <startouttext />'.$hint.'<endouttext/>
  695:      </hintpart>
  696:     </hintgroup>';
  697:                 }
  698:                 $output .= qq|
  699:   </optionresponse>
  700: </problem>
  701: |;
  702:             } else {
  703:                 $output .= qq|
  704: <problem>
  705:   <startouttext />$qtext<endouttext />
  706:   <radiobuttonresponse max="$maxfoils" randomize="$randomize">
  707:   <foilgroup>
  708: |;
  709:                 for (my $k=0; $k<10; $k++) {
  710:                     my $iter = $k+1;
  711:                     $output .= '   <foil name="foil'.$k.'" value="'.$qparms{$prefix.'value'.$iter}.'"';
  712:                     $output .= ' location="'.$qparms{$prefix.'position'.$iter}.'" ';
  713:                     $output .= '><startouttext />'.$qparms{$prefix.'text'.$iter}.'<endouttext /></foil>'."\n";
  714:                 }
  715:                 chomp($output);
  716:                 $output .= qq|
  717:    </foilgroup>
  718: |;
  719:                 if ($hint) {
  720:                     $output .= '
  721:    <hintgroup>
  722:     <hintpart on="default">
  723:      <startouttext />'.$hint.'<endouttext/>
  724:     </hintpart>
  725:    </hintgroup>';
  726:                 }
  727:                 $output .= qq|
  728:   </radiobuttonresponse>
  729: </problem>
  730: |;
  731:             }
  732:         } elsif ($qtype eq 'string') {
  733:             my $stringanswer = $qparms{$prefix.'stringanswer'};
  734:             my $stringtype=$qparms{$prefix.'stringtype'};
  735:             $output .= qq|
  736: <problem>
  737:   <stringresponse answer="$stringanswer" type="$stringtype">
  738:   <startouttext />$qtext<endouttext />
  739:     <textline />
  740:             |;
  741:             if ($hint) {
  742:                 $output .= '
  743:    <hintgroup>
  744:     <hintpart on="default">
  745:      <startouttext />'.$hint.'<endouttext/>
  746:     </hintpart>
  747:    </hintgroup>';
  748:             }
  749:             $output .= qq|
  750:   </stringresponse>
  751: </problem>
  752: |;
  753:         } elsif ($qtype eq 'numerical') {
  754:             my $sigfigs = $qparms{$prefix.'numericalsigfigs'};
  755:             my $unit = $qparms{$prefix.'numericalunit'};
  756:             my $answer = $qparms{$prefix.'numericalanswer'};
  757:             my $tolerance = $qparms{$prefix.'numericaltolerance'};
  758:             my $format = $qparms{$prefix.'numericalformat'};
  759:             my $scriptblock = $qparms{$prefix.'numericalscript'};
  760:             $output .= qq|
  761: <problem>
  762: |;
  763:             if ($scriptblock) {
  764:                 $output .= qq|
  765: <script type="loncapa/perl">
  766: $scriptblock
  767: </script>|;
  768:              }
  769:              $output .= qq|
  770: <startouttext />$qtext<endouttext />
  771: <numericalresponse answer="$answer" |;
  772:              if ($unit ne '') {
  773:                  $output .= qq|unit="$unit" |;
  774:              }
  775:              if ($format ne '') {
  776:                  $output .= qq|format="$format" |;
  777:              }
  778:              $output =~ s{ $}{};
  779:              $output .= '>';
  780:              if ($tolerance ne '') {
  781:                  $output .= qq|
  782:   <responseparam name="tol" type="tolerance" default="$tolerance" description="Numerical Tolerance" />|;
  783:              }
  784:              if ($sigfigs) {
  785:                  $output .= qq|
  786:   <responseparam name="sig" type="int_range" default="$sigfigs" description="Significant Digits" />|;
  787:              }
  788:              $output .= qq|
  789:   <textline />|;
  790:             if ($hint) {
  791:                 $output .= qq|
  792:   <hintgroup>
  793:     <hintpart on="default">
  794:       <startouttext />'.$hint.'<endouttext/>
  795:     </hintpart>
  796:   </hintgroup>|;
  797:             }
  798:             $output .= qq|
  799: </numericalresponse>
  800: </problem>
  801: |;
  802:         } else {
  803:             $output .= qq|
  804: <problem>
  805:   <startouttext />$qtext<endouttext />
  806:   <essayresponse>
  807:   <textfield></textfield>
  808:   </essayresponse>
  809: </problem>
  810: |;
  811:         }
  812:     }
  813:     return $output;
  814: }
  815: 
  816: sub evaloptionhash {
  817:     my $options=shift;
  818:     $options=~s/^\(\'//;
  819:     $options=~s/\'\)$//;
  820:     my %returnhash=();
  821:     foreach (split(/\'\,\'/,$options)) {
  822:         $returnhash{$_}=$_;
  823:     }
  824:     return %returnhash;
  825: }
  826: 
  827: sub external {
  828:     my ($symb,$title) = @_;
  829:     my $output;
  830:     if ($symb =~  m-\.sequence___\d+___ext(.+)$-) {
  831:         my $exturl = &unescape($1);
  832:         $output = qq|
  833: <html>
  834: <head><title>$title</title>
  835: </head>
  836: <frameset rows="0,*" border="0">
  837: <frame src='' />
  838: <frame src="http://$exturl" name="external" />
  839: </frameset>
  840: </html>
  841:         |;
  842:     }
  843:     return $output;
  844: }
  845: 
  846: sub templatedpage {
  847:     my ($content_type,$timestamp,$count,$uploads,$udom,$uname) = @_;
  848:     my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
  849:     my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};  
  850:     my $output = '
  851: <'.$content_type.'>';
  852:     my %syllabusdata=();
  853:     my %syllabusfields=();
  854:     if ($content_type eq 'syllabus') {
  855:         %syllabusfields=&Apache::lonlocal::texthash(
  856:            'aaa_instructorinfo' => 'Instructor Information',
  857:            'bbb_description'    => 'Course Description',
  858:            'ccc_prereq'         => 'Prerequisites',
  859:            'cdc_classhours'     => 'Class Hours',
  860:            'ddd_officehours'    => 'Office Hours',
  861:            'eee_helproom'       => 'Helproom Hours',
  862:            'efe_projectinfo'    => 'Project Information',
  863:            'fff_examinfo'       => 'Exam Information',
  864:            'fgf_deadlines'      => 'Deadlines',
  865:            'ggg_grading'        => 'Grading Information',
  866:            'hhh_readings'       => 'Readings',
  867:            'iii_coursepack'     => 'Coursepack',
  868:            'jjj_weblinks'       => 'Web Links',
  869:            'kkk_textbook'       => 'Textbook',
  870:            'lll_includeurl'     => 'URLs To Include in Syllabus'
  871:         );
  872:         %syllabusdata = &Apache::lonnet::dump('syllabus',$cdom,$cnum);
  873: 
  874:     } elsif ($content_type eq 'simplepage') {
  875:         %syllabusfields=&Apache::lonlocal::texthash(
  876:            'aaa_title'         => 'Page Title',
  877:            'bbb_content'       => 'Content',
  878:            'ccc_webreferences' => 'Web References'
  879:         );
  880:         %syllabusdata = &Apache::lonnet::dump('smppage_'.$timestamp,$cdom,$cnum);
  881:     } elsif ($content_type eq 'bulletinboard') {
  882:         %syllabusfields=&Apache::lonlocal::texthash(
  883:            'aaa_title'         => 'Topic',
  884:            'bbb_content'       => 'Task',
  885:            'ccc_webreferences' => 'Web References'
  886:         );
  887:         %syllabusdata = &Apache::lonnet::dump('bulletinpage_'.$timestamp,$cdom,$cnum);
  888:     } elsif ($content_type eq 'aboutme') {
  889:         %syllabusdata=&Apache::lonnet::dump('aboutme',$udom,$uname);
  890:         %syllabusfields=&Apache::lonlocal::texthash(
  891:            'aaa_contactinfo'   => 'Contact Information',
  892:            'bbb_aboutme'       => 'Personal Information',
  893:            'ccc_webreferences' => 'Web References'
  894:         );
  895:         $output .= qq|
  896:   <username>$uname</username>
  897:   <domain>$udom</domain>
  898: |;
  899:     }
  900:     foreach (sort keys %syllabusfields) {
  901:         $output .= qq|
  902: <$_>
  903:  <name>$syllabusfields{$_}</name>
  904:  <value>$syllabusdata{$_}</value>
  905: </$_>|;
  906:     }
  907:     if (defined($syllabusdata{'uploaded.photourl'})) {
  908:         if ($syllabusdata{'uploaded.photourl'} =~  m-/([^/]+)$-) {
  909:             push @$uploads, $syllabusdata{'uploaded.photourl'};
  910:         }
  911:         $output .= '
  912: <photo>
  913:  <filename>'.$count.'/'.$1.'</filename>
  914: </photo>';
  915:     }
  916:     $output .= '
  917: </'.$content_type.'>';
  918:     return $output;
  919: }
  920: 
  921: 1;
  922: 
  923: __END__
  924: 
  925: =head1 NAME
  926: 
  927: Apache::imsexport.pm
  928: 
  929: =head1 SYNOPSIS
  930: 
  931: This is part of the LearningOnline Network with CAPA project
  932: described at http://www.lon-capa.org.
  933: 
  934: =head1 SUBROUTINES
  935: 
  936: =over
  937: 
  938: =item exportcourse()
  939: 
  940: =item create_ims_store()
  941: 
  942: =item build_package()
  943: 
  944: =item get_dependencies()
  945: 
  946: =item process_content()
  947: 
  948: =item replicate_content()
  949: 
  950: =item extract_media()
  951: 
  952: =item store_template()
  953: 
  954: =item simpleproblem()
  955: 
  956: =item evaloptionhash()
  957: 
  958: =item external()
  959: 
  960: =item templatedpage()
  961: 
  962: =back
  963: 
  964: =cut
  965: 

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