File:  [LON-CAPA] / loncom / publisher / loncleanup.pm
Revision 1.22: download - view: text, annotated - select for diffs
Sun Jul 23 13:16:29 2023 UTC (9 months, 1 week ago) by raeburn
Branches: MAIN
CVS tags: version_2_12_X, HEAD
- Fix typo in rev 1.21

    1: # The LearningOnline Network with CAPA
    2: # Handler to cleanup XML files
    3: #
    4: # $Id: loncleanup.pm,v 1.22 2023/07/23 13:16:29 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: ###
   30: 
   31: package Apache::loncleanup;
   32: 
   33: use strict;
   34: use Apache::File;
   35: use File::Copy;
   36: use Apache::Constants qw(:common :http :methods);
   37: use Apache::loncommon();
   38: use Apache::lonhtmlcommon();
   39: use Apache::lonlocal;
   40: use Apache::lonnet;
   41: use lib '/home/httpd/lib/perl/';
   42: use LONCAPA;
   43: use HTML::Entities();
   44:  
   45: 
   46: sub latextrans {
   47:     my $symbolfont=shift;
   48:     my %latexsymb=(
   49: 		   '±' => '\pm',
   50: 		   '´' => '\times',
   51: 		   '¸' => '\div',
   52: 		   'Ò' => '(R)',
   53: 		   'Ó' => '\copy',
   54: 		   'Ø' => '\neg',
   55: 		   'â' => '(R)',
   56: 		   'ã' => '\copy',
   57: 		   '¦' => 'f',
   58: 		   'A' => '\Alpha',
   59: 		   'B' => '\Beta',
   60: 		   'G' => '\Gamma',
   61: 		   'D' => '\Delta',
   62: 		   'E' => '\Epsilon',
   63: 		   'Z' => '\Zeta',
   64: 		   'H' => '\Eta',
   65: 		   'Q' => '\Theta',
   66: 		   'I' => '\Iota',
   67: 		   'K' => '\Kappa',
   68: 		   'L' => '\Lambda',
   69: 		   'M' => '\Mu',
   70: 		   'N' => '\Nu',
   71: 		   'X' => '\Xi',
   72: 		   'O' => '\Omicron',
   73: 		   'P' => '\Pi',
   74: 		   'R' => '\Rho',
   75: 		   'S' => '\Sigma',
   76: 		   'T' => '\Tau',
   77: 		   'U' => 'Y',
   78: 		   'F' => '\Phi',
   79: 		   'C' => '\Chi',
   80: 		   'Y' => '\Psi',
   81: 		   'W' => '\Omega',
   82: 		   'a' => '\alpha',
   83: 		   'b' => '\beta',
   84: 		   'g' => '\gamma',
   85: 		   'd' => '\delta',
   86: 		   'e' => '\epsilon',
   87: 		   'z' => '\zeta',
   88: 		   'h' => '\eta',
   89: 		   'q' => '\theta',
   90: 		   'i' => '\iota',
   91: 		   'k' => '\kappa',
   92: 		   'l' => '\lambda',
   93: 		   'm' => '\mu',
   94: 		   'n' => '\nu',
   95: 		   'x' => '\xi',
   96: 		   'o' => '\omicron',
   97: 		   'p' => '\pi',
   98: 		   'r' => '\rho',
   99: 		   'V' => '\sigmaf',
  100: 		   's' => '\sigma',
  101: 		   't' => '\tau',
  102: 		   'u' => '\upsilon',
  103: 		   'f' => '\phi',
  104: 		   'c' => '\chi',
  105: 		   'y' => '\psi',
  106: 		   'w' => '\omega',
  107: 		   'J' => '\vartheta',
  108: 		   'j' => '\varphi',
  109: 		   'v' => '\varpi',
  110: 		   '¡' => '\Upsilon',
  111: 		   '¢' => "'",
  112: 		   '¤' => '/',
  113: 		   '²' => '"',
  114: 		   '¼' => '\ldots',
  115: 		   'À' => '\aleph',
  116: 		   'Á' => '\Im',
  117: 		   'Â' => '\Re',
  118: 		   'Ã' => '\wp',
  119: 		   'Ô' => '^{TM}',
  120: 		   'ä' => '^{TM}',
  121: 		   'ð' => 'EUR',
  122: 		   '«' => '\leftrightarrow',
  123: 		   '¬' => '\leftarrow',
  124: 		   '­' => '\uparrow',
  125: 		   '®' => '\rightarrow',
  126: 		   '¯' => '\downarraw',
  127: 		   '¿' => '\hookleftarrow',
  128: 		   'Û' => '\Leftrightarrow',
  129: 		   'Ü' => '\Leftarrow',
  130: 		   'Ý' => '\Uparrow',
  131: 		   'Þ' => '\Rightarrow',
  132: 		   'ß' => '\Downarrow',
  133: 		   '"' => '\forall',
  134: 		   '$' => '\exists',
  135: 		   ''' => '\ni',
  136: 		   '*' => '\ast',
  137: 		   '-' => '-',
  138: 		   '@' => '\cong',
  139: 		   '\' => '\therefore',
  140: 		   '^' => '\perp',
  141: 		   '~' => '\sim',
  142: 		   '£' => '\leq',
  143: 		   '¥' => '\infty',
  144: 		   '³' => '\geq',
  145: 		   'µ' => '\propto',
  146: 		   '¶' => '\partial',
  147: 		   '·' => '\cdot',
  148: 		   '¹' => '\not=',
  149: 		   'º' => '\equiv',
  150: 		   '»' => '\approx',
  151: 		   'Ä' => '\otimes',
  152: 		   'Å' => '\oplus',
  153: 		   'Æ' => '\emptyset',
  154: 		   'Ç' => '\cap',
  155: 		   'È' => '\cup',
  156: 		   'É' => '\supset',
  157: 		   'Ê' => '\supseteq',
  158: 		   'Ë' => '\not\subset',
  159: 		   'Ì' => '\subset',
  160: 		   'Í' => '\subseteq',
  161: 		   'Î' => '\in',
  162: 		   'Ï' => '\not\in',
  163: 		   'Ð' => '\angle',
  164: 		   'Ñ' => '\nabla',
  165: 		   'Õ' => '\prod',
  166: 		   'Ö' => '\surd',
  167: 		   '×' => '\cdot',
  168: 		   'Ù' => '\wedge',
  169: 		   'Ú' => '\wee',
  170: 		   'å' => '\sum',
  171: 		   'ò' => '\int',
  172: 		   'á' => '\langle',
  173: 		   'ñ' => '\rangle',
  174: 		   'à' => '\diamondsuit',
  175: 		   '§' => '\clubsuit',
  176: 		   '¨' => '\diamondsuit',
  177: 		   '©' => '\heartsuit',
  178: 		   'ª' => '\spadesuit'
  179: 		   );
  180:     my $output='';
  181:     my $char='';
  182:     my $entitymode=0;
  183:     for (my $i=0; $i<length($symbolfont); $i++) {
  184:         my $newchar=substr($symbolfont,$i,1);
  185:         $char.=$newchar;
  186:         if ($newchar eq '&') { $entitymode=1; }
  187:         if (($entitymode) && ($newchar ne ';')) { next; }
  188:         my $latex=$latexsymb{$char};
  189: 	if ($latex) {
  190: 	    $output.=$latex;
  191: 	} else {
  192: 	    $output.=$char;
  193: 	}
  194:         $char='';
  195:         $entitymode=0;
  196:     }
  197:     return $output;
  198: }
  199: 
  200: sub insidetrans {
  201:     my @args=@_;
  202:     return '<font'.$args[0].$args[1].'><m>$'.&latextrans($args[2]).'$</m>';
  203: }
  204: 
  205: sub symbolfontreplace {
  206:     my $text=shift;
  207:     my @fragments=split(/\<\/font\>/si,$text);
  208:     for (my $i=0; $i<=$#fragments;$i++) {
  209: 	$fragments[$i]=~s/\<font([^\>]*)\s+face=[\"\']*symbol[\"\']*([^\>]*)\>(.*)$/&insidetrans($1,$2,$3)/gsie;
  210:     }
  211:     return join('</font>',@fragments);
  212: }
  213: 
  214: sub htmlclean {
  215:     my ($raw,$full,$blocklinefeed,$blockemptytags,$blocklowercasing,$blockdesymboling)=@_;
  216: # Take care of CRLF etc
  217:     unless ($blocklinefeed) {
  218: 	$raw=~s/\r\f/\n/gs; $raw=~s/\f\r/\n/gs;
  219: 	$raw=~s/\r\n/\n/gs; $raw=~s/\n\r/\n/gs;
  220: 	$raw=~s/\f/\n/gs; $raw=~s/\r/\n/gs;
  221: 	$raw=~s/\&\#10\;/\n/gs; $raw=~s/\&\#13\;/\n/gs;
  222:     }
  223: # Generate empty tags, remove wrong end tags
  224:     unless ($blockemptytags) {
  225: 	$raw=~s/\<(br|hr|img|meta|embed|allow|basefont)([^\>]*?)\>/\<$1$2 \/\>/gis;
  226: 	$raw=~s/\<\/(br|hr|img|meta|embed|allow|basefont)\>//gis;
  227: 	$raw=~s/\/ \/\>/\/\>/gs;
  228: 	unless ($full) {
  229: 	    $raw=~s/\<[\/]*(body|head|html)\>//gis;
  230: 	}
  231:     }
  232: # Make standard tags lowercase
  233:     unless ($blocklowercasing) {
  234: 	foreach ('html','body','head','meta','h1','h2','h3','h4','b','i','m',
  235: 		 'table','tr','td','th','p','br','hr','img','embed','font',
  236: 		 'a','strong','center','title','basefont','li','ol','ul',
  237: 		 'input','select','form','option','script','pre') {
  238: 	    $raw=~s/\<$_\s*\>/\<$_\>/gis;
  239: 	    $raw=~s/\<\/$_\s*\>/<\/$_\>/gis;
  240: 	    $raw=~s/\<$_\s([^\>]*)\>/<$_ $1\>/gis;
  241: 	}
  242:     }
  243: # Replace <font face="symbol">
  244:     unless ($blockdesymboling) {
  245: 	$raw=&symbolfontreplace($raw);
  246:     }
  247:     return $raw;
  248: }
  249: 
  250: sub phaseone {
  251:     my ($r,$fn,$uname,$udom)=@_;
  252:     $r->print(
  253:         &Apache::lonhtmlcommon::start_pick_box()
  254:        .&Apache::lonhtmlcommon::row_title(&mt('Select actions to attempt'))
  255:        .'<label>'
  256:        .'<input type="checkbox" name="linefeed" checked="checked" /> '
  257:        .&mt('Linefeeds, formfeeds, and carriage returns')
  258:        .'</label><br />'
  259:        .'<label>'
  260:        .'<input type="checkbox" name="empty" checked="checked" /> '
  261:        .&mt('Empty tags')
  262:        .'</label><br />'
  263:        .'<label>'
  264:        .'<input type="checkbox" name="lower" checked="checked" /> '
  265:        .&mt('Lower casing')
  266:        .'</label><br />'
  267:        .'<label>'
  268:        .'<input type="checkbox" name="symbol" checked="checked" /> '
  269:        .&mt('Symbol font')
  270:        .'</label>'
  271:        .&Apache::lonhtmlcommon::row_closure(1)
  272:        .&Apache::lonhtmlcommon::end_pick_box()
  273:     );
  274: 
  275:     $r->print(
  276:         '<input type="hidden" name="phase" value="two" />'
  277:        .'<p>'
  278:        .'<input type="submit" value="'.&mt('Next').'" />'
  279:        .'</p>'
  280:     );
  281: }
  282: 
  283: sub phasetwo {
  284:     # Check original file
  285:     my ($r,$fn,$uname,$udom)=@_;
  286:     my $text='';
  287:     my $londocroot = $r->dir_config('lonDocRoot');
  288:     if (open(IN,"<$londocroot/priv/$udom/$uname".$fn)) {
  289:         while (my $line=<IN>) {
  290: 	    $text.=$line;
  291:         }
  292:         close(IN);
  293:     }
  294:     # Check if any selection was made
  295:     if ($env{'form.linefeed'} ne 'on' &&
  296:         $env{'form.empty'} ne 'on' &&
  297:         $env{'form.lower'} ne 'on' &&
  298:         $env{'form.symbol'} ne 'on') {
  299:         $r->print(
  300:             '<p class="LC_warning">'
  301:            .&mt('Please select at least one option.')
  302:            .'</p>'
  303:            .'<p><a href="javascript:history.back();">'.&mt('Back').'</p>'
  304:         );
  305:         return;
  306:     }
  307: 
  308:     my $uri="/priv/$udom/$uname".$fn;
  309:     my $result=&Apache::lonnet::ssi_body($uri,
  310: 					 ('grade_target'=>'web',
  311: 					  'return_only_error_and_warning_counts' => 1));
  312:     my ($errorcount,$warningcount)=split(':',$result);
  313: 
  314:     # Display results for original file
  315:     $r->print(
  316:         &Apache::lonhtmlcommon::start_pick_box()
  317:        .&Apache::lonhtmlcommon::row_title(&mt('Original file'))
  318:        .&Apache::lonhtmlcommon::confirm_success(
  319:            &mt('[quant,_1,error]',$errorcount), $errorcount)
  320:        .'<br />'
  321:        .&Apache::lonhtmlcommon::confirm_success(
  322:            &mt('[quant,_1,warning]',$warningcount), $warningcount)
  323:        .&Apache::lonhtmlcommon::row_closure()
  324:     );
  325: 
  326:     # Clean up file
  327:     $text=&htmlclean($text,1,
  328:                ($env{'form.linefeed'} ne 'on'),
  329:                ($env{'form.empty'} ne 'on'),
  330:                ($env{'form.lower'} ne 'on'),
  331:                ($env{'form.symbol'} ne 'on'));
  332:     my ($main,$ext)=($fn=~/^(.*)\.(\w+)/);
  333:     my $newfn=$main.'_Auto_Cleaned_Up.'.$ext;
  334:     if (open(OUT,">$londocroot/priv/$udom/$uname".$newfn)) {
  335:         print OUT $text;
  336:         close(OUT);
  337:     }
  338:     my $newuri="/priv/$udom/$uname".$newfn;
  339:     $result=&Apache::lonnet::ssi_body($newuri,
  340:                                          ('grade_target'=>'web',
  341:                                           'return_only_error_and_warning_counts' => 1));
  342:     ($errorcount,$warningcount)=split(':',$result);
  343: 
  344:     # Display results for cleaned up file
  345:     $r->print(
  346:         &Apache::lonhtmlcommon::row_title(&mt('Cleaned up file'))
  347:        .&Apache::lonhtmlcommon::confirm_success(
  348:            &mt('[quant,_1,error]',$errorcount), $errorcount)
  349:        .'<br />'
  350:        .&Apache::lonhtmlcommon::confirm_success(
  351:            &mt('[quant,_1,warning]',$warningcount), $warningcount)
  352:        .&Apache::lonhtmlcommon::row_closure()
  353:     );
  354: 
  355:     # Display actions
  356:     $r->print(
  357:         &Apache::lonhtmlcommon::row_title(&mt('Actions'))
  358:        .'<ul>'
  359:        .'<li><a href="'.$newuri.'" target="prev">'
  360:        .&mt('Open (and edit) cleaned up file in new window')
  361:        .'</a></li>'
  362:        .'<li><a href="'
  363:        .&HTML::Entities::encode(
  364:             '/adm/diff?filename='.&escape($uri)
  365:            .'&versionone=priv&filetwo='.&escape($newuri))
  366:        .'" target="prev">'
  367:        .&mt('Show diffs in new window')
  368:        .'</a></li>'
  369:        .'</ul>'
  370:        .&Apache::lonhtmlcommon::row_closure(1)
  371:        .&Apache::lonhtmlcommon::end_pick_box()
  372:        .'<p>'
  373:        .'<input type="hidden" name="phase" value="three" />'
  374:        .'<input type="submit" name="accept" value="'
  375:        .&mt('Clean Up').'" />'
  376:        .' <input type="submit" name="reject" value="'
  377:        .&mt('Cancel').'" />'
  378:        .'</p>'
  379:     );
  380: }
  381: 
  382: sub phasethree {
  383:     my ($r,$fn,$uname,$udom)=@_;
  384:     my $old=$r->dir_config('lonDocRoot')."/priv/$udom/$uname".$fn;
  385:     my ($main,$ext)=($fn=~/^(.*)\.(\w+)/);
  386:     my $newfn=$main.'_Auto_Cleaned_Up.'.$ext;
  387:     my $new=$r->dir_config('lonDocRoot')."/priv/$udom/$uname".$newfn;
  388:     if ($env{'form.accept'}) {
  389:         $r->print(
  390:         '<p class="LC_info">'
  391:        .&mt('Accepting changes...')
  392:        .'</p>'
  393:     );
  394:         move($new,$old);
  395:     } else {
  396:         $r->print(
  397:         '<p class="LC_info">'
  398:        .&mt('Rejecting changes...')
  399:        .'</p>'
  400:         );
  401:         unlink($new);
  402:     }
  403:     $r->print(
  404:         '<p>'
  405:        .&Apache::lonhtmlcommon::confirm_success(&mt('Done')));
  406:         '</p>'
  407: }
  408: 
  409: # ---------------------------------------------------------------- Main Handler
  410: sub handler {
  411: 
  412:     my $r=shift;
  413:     my $fn='';
  414: 
  415: # Get query string for limited number of parameters
  416: 
  417:     &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
  418: 					    ['filename']);
  419: 
  420:     if ($env{'form.filename'}) {
  421: 	$fn=$env{'form.filename'};
  422: 	$fn=~s{^https?\://[^/]+}{};
  423:     } else {
  424: 	$r->log_reason($env{'user.name'}.' at '.$env{'user.domain'}.
  425: 		       ' unspecified filename for cleanup', $r->filename); 
  426: 	return HTTP_NOT_FOUND;
  427:     }
  428: 
  429:     unless ($fn) { 
  430: 	$r->log_reason($env{'user.name'}.' at '.$env{'user.domain'}.
  431: 		       ' trying to cleanup non-existing file', $r->filename); 
  432: 	return HTTP_NOT_FOUND;
  433:     } 
  434: 
  435: # ----------------------------------------------------------- Start page output
  436:     my $uname;
  437:     my $udom;
  438: 
  439:     ($uname,$udom)=&Apache::lonnet::constructaccess($fn);
  440:     unless (($uname) && ($udom)) {
  441: 	$r->log_reason($uname.' at '.$udom.
  442: 		       ' trying to cleanup file '.$env{'form.filename'}.
  443: 		       ' ('.$fn.') - not authorized', 
  444: 		       $r->filename); 
  445: 	return HTTP_NOT_ACCEPTABLE;
  446:     }
  447: 
  448:     &Apache::loncommon::content_type($r,'text/html');
  449:     $r->send_http_header;
  450: 
  451:     # Breadcrumbs
  452:     my $text = 'Authoring Space';
  453:     my $href = &Apache::loncommon::authorspace($fn);
  454:     if ($env{'request.course.id'}) {
  455:         my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
  456:         my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
  457:         if ($href eq "/priv/$cdom/$cnum/") {
  458:             $text = 'Course Authoring Space';
  459:         }
  460:     }
  461:     my $brcrum = [{'href' => $href,
  462:                    'text' => $text},
  463:                   {'href' => '',
  464:                    'text' => 'Cleanup XML Document'}];
  465: 
  466:     $fn=~s{^/priv/$LONCAPA::domain_re/$LONCAPA::username_re}{};
  467: 
  468:     $r->print(&Apache::loncommon::start_page('Cleanup XML Document',
  469:                                              undef,
  470:                                              {'bread_crumbs' => $brcrum,}));
  471:     $r->print('<h2>'.$fn.'</h2>'.
  472:               '<form action="/adm/cleanup" method="post">'.
  473:               '<input type="hidden" name="filename" value="'.$env{'form.filename'}.'" />');
  474:     unless ($fn=~/\.(problem|exam|quiz|assess|survey|form|library|xml|html|htm|xhtml|xhtm|sty)$/) {
  475: 	$r->print('<p class="LC_warning">'.&mt('Cannot cleanup this filetype').'</p>');
  476:     } else {
  477: 	if ($env{'form.phase'} eq 'three') {
  478: 	    &phasethree($r,$fn,$uname,$udom);
  479: 	} elsif ($env{'form.phase'} eq 'two') {
  480: 	    &phasetwo($r,$fn,$uname,$udom);
  481: 	} else {
  482: 	    &phaseone($r,$fn,$uname,$udom);
  483: 	}
  484:     }
  485:     my $dir=$fn;
  486:     $dir=~s{[^/]+$}{};
  487:     $r->print(
  488:         '</form>'
  489:        .&Apache::lonhtmlcommon::actionbox(
  490:             ['<a href="/priv/'.$udom.'/'.$uname.$fn.'">'.
  491:                  &mt('Back to Source File').'</a>',
  492:             '<a href="/priv/'.$udom.'/'.$uname.$dir.'">'.
  493:                 &mt('Back to Source Directory').'</a>'])
  494:        .&Apache::loncommon::end_page()
  495:     );
  496: 
  497:     return OK;  
  498: }
  499: 
  500: 1;
  501: __END__

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