Annotation of loncom/interface/loncommon.pm, revision 1.41
1.10 albertel 1: # The LearningOnline Network with CAPA
1.1 albertel 2: # a pile of common routines
1.10 albertel 3: #
1.41 ! ng 4: # $Id: loncommon.pm,v 1.39 2002/06/24 20:17:55 matthew Exp $
1.10 albertel 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: #
1.12 harris41 28: # YEAR=2001
29: # 2/13-12/7 Guy Albertelli
1.17 harris41 30: # 12/11,12/12,12/17 Scott Harrison
1.18 www 31: # 12/21 Gerd Kortemeyer
1.20 www 32: # 12/21 Scott Harrison
1.22 www 33: # 12/25,12/28 Gerd Kortemeyer
1.23 www 34: # YEAR=2002
35: # 1/4 Gerd Kortemeyer
1.1 albertel 36:
37: # Makes a table out of the previous attempts
1.2 albertel 38: # Inputs result_from_symbread, user, domain, course_id
1.16 harris41 39: # Reads in non-network-related .tab files
1.1 albertel 40:
1.35 matthew 41: # POD header:
42:
43: =head1 NAME
44:
45: Apache::loncommon - pile of common routines
46:
47: =head1 SYNOPSIS
48:
49: Referenced by other mod_perl Apache modules.
50:
51: Invocation:
52: &Apache::loncommon::SUBROUTINENAME(ARGUMENTS);
53:
54: =head1 INTRODUCTION
55:
56: Common collection of used subroutines. This collection helps remove
57: redundancy from other modules and increase efficiency of memory usage.
58:
59: Current things done:
60:
61: Makes a table out of the previous homework attempts
62: Inputs result_from_symbread, user, domain, course_id
63: Reads in non-network-related .tab files
64:
65: This is part of the LearningOnline Network with CAPA project
66: described at http://www.lon-capa.org.
67:
1.41 ! ng 68: =head2 General Subroutines
1.35 matthew 69:
70: =over 4
71:
72: =cut
73:
74: # End of POD header
1.1 albertel 75: package Apache::loncommon;
76:
77: use strict;
1.22 www 78: use Apache::lonnet();
1.8 albertel 79: use POSIX qw(strftime);
1.1 albertel 80: use Apache::Constants qw(:common);
81: use Apache::lonmsg();
1.22 www 82: my $readit;
83:
1.20 www 84: # ----------------------------------------------- Filetypes/Languages/Copyright
1.12 harris41 85: my %language;
86: my %cprtag;
87: my %fe; my %fd;
1.41 ! ng 88: my %category_extensions;
1.12 harris41 89:
1.20 www 90: # -------------------------------------------------------------- Thesaurus data
1.21 www 91: my @therelated;
92: my @theword;
93: my @thecount;
94: my %theindex;
95: my $thetotalcount;
1.20 www 96: my $thefuzzy=2;
97: my $thethreshold=0.1/$thefuzzy;
98: my $theavecount;
99:
1.12 harris41 100: # ----------------------------------------------------------------------- BEGIN
1.41 ! ng 101:
! 102: =pod
! 103:
1.35 matthew 104: =item BEGIN()
105:
106: Initialize values from language.tab, copyright.tab, filetypes.tab,
107: and filecategories.tab.
108:
109: =cut
110: # ----------------------------------------------------------------------- BEGIN
111:
1.18 www 112: BEGIN {
1.22 www 113:
114: unless ($readit) {
1.12 harris41 115: # ------------------------------------------------------------------- languages
116: {
117: my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.
118: '/language.tab');
1.16 harris41 119: if ($fh) {
120: while (<$fh>) {
121: next if /^\#/;
122: chomp;
123: my ($key,$val)=(split(/\s+/,$_,2));
124: $language{$key}=$val;
125: }
1.12 harris41 126: }
127: }
128: # ------------------------------------------------------------------ copyrights
129: {
1.16 harris41 130: my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonIncludes'}.
131: '/copyright.tab');
132: if ($fh) {
133: while (<$fh>) {
134: next if /^\#/;
135: chomp;
136: my ($key,$val)=(split(/\s+/,$_,2));
137: $cprtag{$key}=$val;
138: }
1.12 harris41 139: }
140: }
1.15 harris41 141: # ------------------------------------------------------------- file categories
142: {
143: my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.
1.16 harris41 144: '/filecategories.tab');
145: if ($fh) {
146: while (<$fh>) {
147: next if /^\#/;
148: chomp;
1.41 ! ng 149: my ($extension,$category)=(split(/\s+/,$_,2));
! 150: push @{$category_extensions{lc($category)}},$extension;
1.16 harris41 151: }
1.15 harris41 152: }
153: }
1.12 harris41 154: # ------------------------------------------------------------------ file types
155: {
1.16 harris41 156: my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.
157: '/filetypes.tab');
158: if ($fh) {
159: while (<$fh>) {
160: next if (/^\#/);
161: chomp;
162: my ($ending,$emb,$descr)=split(/\s+/,$_,3);
163: if ($descr ne '') {
164: $fe{$ending}=lc($emb);
165: $fd{$ending}=$descr;
166: }
1.12 harris41 167: }
168: }
169: }
1.20 www 170: # -------------------------------------------------------------- Thesaurus data
171: {
172: my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.
173: '/thesaurus.dat');
174: if ($fh) {
175: while (<$fh>) {
176: my ($tword,$tindex,$tcount,$trelated)=split(/\@/,$_);
177: $theindex{$tword}=$tindex;
178: $theword[$tindex]=$tword;
179: $thecount[$tindex]=$tcount;
180: $thetotalcount+=$tcount;
181: $therelated[$tindex]=$trelated;
182: }
183: }
184: $theavecount=$thetotalcount/$#thecount;
185: }
1.22 www 186: &Apache::lonnet::logthis(
187: "<font color=yellow>INFO: Read file types and thesaurus</font>");
188: $readit=1;
189: }
1.32 matthew 190:
191: }
192: # ============================================================= END BEGIN BLOCK
1.36 matthew 193:
194: =item linked_select_forms(...)
195:
196: linked_select_forms returns a string containing a <script></script> block
197: and html for two <select> menus. The select menus will be linked in that
198: changing the value of the first menu will result in new values being placed
199: in the second menu. The values in the select menu will appear in alphabetical
200: order.
201:
202: linked_select_forms takes the following ordered inputs:
203:
204: =over 4
205:
206: =item $formname, the name of the <form> tag
207:
208: =item $middletext, the text which appears between the <select> tags
209:
210: =item $firstdefault, the default value for the first menu
211:
212: =item $firstselectname, the name of the first <select> tag
213:
214: =item $secondselectname, the name of the second <select> tag
215:
216: =item $hashref, a reference to a hash containing the data for the menus.
217:
1.41 ! ng 218: =back
! 219:
1.36 matthew 220: Below is an example of such a hash. Only the 'text', 'default', and
221: 'select2' keys must appear as stated. keys(%menu) are the possible
222: values for the first select menu. The text that coincides with the
1.41 ! ng 223: first menu value is given in $menu{$choice1}->{'text'}. The values
1.36 matthew 224: and text for the second menu are given in the hash pointed to by
225: $menu{$choice1}->{'select2'}.
226:
1.41 ! ng 227: my %menu = ( A1 => { text =>"Choice A1" ,
1.36 matthew 228: default => "B3",
229: select2 => {
230: B1 => "Choice B1",
231: B2 => "Choice B2",
232: B3 => "Choice B3",
233: B4 => "Choice B4"
234: }
235: },
236: A2 => { text =>"Choice A2" ,
237: default => "C2",
238: select2 => {
239: C1 => "Choice C1",
240: C2 => "Choice C2",
241: C3 => "Choice C3"
242: }
243: },
244: A3 => { text =>"Choice A3" ,
245: default => "D6",
246: select2 => {
247: D1 => "Choice D1",
248: D2 => "Choice D2",
249: D3 => "Choice D3",
250: D4 => "Choice D4",
251: D5 => "Choice D5",
252: D6 => "Choice D6",
253: D7 => "Choice D7"
254: }
255: }
256: );
257:
258: =back
259:
260: =cut
261:
262: # ------------------------------------------------
263:
264: sub linked_select_forms {
265: my ($formname,
266: $middletext,
267: $firstdefault,
268: $firstselectname,
269: $secondselectname,
270: $hashref
271: ) = @_;
272: my $second = "document.$formname.$secondselectname";
273: my $first = "document.$formname.$firstselectname";
274: # output the javascript to do the changing
275: my $result = '';
276: $result.="<script>\n";
277: $result.="var select2data = new Object();\n";
278: $" = '","';
279: my $debug = '';
280: foreach my $s1 (sort(keys(%$hashref))) {
281: $result.="select2data.d_$s1 = new Object();\n";
282: $result.="select2data.d_$s1.def = new String('".
283: $hashref->{$s1}->{'default'}."');\n";
284: $result.="select2data.d_$s1.values = new Array(";
285: my @s2values = sort(keys( %{ $hashref->{$s1}->{'select2'} } ));
286: $result.="\"@s2values\");\n";
287: $result.="select2data.d_$s1.texts = new Array(";
288: my @s2texts;
289: foreach my $value (@s2values) {
290: push @s2texts, $hashref->{$s1}->{'select2'}->{$value};
291: }
292: $result.="\"@s2texts\");\n";
293: }
294: $"=' ';
295: $result.= <<"END";
296:
297: function select1_changed() {
298: // Determine new choice
299: var newvalue = "d_" + $first.value;
300: // update select2
301: var values = select2data[newvalue].values;
302: var texts = select2data[newvalue].texts;
303: var select2def = select2data[newvalue].def;
304: var i;
305: // out with the old
306: for (i = 0; i < $second.options.length; i++) {
307: $second.options[i] = null;
308: }
309: // in with the nuclear
310: for (i=0;i<values.length; i++) {
311: $second.options[i] = new Option(values[i]);
312: $second.options[i].text = texts[i];
313: if (values[i] == select2def) {
314: $second.options[i].selected = true;
315: }
316: }
317: }
318: </script>
319: END
320: # output the initial values for the selection lists
321: $result .= "<select size=\"1\" name=\"$firstselectname\" onchange=\"select1_changed()\">\n";
322: foreach my $value (sort(keys(%$hashref))) {
323: $result.=" <option value=\"$value\" ";
324: $result.=" selected=\"true\" " if ($value eq $firstdefault);
325: $result.=">$hashref->{$value}->{'text'}</option>\n";
326: }
327: $result .= "</select>\n";
328: my %select2 = %{$hashref->{$firstdefault}->{'select2'}};
329: $result .= $middletext;
330: $result .= "<select size=\"1\" name=\"$secondselectname\">\n";
331: my $seconddefault = $hashref->{$firstdefault}->{'default'};
332: foreach my $value (sort(keys(%select2))) {
333: $result.=" <option value=\"$value\" ";
334: $result.=" selected=\"true\" " if ($value eq $seconddefault);
335: $result.=">$select2{$value}</option>\n";
336: }
337: $result .= "</select>\n";
338: # return $debug;
339: return $result;
340: } # end of sub linked_select_forms {
341:
1.33 matthew 342: ###############################################################
1.37 matthew 343:
344: =item csv_translate($text)
345:
346: Translate $text to allow it to be output as a 'comma seperated values'
347: format.
348:
349: =cut
350:
351: sub csv_translate {
352: my $text = shift;
353: $text =~ s/\"/\"\"/g;
354: $text =~ s/\n//g;
355: return $text;
356: }
357:
358: ###############################################################
359:
360: ###############################################################
1.33 matthew 361: ## Home server <option> list generating code ##
362: ###############################################################
1.35 matthew 363: #-------------------------------------------
364:
365: =item get_domains()
366:
367: Returns an array containing each of the domains listed in the hosts.tab
368: file.
369:
370: =cut
371:
372: #-------------------------------------------
1.34 matthew 373: sub get_domains {
374: # The code below was stolen from "The Perl Cookbook", p 102, 1st ed.
375: my @domains;
376: my %seen;
377: foreach (sort values(%Apache::lonnet::hostdom)) {
378: push (@domains,$_) unless $seen{$_}++;
379: }
380: return @domains;
381: }
382:
1.35 matthew 383: #-------------------------------------------
384:
385: =item select_dom_form($defdom,$name)
386:
387: Returns a string containing a <select name='$name' size='1'> form to
388: allow a user to select the domain to preform an operation in.
389: See loncreateuser.pm for an example invocation and use.
390:
391: =cut
392:
393: #-------------------------------------------
1.34 matthew 394: sub select_dom_form {
395: my ($defdom,$name) = @_;
396: my @domains = get_domains();
397: my $selectdomain = "<select name=\"$name\" size=\"1\">\n";
398: foreach (@domains) {
399: $selectdomain.="<option value=\"$_\" ".
400: ($_ eq $defdom ? 'selected' : '').
401: ">$_</option>\n";
402: }
403: $selectdomain.="</select>";
404: return $selectdomain;
405: }
406:
1.35 matthew 407: #-------------------------------------------
408:
409: =item get_home_servers($domain)
410:
411: Returns a hash which contains keys like '103l3' and values like
412: 'kirk.lite.msu.edu'. All of the keys will be for machines in the
413: given $domain.
414:
415: =cut
416:
417: #-------------------------------------------
1.33 matthew 418: sub get_home_servers {
419: my $domain = shift;
420: my %home_servers;
421: foreach (keys(%Apache::lonnet::libserv)) {
422: if ($Apache::lonnet::hostdom{$_} eq $domain) {
423: $home_servers{$_} = $Apache::lonnet::hostname{$_};
424: }
425: }
426: return %home_servers;
427: }
428:
1.35 matthew 429: #-------------------------------------------
430:
431: =item home_server_option_list($domain)
432:
433: returns a string which contains an <option> list to be used in a
434: <select> form input. See loncreateuser.pm for an example.
435:
436: =cut
437:
438: #-------------------------------------------
1.33 matthew 439: sub home_server_option_list {
440: my $domain = shift;
441: my %servers = &get_home_servers($domain);
442: my $result = '';
443: foreach (sort keys(%servers)) {
444: $result.=
445: '<option value="'.$_.'">'.$_.' '.$servers{$_}."</option>\n";
446: }
447: return $result;
448: }
449: ###############################################################
450: ## End of home server <option> list generating code ##
451: ###############################################################
1.32 matthew 452:
453: ###############################################################
454: ## Authentication changing form generation subroutines ##
455: ###############################################################
456: ##
457: ## All of the authform_xxxxxxx subroutines take their inputs in a
458: ## hash, and have reasonable default values.
459: ##
460: ## formname = the name given in the <form> tag.
1.35 matthew 461: #-------------------------------------------
462:
463: =item authform_xxxxxx
464:
465: The authform_xxxxxx subroutines provide javascript and html forms which
466: handle some of the conveniences required for authentication forms.
467: This is not an optimal method, but it works.
468:
469: See loncreateuser.pm for invocation and use examples.
470:
471: =over 4
472:
473: =item authform_header
474:
475: =item authform_authorwarning
476:
477: =item authform_nochange
478:
479: =item authform_kerberos
480:
481: =item authform_internal
482:
483: =item authform_filesystem
484:
485: =back
486:
487: =cut
488:
489: #-------------------------------------------
1.32 matthew 490: sub authform_header{
491: my %in = (
492: formname => 'cu',
493: kerb_def_dom => 'MSU.EDU',
494: @_,
495: );
496: $in{'formname'} = 'document.' . $in{'formname'};
497: my $result='';
498: $result.=<<"END";
499: var current = new Object();
500: current.radiovalue = 'nochange';
501: current.argfield = null;
502:
503: function changed_radio(choice,currentform) {
504: var choicearg = choice + 'arg';
505: // If a radio button in changed, we need to change the argfield
506: if (current.radiovalue != choice) {
507: current.radiovalue = choice;
508: if (current.argfield != null) {
509: currentform.elements[current.argfield].value = '';
510: }
511: if (choice == 'nochange') {
512: current.argfield = null;
513: } else {
514: current.argfield = choicearg;
515: switch(choice) {
516: case 'krb':
517: currentform.elements[current.argfield].value =
518: "$in{'kerb_def_dom'}";
519: break;
520: default:
521: break;
522: }
523: }
524: }
525: return;
526: }
1.22 www 527:
1.32 matthew 528: function changed_text(choice,currentform) {
529: var choicearg = choice + 'arg';
530: if (currentform.elements[choicearg].value !='') {
531: switch (choice) {
532: case 'krb': currentform.elements[choicearg].value =
533: currentform.elements[choicearg].value.toUpperCase();
534: break;
535: default:
536: }
537: // clear old field
538: if ((current.argfield != choicearg) && (current.argfield != null)) {
539: currentform.elements[current.argfield].value = '';
540: }
541: current.argfield = choicearg;
542: }
543: set_auth_radio_buttons(choice,currentform);
544: return;
1.20 www 545: }
1.32 matthew 546:
547: function set_auth_radio_buttons(newvalue,currentform) {
548: var i=0;
549: while (i < currentform.login.length) {
550: if (currentform.login[i].value == newvalue) { break; }
551: i++;
552: }
553: if (i == currentform.login.length) {
554: return;
555: }
556: current.radiovalue = newvalue;
557: currentform.login[i].checked = true;
558: return;
559: }
560: END
561: return $result;
562: }
563:
564: sub authform_authorwarning{
565: my $result='';
566: $result=<<"END";
567: <i>As a general rule, only authors or co-authors should be filesystem
568: authenticated (which allows access to the server filesystem).</i>
569: END
570: return $result;
571: }
572:
573: sub authform_nochange{
574: my %in = (
575: formname => 'document.cu',
576: kerb_def_dom => 'MSU.EDU',
577: @_,
578: );
579: my $result='';
580: $result.=<<"END";
581: <input type="radio" name="login" value="nochange" checked="checked"
582: onclick="javascript:changed_radio('nochange',$in{'formname'});">
583: Do not change login data
584: END
585: return $result;
586: }
587:
588: sub authform_kerberos{
589: my %in = (
590: formname => 'document.cu',
591: kerb_def_dom => 'MSU.EDU',
592: @_,
593: );
594: my $result='';
595: $result.=<<"END";
596: <input type="radio" name="login" value="krb"
597: onclick="javascript:changed_radio('krb',$in{'formname'});"
598: onchange="javascript:changed_radio('krb',$in{'formname'});">
599: Kerberos authenticated with domain
600: <input type="text" size="10" name="krbarg" value=""
601: onchange="javascript:changed_text('krb',$in{'formname'});">
602: END
603: return $result;
604: }
605:
606: sub authform_internal{
607: my %args = (
608: formname => 'document.cu',
609: kerb_def_dom => 'MSU.EDU',
610: @_,
611: );
612: my $result='';
613: $result.=<<"END";
614: <input type="radio" name="login" value="int"
615: onchange="javascript:changed_radio('int',$args{'formname'});"
616: onclick="javascript:changed_radio('int',$args{'formname'});">
617: Internally authenticated (with initial password
618: <input type="text" size="10" name="intarg" value=""
619: onchange="javascript:changed_text('int',$args{'formname'});">
620: END
621: return $result;
622: }
623:
624: sub authform_local{
625: my %in = (
626: formname => 'document.cu',
627: kerb_def_dom => 'MSU.EDU',
628: @_,
629: );
630: my $result='';
631: $result.=<<"END";
632: <input type="radio" name="login" value="loc"
633: onchange="javascript:changed_radio('loc',$in{'formname'});"
634: onclick="javascript:changed_radio('loc',$in{'formname'});">
635: Local Authentication with argument
636: <input type="text" size="10" name="locarg" value=""
637: onchange="javascript:changed_text('loc',$in{'formname'});">
638: END
639: return $result;
640: }
641:
642: sub authform_filesystem{
643: my %in = (
644: formname => 'document.cu',
645: kerb_def_dom => 'MSU.EDU',
646: @_,
647: );
648: my $result='';
649: $result.=<<"END";
650: <input type="radio" name="login" value="fsys"
651: onchange="javascript:changed_radio('fsys',$in{'formname'});"
652: onclick="javascript:changed_radio('fsys',$in{'formname'});">
653: Filesystem authenticated (with initial password
654: <input type="text" size="10" name="fsysarg" value=""
655: onchange="javascript:changed_text('fsys',$in{'formname'});">
656: END
657: return $result;
658: }
659:
660: ###############################################################
661: ## End Authentication changing form generation functions ##
662: ###############################################################
663:
1.20 www 664:
665:
666: # ---------------------------------------------------------- Is this a keyword?
667:
668: sub keyword {
669: my $newword=shift;
670: $newword=~s/\W//g;
671: $newword=~tr/A-Z/a-z/;
672: my $tindex=$theindex{$newword};
673: if ($tindex) {
674: if ($thecount[$tindex]>$theavecount) {
675: return 1;
676: }
677: }
678: return 0;
679: }
680: # -------------------------------------------------------- Return related words
681:
682: sub related {
683: my $newword=shift;
684: $newword=~s/\W//g;
685: $newword=~tr/A-Z/a-z/;
686: my $tindex=$theindex{$newword};
687: if ($tindex) {
688: my %found=();
689: foreach (split(/\,/,$therelated[$tindex])) {
690: # - Related word found
691: my ($ridx,$rcount)=split(/\:/,$_);
692: # - Direct relation index
693: my $directrel=$rcount/$thecount[$tindex];
694: if ($directrel>$thethreshold) {
695: foreach (split(/\,/,$therelated[$ridx])) {
696: my ($rridx,$rrcount)=split(/\:/,$_);
697: if ($rridx==$tindex) {
698: # - Determine reverse relation index
699: my $revrel=$rrcount/$thecount[$ridx];
700: # - Calculate full index
701: $found{$ridx}=$directrel*$revrel;
702: if ($found{$ridx}>$thethreshold) {
703: foreach (split(/\,/,$therelated[$ridx])) {
704: my ($rrridx,$rrrcount)=split(/\:/,$_);
705: unless ($found{$rrridx}) {
706: my $revrevrel=$rrrcount/$thecount[$ridx];
707: if (
708: $directrel*$revrel*$revrevrel>$thethreshold
709: ) {
710: $found{$rrridx}=
711: $directrel*$revrel*$revrevrel;
712: }
713: }
714: }
715: }
716: }
717: }
718: }
719: }
720: }
721: return ();
1.14 harris41 722: }
723:
724: # ---------------------------------------------------------------- Language IDs
725: sub languageids {
1.16 harris41 726: return sort(keys(%language));
1.14 harris41 727: }
728:
729: # -------------------------------------------------------- Language Description
730: sub languagedescription {
1.16 harris41 731: return $language{shift(@_)};
1.14 harris41 732: }
733:
734: # --------------------------------------------------------------- Copyright IDs
735: sub copyrightids {
1.16 harris41 736: return sort(keys(%cprtag));
1.14 harris41 737: }
738:
739: # ------------------------------------------------------- Copyright Description
740: sub copyrightdescription {
1.16 harris41 741: return $cprtag{shift(@_)};
1.14 harris41 742: }
743:
744: # ------------------------------------------------------------- File Categories
745: sub filecategories {
1.41 ! ng 746: return sort(keys(%category_extensions));
1.15 harris41 747: }
1.14 harris41 748:
1.17 harris41 749: # -------------------------------------- File Types within a specified category
1.15 harris41 750: sub filecategorytypes {
1.41 ! ng 751: return @{$category_extensions{lc($_[0])}};
1.14 harris41 752: }
753:
754: # ------------------------------------------------------------------ File Types
755: sub fileextensions {
1.16 harris41 756: return sort(keys(%fe));
1.14 harris41 757: }
758:
759: # ------------------------------------------------------------- Embedding Style
760: sub fileembstyle {
1.16 harris41 761: return $fe{lc(shift(@_))};
1.14 harris41 762: }
763:
764: # ------------------------------------------------------------ Description Text
765: sub filedescription {
1.16 harris41 766: return $fd{lc(shift(@_))};
767: }
768:
769: # ------------------------------------------------------------ Description Text
770: sub filedescriptionex {
771: my $ex=shift;
772: return '.'.$ex.' '.$fd{lc($ex)};
1.12 harris41 773: }
1.1 albertel 774:
1.40 ng 775: # ---- Retrieve attempts by students
776: # input
777: # $symb - problem including path
778: # $username,$domain - that of the student
779: # $course - course name
780: # $getattempt - leave blank if want all attempts, else put something.
781: #
782: # output
783: # formatted as a table all the attempts, if any.
784: #
1.1 albertel 785: sub get_previous_attempt {
1.40 ng 786: my ($symb,$username,$domain,$course,$getattempt)=@_;
1.1 albertel 787: my $prevattempts='';
788: if ($symb) {
1.3 albertel 789: my (%returnhash)=
790: &Apache::lonnet::restore($symb,$course,$domain,$username);
1.1 albertel 791: if ($returnhash{'version'}) {
792: my %lasthash=();
793: my $version;
794: for ($version=1;$version<=$returnhash{'version'};$version++) {
1.19 harris41 795: foreach (sort(split(/\:/,$returnhash{$version.':keys'}))) {
1.1 albertel 796: $lasthash{$_}=$returnhash{$version.':'.$_};
1.19 harris41 797: }
1.1 albertel 798: }
1.40 ng 799: $prevattempts='<table border="0" width="100%"><tr><td bgcolor="#000000">';
800: $prevattempts.='<table border="0" width="100%"><tr bgcolor="#e6ffff"><td>History</td>';
1.16 harris41 801: foreach (sort(keys %lasthash)) {
1.31 albertel 802: my ($ign,@parts) = split(/\./,$_);
1.41 ! ng 803: if ($#parts > 0) {
1.31 albertel 804: my $data=$parts[-1];
805: pop(@parts);
1.40 ng 806: $prevattempts.='<td>Part '.join('.',@parts).'<br />'.$data.' </td>';
1.31 albertel 807: } else {
1.41 ! ng 808: if ($#parts == 0) {
! 809: $prevattempts.='<th>'.$parts[0].'</th>';
! 810: } else {
! 811: $prevattempts.='<th>'.$ign.'</th>';
! 812: }
1.31 albertel 813: }
1.16 harris41 814: }
1.40 ng 815: if ($getattempt eq '') {
816: for ($version=1;$version<=$returnhash{'version'};$version++) {
817: $prevattempts.='</tr><tr bgcolor="#ffffe6"><td>Transaction '.$version.'</td>';
818: foreach (sort(keys %lasthash)) {
819: my $value;
820: if ($_ =~ /timestamp/) {
821: $value=scalar(localtime($returnhash{$version.':'.$_}));
822: } else {
823: $value=$returnhash{$version.':'.$_};
824: }
825: $prevattempts.='<td>'.$value.' </td>';
826: }
827: }
1.1 albertel 828: }
1.40 ng 829: $prevattempts.='</tr><tr bgcolor="#ffffe6"><td>Current</td>';
1.16 harris41 830: foreach (sort(keys %lasthash)) {
1.5 albertel 831: my $value;
832: if ($_ =~ /timestamp/) {
833: $value=scalar(localtime($lasthash{$_}));
834: } else {
835: $value=$lasthash{$_};
836: }
1.40 ng 837: $prevattempts.='<td>'.$value.' </td>';
1.16 harris41 838: }
1.40 ng 839: $prevattempts.='</tr></table></td></tr></table>';
1.1 albertel 840: } else {
841: $prevattempts='Nothing submitted - no attempts.';
842: }
843: } else {
844: $prevattempts='No data.';
845: }
1.10 albertel 846: }
847:
848: sub get_student_view {
849: my ($symb,$username,$domain,$courseid) = @_;
850: my ($map,$id,$feedurl) = split(/___/,$symb);
851: my (%old,%moreenv);
852: my @elements=('symb','courseid','domain','username');
853: foreach my $element (@elements) {
854: $old{$element}=$ENV{'form.grade_'.$element};
855: $moreenv{'form.grade_'.$element}=eval '$'.$element #'
856: }
1.11 albertel 857: &Apache::lonnet::appenv(%moreenv);
858: my $userview=&Apache::lonnet::ssi('/res/'.$feedurl);
859: &Apache::lonnet::delenv('form.grade_');
860: foreach my $element (@elements) {
861: $ENV{'form.grade_'.$element}=$old{$element};
862: }
863: $userview=~s/\<body[^\>]*\>//gi;
864: $userview=~s/\<\/body\>//gi;
865: $userview=~s/\<html\>//gi;
866: $userview=~s/\<\/html\>//gi;
867: $userview=~s/\<head\>//gi;
868: $userview=~s/\<\/head\>//gi;
869: $userview=~s/action\s*\=/would_be_action\=/gi;
870: return $userview;
871: }
872:
873: sub get_student_answers {
874: my ($symb,$username,$domain,$courseid) = @_;
875: my ($map,$id,$feedurl) = split(/___/,$symb);
876: my (%old,%moreenv);
877: my @elements=('symb','courseid','domain','username');
878: foreach my $element (@elements) {
879: $old{$element}=$ENV{'form.grade_'.$element};
880: $moreenv{'form.grade_'.$element}=eval '$'.$element #'
881: }
882: $moreenv{'form.grade_target'}='answer';
1.10 albertel 883: &Apache::lonnet::appenv(%moreenv);
884: my $userview=&Apache::lonnet::ssi('/res/'.$feedurl);
885: &Apache::lonnet::delenv('form.grade_');
886: foreach my $element (@elements) {
887: $ENV{'form.grade_'.$element}=$old{$element};
888: }
889: return $userview;
1.1 albertel 890: }
1.37 matthew 891:
892: ###############################################
893:
894: ###############################################
1.1 albertel 895:
1.6 albertel 896: sub get_unprocessed_cgi {
1.25 albertel 897: my ($query,$possible_names)= @_;
1.26 matthew 898: # $Apache::lonxml::debug=1;
1.16 harris41 899: foreach (split(/&/,$query)) {
1.6 albertel 900: my ($name, $value) = split(/=/,$_);
1.25 albertel 901: $name = &Apache::lonnet::unescape($name);
902: if (!defined($possible_names) || (grep {$_ eq $name} @$possible_names)) {
903: $value =~ tr/+/ /;
904: $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
905: &Apache::lonxml::debug("Seting :$name: to :$value:");
1.30 albertel 906: unless (defined($ENV{'form.'.$name})) { &add_to_env('form.'.$name,$value) };
1.25 albertel 907: }
1.16 harris41 908: }
1.6 albertel 909: }
910:
1.7 albertel 911: sub cacheheader {
1.23 www 912: unless ($ENV{'request.method'} eq 'GET') { return ''; }
1.8 albertel 913: my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime);
1.7 albertel 914: my $output .='<meta HTTP-EQUIV="Expires" CONTENT="'.$date.'" />
915: <meta HTTP-EQUIV="Cache-control" CONTENT="no-cache" />
916: <meta HTTP-EQUIV="Pragma" CONTENT="no-cache" />';
917: return $output;
918: }
919:
1.9 albertel 920: sub no_cache {
921: my ($r) = @_;
1.23 www 922: unless ($ENV{'request.method'} eq 'GET') { return ''; }
1.24 albertel 923: #my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime);
1.9 albertel 924: $r->no_cache(1);
925: $r->header_out("Pragma" => "no-cache");
1.24 albertel 926: #$r->header_out("Expires" => $date);
1.9 albertel 927: }
1.25 albertel 928:
929: sub add_to_env {
930: my ($name,$value)=@_;
1.28 albertel 931: if (defined($ENV{$name})) {
1.27 albertel 932: if (ref($ENV{$name})) {
1.25 albertel 933: #already have multiple values
934: push(@{ $ENV{$name} },$value);
935: } else {
936: #first time seeing multiple values, convert hash entry to an arrayref
937: my $first=$ENV{$name};
938: undef($ENV{$name});
939: push(@{ $ENV{$name} },$first,$value);
940: }
941: } else {
942: $ENV{$name}=$value;
943: }
1.31 albertel 944: }
945:
1.41 ! ng 946: =pod
! 947:
! 948: =head2 CSV Upload/Handling functions
1.38 albertel 949:
1.41 ! ng 950: =over 4
! 951:
! 952: =item upfile_store($r)
! 953:
! 954: Store uploaded file, $r should be the HTTP Request object,
! 955: needs $ENV{'form.upfile'}
! 956: returns $datatoken to be put into hidden field
! 957:
! 958: =cut
1.31 albertel 959:
960: sub upfile_store {
961: my $r=shift;
962: $ENV{'form.upfile'}=~s/\r/\n/gs;
963: $ENV{'form.upfile'}=~s/\f/\n/gs;
964: $ENV{'form.upfile'}=~s/\n+/\n/gs;
965: $ENV{'form.upfile'}=~s/\n+$//gs;
966:
967: my $datatoken=$ENV{'user.name'}.'_'.$ENV{'user.domain'}.
968: '_enroll_'.$ENV{'request.course.id'}.'_'.time.'_'.$$;
969: {
970: my $fh=Apache::File->new('>'.$r->dir_config('lonDaemons').
971: '/tmp/'.$datatoken.'.tmp');
972: print $fh $ENV{'form.upfile'};
973: }
974: return $datatoken;
975: }
976:
1.41 ! ng 977: =item load_tmp_file($r)
! 978:
! 979: Load uploaded file from tmp, $r should be the HTTP Request object,
! 980: needs $ENV{'form.datatoken'},
! 981: sets $ENV{'form.upfile'} to the contents of the file
! 982:
! 983: =cut
1.31 albertel 984:
985: sub load_tmp_file {
986: my $r=shift;
987: my @studentdata=();
988: {
989: my $fh;
990: if ($fh=Apache::File->new($r->dir_config('lonDaemons').
991: '/tmp/'.$ENV{'form.datatoken'}.'.tmp')) {
992: @studentdata=<$fh>;
993: }
994: }
995: $ENV{'form.upfile'}=join('',@studentdata);
996: }
997:
1.41 ! ng 998: =item upfile_record_sep()
! 999:
! 1000: Separate uploaded file into records
! 1001: returns array of records,
! 1002: needs $ENV{'form.upfile'} and $ENV{'form.upfiletype'}
! 1003:
! 1004: =cut
1.31 albertel 1005:
1006: sub upfile_record_sep {
1007: if ($ENV{'form.upfiletype'} eq 'xml') {
1008: } else {
1009: return split(/\n/,$ENV{'form.upfile'});
1010: }
1011: }
1012:
1.41 ! ng 1013: =item record_sep($record)
! 1014:
! 1015: Separate a record into fields $record should be an item from the upfile_record_sep(), needs $ENV{'form.upfiletype'}
! 1016:
! 1017: =cut
! 1018:
1.31 albertel 1019: sub record_sep {
1020: my $record=shift;
1021: my %components=();
1022: if ($ENV{'form.upfiletype'} eq 'xml') {
1023: } elsif ($ENV{'form.upfiletype'} eq 'space') {
1024: my $i=0;
1025: foreach (split(/\s+/,$record)) {
1026: my $field=$_;
1027: $field=~s/^(\"|\')//;
1028: $field=~s/(\"|\')$//;
1029: $components{$i}=$field;
1030: $i++;
1031: }
1032: } elsif ($ENV{'form.upfiletype'} eq 'tab') {
1033: my $i=0;
1034: foreach (split(/\t+/,$record)) {
1035: my $field=$_;
1036: $field=~s/^(\"|\')//;
1037: $field=~s/(\"|\')$//;
1038: $components{$i}=$field;
1039: $i++;
1040: }
1041: } else {
1042: my @allfields=split(/\,/,$record);
1043: my $i=0;
1044: my $j;
1045: for ($j=0;$j<=$#allfields;$j++) {
1046: my $field=$allfields[$j];
1047: if ($field=~/^\s*(\"|\')/) {
1048: my $delimiter=$1;
1049: while (($field!~/$delimiter$/) && ($j<$#allfields)) {
1050: $j++;
1051: $field.=','.$allfields[$j];
1052: }
1053: $field=~s/^\s*$delimiter//;
1054: $field=~s/$delimiter\s*$//;
1055: }
1056: $components{$i}=$field;
1057: $i++;
1058: }
1059: }
1060: return %components;
1061: }
1062:
1.41 ! ng 1063: =item upfile_select_html()
! 1064:
! 1065: return HTML code to select file and specify its type
! 1066:
! 1067: =cut
! 1068:
1.31 albertel 1069: sub upfile_select_html {
1070: return (<<'ENDUPFORM');
1071: <input type="file" name="upfile" size="50">
1072: <br />Type: <select name="upfiletype">
1073: <option value="csv">CSV (comma separated values, spreadsheet)</option>
1074: <option value="space">Space separated</option>
1075: <option value="tab">Tabulator separated</option>
1076: <option value="xml">HTML/XML</option>
1077: </select>
1078: ENDUPFORM
1079: }
1080:
1.41 ! ng 1081: =item csv_print_samples($r,$records)
! 1082:
! 1083: Prints a table of sample values from each column uploaded $r is an
! 1084: Apache Request ref, $records is an arrayref from
! 1085: &Apache::loncommon::upfile_record_sep
! 1086:
! 1087: =cut
! 1088:
1.31 albertel 1089: sub csv_print_samples {
1090: my ($r,$records) = @_;
1091: my (%sone,%stwo,%sthree);
1092: %sone=&record_sep($$records[0]);
1093: if (defined($$records[1])) {%stwo=&record_sep($$records[1]);}
1094: if (defined($$records[2])) {%sthree=&record_sep($$records[2]);}
1095:
1096: $r->print('Samples<br /><table border="2"><tr>');
1097: foreach (sort({$a <=> $b} keys(%sone))) { $r->print('<th>Column '.($_+1).'</th>'); }
1098: $r->print('</tr>');
1099: foreach my $hash (\%sone,\%stwo,\%sthree) {
1100: $r->print('<tr>');
1101: foreach (sort({$a <=> $b} keys(%sone))) {
1102: $r->print('<td>');
1103: if (defined($$hash{$_})) { $r->print($$hash{$_}); }
1104: $r->print('</td>');
1105: }
1106: $r->print('</tr>');
1107: }
1108: $r->print('</tr></table><br />'."\n");
1109: }
1110:
1.41 ! ng 1111: =item csv_print_select_table($r,$records,$d)
! 1112:
! 1113: Prints a table to create associations between values and table columns.
! 1114: $r is an Apache Request ref,
! 1115: $records is an arrayref from &Apache::loncommon::upfile_record_sep,
! 1116: $d is an array of 2 element arrays (internal name, displayed name)
! 1117:
! 1118: =cut
! 1119:
1.31 albertel 1120: sub csv_print_select_table {
1121: my ($r,$records,$d) = @_;
1122: my $i=0;my %sone;
1123: %sone=&record_sep($$records[0]);
1124: $r->print('Associate columns with student attributes.'."\n".
1125: '<table border="2"><tr><th>Attribute</th><th>Column</th></tr>'."\n");
1126: foreach (@$d) {
1127: my ($value,$display)=@{ $_ };
1128: $r->print('<tr><td>'.$display.'</td>');
1129:
1130: $r->print('<td><select name=f'.$i.
1.32 matthew 1131: ' onchange="javascript:flip(this.form,'.$i.');">');
1.31 albertel 1132: $r->print('<option value="none"></option>');
1133: foreach (sort({$a <=> $b} keys(%sone))) {
1134: $r->print('<option value="'.$_.'">Column '.($_+1).'</option>');
1135: }
1136: $r->print('</select></td></tr>'."\n");
1137: $i++;
1138: }
1139: $i--;
1140: return $i;
1141: }
1142:
1.41 ! ng 1143: =item csv_samples_select_table($r,$records,$d)
! 1144:
! 1145: Prints a table of sample values from the upload and can make associate samples to internal names.
! 1146:
! 1147: $r is an Apache Request ref,
! 1148: $records is an arrayref from &Apache::loncommon::upfile_record_sep,
! 1149: $d is an array of 2 element arrays (internal name, displayed name)
! 1150:
! 1151: =cut
! 1152:
1.31 albertel 1153: sub csv_samples_select_table {
1154: my ($r,$records,$d) = @_;
1155: my %sone; my %stwo; my %sthree;
1156: my $i=0;
1157:
1158: $r->print('<table border=2><tr><th>Field</th><th>Samples</th></tr>');
1159: %sone=&record_sep($$records[0]);
1160: if (defined($$records[1])) {%stwo=&record_sep($$records[1]);}
1161: if (defined($$records[2])) {%sthree=&record_sep($$records[2]);}
1162:
1163: foreach (sort keys %sone) {
1164: $r->print('<tr><td><select name=f'.$i.
1.32 matthew 1165: ' onchange="javascript:flip(this.form,'.$i.');">');
1.31 albertel 1166: foreach (@$d) {
1167: my ($value,$display)=@{ $_ };
1168: $r->print('<option value='.$value.'>'.$display.'</option>');
1169: }
1170: $r->print('</select></td><td>');
1171: if (defined($sone{$_})) { $r->print($sone{$_}."</br>\n"); }
1172: if (defined($stwo{$_})) { $r->print($stwo{$_}."</br>\n"); }
1173: if (defined($sthree{$_})) { $r->print($sthree{$_}."</br>\n"); }
1174: $r->print('</td></tr>');
1175: $i++;
1176: }
1177: $i--;
1178: return($i);
1.25 albertel 1179: }
1.1 albertel 1180: 1;
1181: __END__;
1.17 harris41 1182:
1.41 ! ng 1183: =pod
! 1184:
! 1185: =back
! 1186:
! 1187: =head2 Access .tab File Data
! 1188:
! 1189: =over 4
! 1190:
1.35 matthew 1191: =item languageids()
1.17 harris41 1192:
1.35 matthew 1193: returns list of all language ids
1.17 harris41 1194:
1.35 matthew 1195: =item languagedescription()
1.17 harris41 1196:
1.35 matthew 1197: returns description of a specified language id
1.17 harris41 1198:
1.35 matthew 1199: =item copyrightids()
1.17 harris41 1200:
1.35 matthew 1201: returns list of all copyrights
1.17 harris41 1202:
1.35 matthew 1203: =item copyrightdescription()
1.17 harris41 1204:
1.35 matthew 1205: returns description of a specified copyright id
1.17 harris41 1206:
1.35 matthew 1207: =item filecategories()
1.17 harris41 1208:
1.35 matthew 1209: returns list of all file categories
1.17 harris41 1210:
1.35 matthew 1211: =item filecategorytypes()
1.17 harris41 1212:
1.35 matthew 1213: returns list of file types belonging to a given file
1.17 harris41 1214: category
1215:
1.35 matthew 1216: =item fileembstyle()
1.17 harris41 1217:
1.35 matthew 1218: returns embedding style for a specified file type
1.17 harris41 1219:
1.35 matthew 1220: =item filedescription()
1.17 harris41 1221:
1.35 matthew 1222: returns description for a specified file type
1.17 harris41 1223:
1.35 matthew 1224: =item filedescriptionex()
1.17 harris41 1225:
1.35 matthew 1226: returns description for a specified file type with
1.17 harris41 1227: extra formatting
1228:
1.41 ! ng 1229: =back
! 1230:
! 1231: =head2 Alternate Problem Views
! 1232:
! 1233: =over 4
! 1234:
1.35 matthew 1235: =item get_previous_attempt()
1.17 harris41 1236:
1.35 matthew 1237: return string with previous attempt on problem
1.17 harris41 1238:
1.35 matthew 1239: =item get_student_view()
1.17 harris41 1240:
1.35 matthew 1241: show a snapshot of what student was looking at
1.17 harris41 1242:
1.35 matthew 1243: =item get_student_answers()
1.17 harris41 1244:
1.35 matthew 1245: show a snapshot of how student was answering problem
1.17 harris41 1246:
1.41 ! ng 1247: =back
! 1248:
! 1249: =head2 HTTP Helper
! 1250:
! 1251: =over 4
! 1252:
! 1253: =item get_unprocessed_cgi($query,$possible_names)
! 1254:
! 1255: Modify the %ENV hash to contain unprocessed CGI form parameters held in
! 1256: $query. The parameters listed in $possible_names (an array reference),
! 1257: will be set in $ENV{'form.name'} if they do not already exist.
1.17 harris41 1258:
1.41 ! ng 1259: Typically called with $ENV{'QUERY_STRING'} as the first parameter.
! 1260: $possible_names is an ref to an array of form element names. As an example:
! 1261: get_unprocessed_cgi($ENV{'QUERY_STRING'},['uname','udom']);
! 1262: will result in $ENV{'form.uname'} and $ENV{'form.udom'} being set.
1.17 harris41 1263:
1.35 matthew 1264: =item cacheheader()
1.17 harris41 1265:
1.35 matthew 1266: returns cache-controlling header code
1.17 harris41 1267:
1.35 matthew 1268: =item nocache()
1.17 harris41 1269:
1.35 matthew 1270: specifies header code to not have cache
1.25 albertel 1271:
1.35 matthew 1272: =item add_to_env($name,$value)
1.25 albertel 1273:
1.35 matthew 1274: adds $name to the %ENV hash with value
1.25 albertel 1275: $value, if $name already exists, the entry is converted to an array
1276: reference and $value is added to the array.
1.17 harris41 1277:
1278: =back
1279:
1280: =cut
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>