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