File:  [LON-CAPA] / loncom / imspackages / imsexport.pm
Revision 1.9: download - view: text, annotated - select for diffs
Sat Mar 31 12:02:29 2012 UTC (12 years, 4 months ago) by raeburn
Branches: MAIN
CVS tags: HEAD
 Bug 6518
 - Utility to select resources/folders in a pop-up window.
 - Code moved from imsexport.pm to loncourserespicker.pm to
   facilitate reuse.

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

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