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