File:  [LON-CAPA] / loncom / homework / edit.pm
Revision 1.38: download - view: text, annotated - select for diffs
Sun Nov 3 19:16:19 2002 UTC (21 years, 6 months ago) by www
Branches: MAIN
CVS tags: HEAD
In "colorful" problem editor, being able to insert tags into the middle,
rather than only on top.

Insert fields are produced by the end_table routine in Apache::edit for
target "edit".

They are labelled "insert_after_(current tag)_(current depth)". The
"(current tag)" was necessary because nested ending tags had the same value
of Apache::lonxml::curdepth.

New routine handle_insertafter is called in "modified" target.

Needed a hack for <endouttext /> in structuretags.pm for target "modified".

Appears to work just fine in several test situations, but needs more testing.

    1: # The LearningOnline Network with CAPA 
    2: # edit mode helpers
    3: #
    4: # $Id: edit.pm,v 1.38 2002/11/03 19:16:19 www 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: # 3/20 Guy
   29: # 01/10/02 Matthew
   30: # 03/06/02 Matthew
   31: package Apache::edit; 
   32: 
   33: use strict;
   34: use Apache::lonnet();
   35: use HTML::Entities();
   36: 
   37: # Global Vars
   38: # default list of colors to use in editing
   39: @Apache::edit::colorlist=('#ffffff','#ff0000','#00ff00','#0000ff','#0ff000','#000ff0','#f0000f');
   40: # depth of nesting of edit
   41: $Apache::edit::colordepth=0;
   42: @Apache::edit::inserttag=();
   43: 
   44: sub initialize_edit {
   45:   $Apache::edit::colordepth=0;
   46:   @Apache::edit::inserttag=();
   47: }
   48: 
   49: sub tag_start {
   50:   my ($target,$token,$description) = @_;
   51:   my $result='';
   52:   if ($target eq "edit") {
   53:     my $tag=$token->[1];
   54:     if (!$description) {
   55:       $description=&Apache::lonxml::description($token);
   56:       if (!$description) { $description="&lt;$tag&gt;"; }
   57:     }
   58:     $result.= &start_table($token)."<tr><td>$description</td>
   59: <td>Delete".
   60:   &deletelist($target,$token)
   61:   ."</td>
   62: <td>".
   63:     &insertlist($target,$token).&end_row().&start_spanning_row();
   64: #<td>". 
   65: #  &movebuttons($target,$token).
   66: #    "</tr><tr><td colspan=\"3\">\n";
   67:   }
   68:   return $result;
   69: }
   70: 
   71: sub tag_end {
   72:   my ($target,$token,$description) = @_;
   73:   my $result='';
   74:   if ($target eq 'edit') {
   75:     my $tag=$token->[1];
   76:     if (!defined($description)) {
   77:       $result.="</td></tr><tr><td>&lt;/$tag&gt;</td><td colspan=\"2\">&nbsp;</td>";
   78:     } else {
   79:       if ($description ne '') { $result.="</td></tr><tr><td>$description</td><td colspan=\"2\">&nbsp;</td>"; }
   80:     }
   81:     $result.="</tr>".&end_table()."\n";
   82:   }
   83:   return $result;
   84: }
   85: 
   86: sub start_table {
   87:   my ($token)=@_;
   88:   my $tag = $token->[1];
   89:   my $tagnum;
   90:   foreach my $namespace (reverse @Apache::lonxml::namespace) {
   91:     my $testtag=$namespace.'::'.$tag;
   92:     $tagnum=$Apache::lonxml::insertlist{"$testtag.num"};
   93:     if (defined($tagnum)) { last; }
   94:   }
   95:   if (!defined ($tagnum)) { $tagnum=$Apache::lonxml::insertlist{"$tag.num"}; }
   96:   my $color = $Apache::lonxml::insertlist{"$tagnum.color"};
   97:   if (!defined($color)) {
   98:     $color = $Apache::edit::colorlist[$Apache::edit::colordepth];
   99:   }
  100:   $Apache::edit::colordepth++;
  101:   push(@Apache::edit::inserttag,$token->[1]);
  102:   my $result="<p align=\"right\"><table bgcolor=\"$color\" width=\"95%\" border=\"2\">";
  103:   return $result;
  104: }
  105: 
  106: sub end_table {
  107:   $Apache::edit::colordepth--;
  108:   my $result="</table></p>";
  109:   $result.=
  110: 	"<p><table width=\"100%\"><tr><td>".
  111:         &innerinsertlist('edit',
  112:  (defined($Apache::edit::inserttag[-2])?$Apache::edit::inserttag[-2]:'problem')
  113: ,
  114:  (defined($Apache::edit::inserttag[-1])?$Apache::edit::inserttag[-1]:'')).
  115:         "</td></tr></table></p>";
  116:   pop(@Apache::edit::inserttag);
  117:   return $result;
  118: }
  119: 
  120: sub start_spanning_row { return '<tr><td colspan="3">';}
  121: sub start_row          { return '<tr><td>';            }
  122: sub end_row            { return '</td></tr>';          }
  123: 
  124: sub movebuttons {
  125:   my ($target,$token) = @_;
  126:   my $result='<input type="submit" name="moveup.'.
  127:     $Apache::lonxml::curdepth.'" value="Move Up" />';
  128:   $result.='<input type="submit" name="movedown.'.
  129:     $Apache::lonxml::curdepth.'" value="Move Down" />';
  130:   return $result;
  131: }
  132: 
  133: sub deletelist {
  134:   my ($target,$token) = @_;
  135:   my $result = "<select name=\"delete_$Apache::lonxml::curdepth\">
  136: <option></option>
  137: <option>Yes</option>
  138: </select>";
  139:   return $result;
  140: }
  141: 
  142: sub handle_delete {
  143:   if (!$ENV{"form.delete_$Apache::lonxml::curdepth"}) { return ''; }
  144:   my ($space,$target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  145:   my $result=0;
  146:   if ($space) {
  147:     my $sub1="$space\:\:delete_$token->[1]";
  148:     {
  149:       no strict 'refs';
  150:       if (defined &$sub1) {
  151: 	$result=&$sub1($target,$token,$tagstack,$parstack,$parser,$safeeval,$style);
  152:       }
  153:     }
  154:   }
  155:   if (!$result) {
  156:     my $endtag='/'.$token->[1];
  157:     my $bodytext=&Apache::lonxml::get_all_text($endtag,$$parser[$#$parser]);
  158:     $$parser['-1']->get_token();
  159:     &Apache::lonxml::debug("Deleting :$bodytext: for $token->[1]");
  160:     &Apache::lonxml::end_tag($tagstack,$parstack,$token);
  161:   }
  162:   return 1;
  163: }
  164: 
  165: sub get_insert_list {
  166:   my ($tagname) = @_;
  167:   my $result='';
  168:   my @tagnums= ();
  169:   #&Apache::lonxml::debug("keys ".join("\n",sort(keys(%Apache::lonxml::insertlist))));
  170:   if ($Apache::lonxml::insertlist{"$tagname.which"}) {
  171:     push (@tagnums, @{ $Apache::lonxml::insertlist{"$tagname.which"} });
  172:   }
  173:   foreach my $namespace (@Apache::lonxml::namespace) {
  174:     if ($Apache::lonxml::insertlist{"$namespace".'::'."$tagname.which"}) {
  175:       push (@tagnums, @{ $Apache::lonxml::insertlist{"$namespace".'::'."$tagname.which"} });
  176:     }
  177:   }
  178:   if (@tagnums) {
  179:     my %options;
  180:     foreach my $tagnum (@tagnums) {
  181:       my $descrip=$Apache::lonxml::insertlist{"$tagnum.description"};
  182:       $options{$descrip} ="<option value=\"$tagnum\">".$descrip."</option>\n";
  183:     }
  184:     foreach my $option (sort(keys(%options))) { $result.=$options{$option}; }
  185:     if ($result) { $result='<option selected="on"></option>'.$result; }
  186:   }
  187:   return $result;
  188: }
  189: 
  190: sub insertlist {
  191:   my ($target,$token) = @_;
  192:   return &innerinsertlist($target,$token->[1]);
  193: }
  194: 
  195: sub innerinsertlist {
  196:   my ($target,$tagname,$closingtag) = @_;
  197:   my $result;
  198:   my $after='';
  199:   if ($closingtag) {
  200:      $after='_after_'.$closingtag; 
  201:   }
  202:   if ($target eq 'edit') {
  203:     my $optionlist= &get_insert_list($tagname);
  204:     if ($optionlist) {
  205:       $result = "Insert:
  206: <select name=\"insert$after\_$Apache::lonxml::curdepth\">
  207: $optionlist
  208: </select>"
  209:     } else {
  210:       $result="&nbsp;";
  211:     }
  212:   }
  213:   return $result;
  214: }
  215: 
  216: sub handle_insert {
  217:   if ($ENV{"form.insert_$Apache::lonxml::curdepth"} eq '') { return ''; }
  218:   my $result;
  219:   my $tagnum = $ENV{"form.insert_$Apache::lonxml::curdepth"};
  220:   my $func=$Apache::lonxml::insertlist{"$tagnum.function"};
  221:   if ($func eq 'default') {
  222:     my $newtag=$Apache::lonxml::insertlist{"$tagnum.tag"};
  223:     my $namespace;
  224:     if ($newtag =~ /::/) { ($namespace,$newtag) = split(/::/,$newtag); }
  225:     $result.="\n<$newtag>\n</$newtag>";
  226:   } else {
  227:     if (defined(&$func)) {
  228:       {
  229: 	no strict 'refs';
  230: 	$result.=&$func();
  231:       }
  232:     } else {
  233:       my $newtag=$Apache::lonxml::insertlist{"$tagnum.tag"};
  234:       &Apache::lonxml::error("Unable to insert tag $newtag, $func was not defined.");
  235:     }
  236:   }
  237:   return $result;
  238: }
  239: 
  240: sub handle_insertafter {
  241:   my $tagname=shift;
  242:   if ($ENV{"form.insert_after_$tagname\_$Apache::lonxml::curdepth"} eq '')
  243:      { return ''; }
  244:   my $result;
  245:   my $tagnum =$ENV{"form.insert_after_$tagname\_$Apache::lonxml::curdepth"};
  246:   my $func=$Apache::lonxml::insertlist{"$tagnum.function"};
  247:   if ($func eq 'default') {
  248:     my $newtag=$Apache::lonxml::insertlist{"$tagnum.tag"};
  249:     my $namespace;
  250:     if ($newtag =~ /::/) { ($namespace,$newtag) = split(/::/,$newtag); }
  251:     $result.="\n<$newtag>\n</$newtag>";
  252:   } else {
  253:     if (defined(&$func)) {
  254:       {
  255: 	no strict 'refs';
  256: 	$result.=&$func();
  257:       }
  258:     } else {
  259:       my $newtag=$Apache::lonxml::insertlist{"$tagnum.tag"};
  260:       &Apache::lonxml::error("Unable to insert (after) tag $newtag, $func was not defined. ($tagname $tagnum)");
  261:     }
  262:   }
  263:   return $result;
  264: }
  265: 
  266: sub insert_responseparam {
  267:   return '
  268:     <responseparam />';
  269: }
  270: 
  271: sub insert_formularesponse {
  272:   return '
  273: <formularesponse answer="" samples="">
  274:     <textline />
  275:     <hintgroup>
  276:     </hintgroup>
  277: </formularesponse>';
  278: }
  279: 
  280: sub insert_numericalresponse {
  281:   return '
  282: <numericalresponse answer="">
  283:     <textline />
  284:     <hintgroup>
  285:     </hintgroup>
  286: </numericalresponse>';
  287: }
  288: 
  289: sub insert_stringresponse {
  290:   return '
  291: <stringresponse answer="" type="">
  292:     <textline />
  293:     <hintgroup>
  294:     </hintgroup>
  295: </stringresponse>';
  296: }
  297: 
  298: sub insert_essayresponse {
  299:   return '
  300: <essayresponse>
  301:     <textfield></textfield>
  302: </essayresponse>';
  303: }
  304: 
  305: sub insert_optionresponse {
  306:   return '
  307: <optionresponse max="10">
  308:     <foilgroup options="">
  309:     </foilgroup>
  310:     <hintgroup>
  311:     </hintgroup>
  312: </optionresponse>';
  313: }
  314: 
  315: sub insert_radiobuttonresponse {
  316:   return '
  317: <radiobuttonresponse max="10">
  318:     <foilgroup>
  319:     </foilgroup>
  320:     <hintgroup>
  321:     </hintgroup>
  322: </radiobuttonresponse>';
  323: }
  324: 
  325: sub insert_displayduedate { return '<displayduedate />'; }
  326: sub insert_displaytitle   { return '<displaytitle />'; }
  327: sub insert_hintpart {
  328:   return '
  329: <hintpart on="default">
  330:     <startouttext/>
  331:     <endouttext />
  332: </hintpart>';
  333: }
  334: 
  335: sub insert_numericalhint {
  336:   return '
  337: <numericalhint>
  338: </numericalhint>';
  339: }
  340: 
  341: sub insert_radiobuttonhint {
  342:   return '
  343: <radiobuttonhint>
  344: </radiobuttonhint>';
  345: }
  346: 
  347: sub insert_startouttext {
  348:   return "<startouttext />\n<endouttext />";
  349: }
  350: 
  351: sub insert_script {
  352:   return "\n<script type=\"loncapa/perl\">\n</script>";
  353: }
  354: 
  355: sub textarea_sizes {
  356:   my ($data)=@_;
  357:   my $count=0;
  358:   my $maxlength=-1;
  359:   foreach (split ("\n", $$data)) { $count++;
  360: 	if (length($_) > $maxlength) { $maxlength = length($_); }
  361:       }
  362:   my $rows = $count;
  363:   my $cols = $maxlength;
  364:   return ($rows,$cols);
  365: }
  366: 
  367: sub editline {
  368:     my ($tag,$data,$description,$size)=@_;
  369:     $data=&HTML::Entities::encode($data);
  370:     if ($description) { $description="<br />".$description."<br />"; }
  371:     my $result = <<"END";
  372: $description
  373: <input type="text" name="homework_edit_$Apache::lonxml::curdepth" 
  374:        value="$data" size="$size" />
  375: END
  376:     return $result;
  377: }
  378: 
  379: sub editfield {
  380:   my ($tag,$data,$description,$minwidth,$minheight)=@_;
  381: 
  382:   my ($rows,$cols)=&textarea_sizes(\$data);
  383:   if ($cols > 80) { $cols = 80; }
  384:   if ($cols < $minwidth ) { $cols = $minwidth; }
  385:   if ($rows < $minheight) { $rows = $minheight; }
  386:   if ($description) { $description="<br />".$description."<br />"; }
  387:   return $description."\n".'&nbsp;&nbsp;&nbsp;<textarea rows="'.$rows.
  388:     '" cols="'.$cols.'" name="homework_edit_'.$Apache::lonxml::curdepth.'">'.
  389:       &HTML::Entities::encode($data).'</textarea>'."\n";
  390: }
  391: 
  392: sub modifiedfield {
  393:   my ($token) = @_;
  394:   my $result;
  395: #  foreach my $envkey (sort keys %ENV) {
  396: #    &Apache::lonxml::debug("$envkey ---- $ENV{$envkey}");
  397: #  }
  398: #  &Apache::lonxml::debug("I want homework_edit_$Apache::lonxml::curdepth");
  399: #  &Apache::lonxml::debug($ENV{"form.homework_edit_$Apache::lonxml::curdepth"});
  400:   $result=$ENV{"form.homework_edit_$Apache::lonxml::curdepth"};
  401:   return $result;
  402: }
  403: 
  404: # Returns a 1 if the token has been modified and you should rebuild the tag
  405: # side-effects, will modify the $token if new values are found
  406: sub get_new_args {
  407:   my ($token,$parstack,$safeeval,@args)=@_;
  408:   my $rebuild=0;
  409:   foreach my $arg (@args) {
  410:     #just want the string that it was set to
  411:     my $value=$token->[2]->{$arg};
  412:     my $newvalue=$ENV{"form.$Apache::lonxml::curdepth.$arg"};
  413:     &Apache::lonxml::debug(" for:$arg: cur is :$value: new is :$newvalue:");
  414:     if ($value ne $newvalue) {
  415:       $token->[2]->{$arg}=$newvalue;
  416:       $rebuild=1;
  417:     }
  418:   }
  419:   return $rebuild;
  420: }
  421: 
  422: # looks for /> on start tags
  423: sub rebuild_tag {
  424:   my ($token) = @_;
  425:   my $result;
  426:   if ($token->[0] eq 'S') {
  427:     $result = '<'.$token->[1];
  428:     while (my ($key,$val)= each(%{$token->[2]})) {
  429:       $val=~s:^\s+|\s+$::g;
  430:       $val=~s:"::g; #"
  431:       &Apache::lonxml::debug("setting :$key: to  :$val:");
  432:       $result.=' '.$key.'="'.$val.'"';
  433:     }
  434:     if ($token->[4] =~ m:/>$:) {
  435:       $result.=' />';
  436:     } else {
  437:       $result.='>';
  438:     }
  439:   } elsif ( $token->[0] eq 'E' ) {
  440:     $result = '</'.$token->[1].'>';
  441:   }
  442:   return $result;
  443: }
  444: 
  445: sub text_arg {
  446:   my ($description,$name,$token,$size) = @_;
  447:   my $result;
  448:   if (!defined $size) { $size=20; }
  449:   my $arg=$token->[2]{$name};
  450:   $result=$description.'<input name="'."$Apache::lonxml::curdepth.$name".
  451:     '" type="text" value="'.$arg.'" size="'.$size.'" />';
  452:   return $result;
  453: }
  454: 
  455: sub select_arg {
  456:   my ($description,$name,$list,$token) = @_;
  457:   my $result;
  458:   my $optionlist="";
  459:   my $selected=$token->[2]{$name};
  460:   foreach my $option (@$list) {
  461:     if ( $selected eq $option ) {
  462:       $optionlist.="<option selected=\"on\">$option</option>\n";
  463:     } else {
  464:       $optionlist.="<option>$option</option>\n";
  465:     }
  466:   }
  467:   $result.=$description.'<select name="'."$Apache::lonxml::curdepth.$name".'">
  468:        '.$optionlist.'
  469:       </select>';
  470:   return $result;
  471: }
  472: 
  473: sub select_or_text_arg {
  474:   my ($description,$name,$list,$token,$size) = @_;
  475:   my $result;
  476:   my $optionlist="";
  477:   my $found=0;
  478:   my $selected=$token->[2]{$name};
  479:   foreach my $option (@$list) {
  480:     if ( $selected eq $option ) {
  481:       $optionlist.="<option selected=\"on\">$option</option>\n";
  482:       $found=1;
  483:     } else {
  484:       $optionlist.="<option>$option</option>\n";
  485:     }
  486:   }
  487:   $optionlist.="<option value=\"TYPEDINVALUE\">Type in value</option>\n";
  488:   if (($found) || (!$selected)) {
  489:     $result.=$description.'<select name="'."$Apache::lonxml::curdepth.$name".'">
  490:        '.$optionlist.'
  491:       </select>';
  492:   } else {
  493:     $result.=&text_arg($description,$name,$token,$size);
  494:   }
  495:   return $result;
  496: }
  497: 
  498: #----------------------------------------------------- browse
  499: sub browse {
  500:     # insert a link to call up the filesystem browser (lonindexer)
  501:     $_ = shift;
  502:     my $form    = 'lonhomework';
  503:     my $element = &Apache::lonnet::escape("$Apache::lonxml::curdepth.$_");
  504:     my $result = <<"ENDBUTTON";
  505: <a href=\"javascript:openbrowser('$form','$element')\"\>Browse</a>
  506: ENDBUTTON
  507:     return $result;
  508: }
  509: 
  510: #----------------------------------------------------- browse
  511: sub search {
  512:     # insert a link to call up the filesystem browser (lonindexer)
  513:     $_ = shift;
  514:     my $form    = 'lonhomework';
  515:     my $element = &Apache::lonnet::escape("$Apache::lonxml::curdepth.$_");
  516:     my $result = <<"ENDBUTTON";
  517: <a href=\"javascript:opensearcher('$form','$element')\"\>Search</a>
  518: ENDBUTTON
  519:     return $result;
  520: }
  521: 
  522: 
  523: 1;
  524: __END__
  525: 
  526: =head1 NAME
  527: 
  528: Apache::edit - edit mode helpers
  529: 
  530: =head1 SYNOPSIS
  531: 
  532: Invoked by many homework and xml related modules.
  533: 
  534:  &Apache::edit::SUBROUTINENAME(ARGUMENTS);
  535: 
  536: =head1 INTRODUCTION
  537: 
  538: This module outputs HTML syntax helpful for the rendering of edit
  539: mode interfaces.
  540: 
  541: This is part of the LearningOnline Network with CAPA project
  542: described at http://www.lon-capa.org.
  543: 
  544: =head1 HANDLER SUBROUTINE
  545: 
  546: There is no handler subroutine.
  547: 
  548: =head1 OTHER SUBROUTINES
  549: 
  550: =over 4
  551: 
  552: =item *
  553: 
  554: initialize_edit() : initialize edit (set colordepth to zero)
  555: 
  556: =item *
  557: 
  558: tag_start($target,$token,$description) : provide deletion and insertion lists
  559: for the manipulation of a start tag; return a scalar string
  560: 
  561: =item *
  562: 
  563: tag_end($target,$token,$description) : ending syntax corresponding to
  564: &tag_start. return a scalar string.
  565: 
  566: =item *
  567: 
  568: start_table($token) : start table; update colordepth; return scalar string.
  569: 
  570: =item *
  571: 
  572: end_table() : reduce color depth; end table; return scalar string
  573: 
  574: =item *
  575: 
  576: start_spanning_row() : start a new table row spanning the 'edit' environment.
  577: 
  578: =item *
  579: 
  580: start_row() : start a new table row and element. 
  581: 
  582: =item *
  583: 
  584: end_row() : end current table element and row.
  585: 
  586: =item *
  587: 
  588: movebuttons($target,$token) : move-up and move-down buttons; return scalar
  589: string
  590: 
  591: =item *
  592: 
  593: deletelist($target,$token) : provide a yes option in an HTML select element;
  594: return scalar string
  595: 
  596: =item *
  597: 
  598: handle_delete($space,$target,$token,$tagstack,$parstack,$parser,$safeeval,
  599: $style) : respond to a user delete request by passing relevant stack
  600: and array information to various rendering functions; return a scalar string
  601: 
  602: =item *
  603: 
  604: get_insert_list($token) : provide an insertion list based on possibilities
  605: from lonxml; return a scalar string
  606: 
  607: =item *
  608: 
  609: insertlist($target,$token) : api that uses get_insert_list;
  610: return a scalar string
  611: 
  612: =item *
  613: 
  614: handleinsert($token) : provide an insertion list based on possibilities
  615: from lonxml; return a scalar string
  616: 
  617: =item *
  618: 
  619: get_insert_list($token) : provide an insertion list based on possibilities
  620: from lonxml; return a scalar string
  621: 
  622: =item *
  623: browse($elementname) : provide a link which will open up the filesystem
  624: browser (lonindexer) and, once a file is selected, place the result in
  625: the form element $elementname.
  626: 
  627: =item *
  628: search($elementname) : provide a link which will open up the filesystem
  629: searcher (lonsearchcat) and, once a file is selected, place the result in
  630: the form element $elementname.
  631: 
  632: =item *
  633: editline(tag,data,description,size): Provide a <input type="text" ../> for
  634: single-line text entry.  This is to be used for text enclosed by tags, not
  635: arguements/parameters associated with a tag.
  636: 
  637: =back
  638: 
  639: incomplete...
  640: 
  641: =cut

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