Annotation of loncom/interface/loncommon.pm, revision 1.550
1.10 albertel 1: # The LearningOnline Network with CAPA
1.1 albertel 2: # a pile of common routines
1.10 albertel 3: #
1.550 ! albertel 4: # $Id: loncommon.pm,v 1.549 2007/07/11 20:37:52 albertel 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.1 albertel 28:
29: # Makes a table out of the previous attempts
1.2 albertel 30: # Inputs result_from_symbread, user, domain, course_id
1.16 harris41 31: # Reads in non-network-related .tab files
1.1 albertel 32:
1.35 matthew 33: # POD header:
34:
1.45 matthew 35: =pod
36:
1.35 matthew 37: =head1 NAME
38:
39: Apache::loncommon - pile of common routines
40:
41: =head1 SYNOPSIS
42:
1.112 bowersj2 43: Common routines for manipulating connections, student answers,
44: domains, common Javascript fragments, etc.
1.35 matthew 45:
1.112 bowersj2 46: =head1 OVERVIEW
1.35 matthew 47:
1.112 bowersj2 48: A collection of commonly used subroutines that don't have a natural
49: home anywhere else. This collection helps remove
1.35 matthew 50: redundancy from other modules and increase efficiency of memory usage.
51:
52: =cut
53:
54: # End of POD header
1.1 albertel 55: package Apache::loncommon;
56:
57: use strict;
1.258 albertel 58: use Apache::lonnet;
1.46 matthew 59: use GDBM_File;
1.51 www 60: use POSIX qw(strftime mktime);
1.82 www 61: use Apache::lonmenu();
1.498 albertel 62: use Apache::lonenc();
1.117 www 63: use Apache::lonlocal;
1.139 matthew 64: use HTML::Entities;
1.334 albertel 65: use Apache::lonhtmlcommon();
66: use Apache::loncoursedata();
1.344 albertel 67: use Apache::lontexconvert();
1.444 albertel 68: use Apache::lonclonecourse();
1.479 albertel 69: use LONCAPA qw(:DEFAULT :match);
1.117 www 70:
1.517 raeburn 71: # ---------------------------------------------- Designs
72: use vars qw(%defaultdesign);
73:
1.22 www 74: my $readit;
75:
1.517 raeburn 76:
1.157 matthew 77: ##
78: ## Global Variables
79: ##
1.46 matthew 80:
1.20 www 81: # ----------------------------------------------- Filetypes/Languages/Copyright
1.12 harris41 82: my %language;
1.124 www 83: my %supported_language;
1.12 harris41 84: my %cprtag;
1.192 taceyjo1 85: my %scprtag;
1.351 www 86: my %fe; my %fd; my %fm;
1.41 ng 87: my %category_extensions;
1.12 harris41 88:
1.46 matthew 89: # ---------------------------------------------- Thesaurus variables
1.144 matthew 90: #
91: # %Keywords:
92: # A hash used by &keyword to determine if a word is considered a keyword.
93: # $thesaurus_db_file
94: # Scalar containing the full path to the thesaurus database.
1.46 matthew 95:
96: my %Keywords;
97: my $thesaurus_db_file;
98:
1.144 matthew 99: #
100: # Initialize values from language.tab, copyright.tab, filetypes.tab,
101: # thesaurus.tab, and filecategories.tab.
102: #
1.18 www 103: BEGIN {
1.46 matthew 104: # Variable initialization
105: $thesaurus_db_file = $Apache::lonnet::perlvar{'lonTabDir'}."/thesaurus.db";
106: #
1.22 www 107: unless ($readit) {
1.12 harris41 108: # ------------------------------------------------------------------- languages
109: {
1.158 raeburn 110: my $langtabfile = $Apache::lonnet::perlvar{'lonTabDir'}.
111: '/language.tab';
112: if ( open(my $fh,"<$langtabfile") ) {
1.356 albertel 113: while (my $line = <$fh>) {
114: next if ($line=~/^\#/);
115: chomp($line);
116: my ($key,$two,$country,$three,$enc,$val,$sup)=(split(/\t/,$line));
1.158 raeburn 117: $language{$key}=$val.' - '.$enc;
118: if ($sup) {
119: $supported_language{$key}=$sup;
120: }
121: }
122: close($fh);
123: }
1.12 harris41 124: }
125: # ------------------------------------------------------------------ copyrights
126: {
1.158 raeburn 127: my $copyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}.
128: '/copyright.tab';
129: if ( open (my $fh,"<$copyrightfile") ) {
1.356 albertel 130: while (my $line = <$fh>) {
131: next if ($line=~/^\#/);
132: chomp($line);
133: my ($key,$val)=(split(/\s+/,$line,2));
1.158 raeburn 134: $cprtag{$key}=$val;
135: }
136: close($fh);
137: }
1.12 harris41 138: }
1.351 www 139: # ----------------------------------------------------------- source copyrights
1.192 taceyjo1 140: {
141: my $sourcecopyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}.
142: '/source_copyright.tab';
143: if ( open (my $fh,"<$sourcecopyrightfile") ) {
1.356 albertel 144: while (my $line = <$fh>) {
145: next if ($line =~ /^\#/);
146: chomp($line);
147: my ($key,$val)=(split(/\s+/,$line,2));
1.192 taceyjo1 148: $scprtag{$key}=$val;
149: }
150: close($fh);
151: }
152: }
1.63 www 153:
1.517 raeburn 154: # -------------------------------------------------------------- default domain designs
1.63 www 155: my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors';
1.517 raeburn 156: my $designfile = $designdir.'/default.tab';
157: if ( open (my $fh,"<$designfile") ) {
158: while (my $line = <$fh>) {
159: next if ($line =~ /^\#/);
160: chomp($line);
161: my ($key,$val)=(split(/\=/,$line));
162: if ($val) { $defaultdesign{$key}=$val; }
163: }
164: close($fh);
1.63 www 165: }
166:
1.15 harris41 167: # ------------------------------------------------------------- file categories
168: {
1.158 raeburn 169: my $categoryfile = $Apache::lonnet::perlvar{'lonTabDir'}.
170: '/filecategories.tab';
171: if ( open (my $fh,"<$categoryfile") ) {
1.356 albertel 172: while (my $line = <$fh>) {
173: next if ($line =~ /^\#/);
174: chomp($line);
175: my ($extension,$category)=(split(/\s+/,$line,2));
1.158 raeburn 176: push @{$category_extensions{lc($category)}},$extension;
177: }
178: close($fh);
179: }
180:
1.15 harris41 181: }
1.12 harris41 182: # ------------------------------------------------------------------ file types
183: {
1.158 raeburn 184: my $typesfile = $Apache::lonnet::perlvar{'lonTabDir'}.
185: '/filetypes.tab';
186: if ( open (my $fh,"<$typesfile") ) {
1.356 albertel 187: while (my $line = <$fh>) {
188: next if ($line =~ /^\#/);
189: chomp($line);
190: my ($ending,$emb,$mime,$descr)=split(/\s+/,$line,4);
1.158 raeburn 191: if ($descr ne '') {
192: $fe{$ending}=lc($emb);
193: $fd{$ending}=$descr;
1.351 www 194: if ($mime ne 'unk') { $fm{$ending}=$mime; }
1.158 raeburn 195: }
196: }
197: close($fh);
198: }
1.12 harris41 199: }
1.22 www 200: &Apache::lonnet::logthis(
1.46 matthew 201: "<font color=yellow>INFO: Read file types</font>");
1.22 www 202: $readit=1;
1.46 matthew 203: } # end of unless($readit)
1.32 matthew 204:
205: }
1.112 bowersj2 206:
1.42 matthew 207: ###############################################################
208: ## HTML and Javascript Helper Functions ##
209: ###############################################################
210:
211: =pod
212:
1.112 bowersj2 213: =head1 HTML and Javascript Functions
1.42 matthew 214:
1.112 bowersj2 215: =over 4
216:
217: =item * browser_and_searcher_javascript ()
218:
219: X<browsing, javascript>X<searching, javascript>Returns a string
220: containing javascript with two functions, C<openbrowser> and
221: C<opensearcher>. Returned string does not contain E<lt>scriptE<gt>
222: tags.
1.42 matthew 223:
1.112 bowersj2 224: =item * openbrowser(formname,elementname,only,omit) [javascript]
1.42 matthew 225:
226: inputs: formname, elementname, only, omit
227:
228: formname and elementname indicate the name of the html form and name of
229: the element that the results of the browsing selection are to be placed in.
230:
231: Specifying 'only' will restrict the browser to displaying only files
1.185 www 232: with the given extension. Can be a comma separated list.
1.42 matthew 233:
234: Specifying 'omit' will restrict the browser to NOT displaying files
1.185 www 235: with the given extension. Can be a comma separated list.
1.42 matthew 236:
1.112 bowersj2 237: =item * opensearcher(formname, elementname) [javascript]
1.42 matthew 238:
239: Inputs: formname, elementname
240:
241: formname and elementname specify the name of the html form and the name
242: of the element the selection from the search results will be placed in.
1.542 raeburn 243:
1.42 matthew 244: =cut
245:
246: sub browser_and_searcher_javascript {
1.199 albertel 247: my ($mode)=@_;
248: if (!defined($mode)) { $mode='edit'; }
1.453 albertel 249: my $resurl=&escape_single(&lastresurl());
1.42 matthew 250: return <<END;
1.219 albertel 251: // <!-- BEGIN LON-CAPA Internal
1.50 matthew 252: var editbrowser = null;
1.135 albertel 253: function openbrowser(formname,elementname,only,omit,titleelement) {
1.170 www 254: var url = '$resurl/?';
1.42 matthew 255: if (editbrowser == null) {
256: url += 'launch=1&';
257: }
258: url += 'catalogmode=interactive&';
1.199 albertel 259: url += 'mode=$mode&';
1.42 matthew 260: url += 'form=' + formname + '&';
261: if (only != null) {
262: url += 'only=' + only + '&';
1.217 albertel 263: } else {
264: url += 'only=&';
265: }
1.42 matthew 266: if (omit != null) {
267: url += 'omit=' + omit + '&';
1.217 albertel 268: } else {
269: url += 'omit=&';
270: }
1.135 albertel 271: if (titleelement != null) {
272: url += 'titleelement=' + titleelement + '&';
1.217 albertel 273: } else {
274: url += 'titleelement=&';
275: }
1.42 matthew 276: url += 'element=' + elementname + '';
277: var title = 'Browser';
1.435 albertel 278: var options = 'scrollbars=1,resizable=1,menubar=0,toolbar=1,location=1';
1.42 matthew 279: options += ',width=700,height=600';
280: editbrowser = open(url,title,options,'1');
281: editbrowser.focus();
282: }
283: var editsearcher;
1.135 albertel 284: function opensearcher(formname,elementname,titleelement) {
1.42 matthew 285: var url = '/adm/searchcat?';
286: if (editsearcher == null) {
287: url += 'launch=1&';
288: }
289: url += 'catalogmode=interactive&';
1.199 albertel 290: url += 'mode=$mode&';
1.42 matthew 291: url += 'form=' + formname + '&';
1.135 albertel 292: if (titleelement != null) {
293: url += 'titleelement=' + titleelement + '&';
1.217 albertel 294: } else {
295: url += 'titleelement=&';
296: }
1.42 matthew 297: url += 'element=' + elementname + '';
298: var title = 'Search';
1.435 albertel 299: var options = 'scrollbars=1,resizable=1,menubar=0,toolbar=1,location=1';
1.42 matthew 300: options += ',width=700,height=600';
301: editsearcher = open(url,title,options,'1');
302: editsearcher.focus();
303: }
1.219 albertel 304: // END LON-CAPA Internal -->
1.42 matthew 305: END
1.170 www 306: }
307:
308: sub lastresurl {
1.258 albertel 309: if ($env{'environment.lastresurl'}) {
310: return $env{'environment.lastresurl'}
1.170 www 311: } else {
312: return '/res';
313: }
314: }
315:
316: sub storeresurl {
317: my $resurl=&Apache::lonnet::clutter(shift);
318: unless ($resurl=~/^\/res/) { return 0; }
319: $resurl=~s/\/$//;
320: &Apache::lonnet::put('environment',{'lastresurl' => $resurl});
321: &Apache::lonnet::appenv('environment.lastresurl' => $resurl);
322: return 1;
1.42 matthew 323: }
324:
1.74 www 325: sub studentbrowser_javascript {
1.111 www 326: unless (
1.258 albertel 327: (($env{'request.course.id'}) &&
1.302 albertel 328: (&Apache::lonnet::allowed('srm',$env{'request.course.id'})
329: || &Apache::lonnet::allowed('srm',$env{'request.course.id'}.
330: '/'.$env{'request.course.sec'})
331: ))
1.258 albertel 332: || ($env{'request.role'}=~/^(au|dc|su)/)
1.111 www 333: ) { return ''; }
1.74 www 334: return (<<'ENDSTDBRW');
335: <script type="text/javascript" language="Javascript" >
336: var stdeditbrowser;
1.111 www 337: function openstdbrowser(formname,uname,udom,roleflag) {
1.74 www 338: var url = '/adm/pickstudent?';
339: var filter;
340: eval('filter=document.'+formname+'.'+uname+'.value;');
341: if (filter != null) {
342: if (filter != '') {
343: url += 'filter='+filter+'&';
344: }
345: }
346: url += 'form=' + formname + '&unameelement='+uname+
347: '&udomelement='+udom;
1.111 www 348: if (roleflag) { url+="&roles=1"; }
1.102 www 349: var title = 'Student_Browser';
1.74 www 350: var options = 'scrollbars=1,resizable=1,menubar=0';
351: options += ',width=700,height=600';
352: stdeditbrowser = open(url,title,options,'1');
353: stdeditbrowser.focus();
354: }
355: </script>
356: ENDSTDBRW
357: }
1.42 matthew 358:
1.74 www 359: sub selectstudent_link {
1.111 www 360: my ($form,$unameele,$udomele)=@_;
1.258 albertel 361: if ($env{'request.course.id'}) {
1.302 albertel 362: if (!&Apache::lonnet::allowed('srm',$env{'request.course.id'})
363: && !&Apache::lonnet::allowed('srm',$env{'request.course.id'}.
364: '/'.$env{'request.course.sec'})) {
1.111 www 365: return '';
366: }
367: return "<a href='".'javascript:openstdbrowser("'.$form.'","'.$unameele.
1.119 www 368: '","'.$udomele.'");'."'>".&mt('Select User')."</a>";
1.74 www 369: }
1.258 albertel 370: if ($env{'request.role'}=~/^(au|dc|su)/) {
1.111 www 371: return "<a href='".'javascript:openstdbrowser("'.$form.'","'.$unameele.
1.119 www 372: '","'.$udomele.'",1);'."'>".&mt('Select User')."</a>";
1.111 www 373: }
374: return '';
1.91 www 375: }
376:
377: sub coursebrowser_javascript {
1.468 raeburn 378: my ($domainfilter,$sec_element,$formname)=@_;
1.377 raeburn 379: my $crs_or_grp_alert = &mt('Please select the type of LON-CAPA entity - Course or Group - for which you wish to add/modify a user role');
1.468 raeburn 380: my $output = '
1.538 albertel 381: <script type="text/javascript">
1.468 raeburn 382: var stdeditbrowser;'."\n";
383: $output .= <<"ENDSTDBRW";
1.377 raeburn 384: function opencrsbrowser(formname,uname,udom,desc,extra_element,multflag,crstype) {
1.91 www 385: var url = '/adm/pickcourse?';
1.468 raeburn 386: var domainfilter = '';
387: var formid = getFormIdByName(formname);
388: if (formid > -1) {
389: var domid = getIndexByName(formid,udom);
390: if (domid > -1) {
391: if (document.forms[formid].elements[domid].type == 'select-one') {
392: domainfilter=document.forms[formid].elements[domid].options[document.forms[formid].elements[domid].selectedIndex].value;
393: }
394: if (document.forms[formid].elements[domid].type == 'hidden') {
395: domainfilter=document.forms[formid].elements[domid].value;
396: }
397: }
1.91 www 398: }
1.128 albertel 399: if (domainfilter != null) {
400: if (domainfilter != '') {
401: url += 'domainfilter='+domainfilter+'&';
402: }
403: }
1.91 www 404: url += 'form=' + formname + '&cnumelement='+uname+
1.187 albertel 405: '&cdomelement='+udom+
406: '&cnameelement='+desc;
1.468 raeburn 407: if (extra_element !=null && extra_element != '') {
408: if (formname == 'rolechoice') {
409: url += '&roleelement='+extra_element;
410: if (domainfilter == null || domainfilter == '') {
411: url += '&domainfilter='+extra_element;
412: }
1.234 raeburn 413: }
1.468 raeburn 414: else {
415: if (formname == 'portform') {
416: url += '&setroles='+extra_element;
417: }
418: }
1.230 raeburn 419: }
1.293 raeburn 420: if (multflag !=null && multflag != '') {
421: url += '&multiple='+multflag;
422: }
1.377 raeburn 423: if (crstype == 'Course/Group') {
424: if (formname == 'cu') {
425: crstype = document.cu.crstype.options[document.cu.crstype.selectedIndex].value;
426: if (crstype == "") {
427: alert("$crs_or_grp_alert");
428: return;
429: }
430: }
431: }
432: if (crstype !=null && crstype != '') {
433: url += '&type='+crstype;
434: }
1.102 www 435: var title = 'Course_Browser';
1.91 www 436: var options = 'scrollbars=1,resizable=1,menubar=0';
437: options += ',width=700,height=600';
438: stdeditbrowser = open(url,title,options,'1');
439: stdeditbrowser.focus();
440: }
1.468 raeburn 441:
442: function getFormIdByName(formname) {
443: for (var i=0;i<document.forms.length;i++) {
444: if (document.forms[i].name == formname) {
445: return i;
446: }
447: }
448: return -1;
449: }
450:
451: function getIndexByName(formid,item) {
452: for (var i=0;i<document.forms[formid].elements.length;i++) {
453: if (document.forms[formid].elements[i].name == item) {
454: return i;
455: }
456: }
457: return -1;
458: }
1.91 www 459: ENDSTDBRW
1.468 raeburn 460: if ($sec_element ne '') {
461: $output .= &setsec_javascript($sec_element,$formname);
462: }
463: $output .= '
464: </script>';
465: return $output;
466: }
467:
468: sub setsec_javascript {
469: my ($sec_element,$formname) = @_;
470: my $setsections = qq|
471: function setSect(sectionlist) {
472: var sectionsArray = sectionlist.split(",");
473: var numSections = sectionsArray.length;
474: document.$formname.$sec_element.length = 0;
475: if (numSections == 0) {
476: document.$formname.$sec_element.multiple=false;
477: document.$formname.$sec_element.size=1;
478: document.$formname.$sec_element.options[0] = new Option('No existing sections','',false,false)
479: } else {
480: if (numSections == 1) {
481: document.$formname.$sec_element.multiple=false;
482: document.$formname.$sec_element.size=1;
483: document.$formname.$sec_element.options[0] = new Option('Select','',true,true);
484: document.$formname.$sec_element.options[1] = new Option('No section','',false,false)
485: document.$formname.$sec_element.options[2] = new Option(sectionsArray[0],sectionsArray[0],false,false);
486: } else {
487: for (var i=0; i<numSections; i++) {
488: document.$formname.$sec_element.options[i] = new Option(sectionsArray[i],sectionsArray[i],false,false)
489: }
490: document.$formname.$sec_element.multiple=true
491: if (numSections < 3) {
492: document.$formname.$sec_element.size=numSections;
493: } else {
494: document.$formname.$sec_element.size=3;
495: }
496: document.$formname.$sec_element.options[0].selected = false
497: }
498: }
1.91 www 499: }
1.468 raeburn 500: |;
501: return $setsections;
502: }
503:
1.91 www 504:
505: sub selectcourse_link {
1.377 raeburn 506: my ($form,$unameele,$udomele,$desc,$extra_element,$multflag,$selecttype)=@_;
1.492 albertel 507: return "<a href='".'javascript:opencrsbrowser("'.$form.'","'.$unameele.
508: '","'.$udomele.'","'.$desc.'","'.$extra_element.'","'.$multflag.'","'.$selecttype.'");'."'>".&mt('Select Course')."</a>";
1.74 www 509: }
1.42 matthew 510:
1.273 raeburn 511: sub check_uncheck_jscript {
512: my $jscript = <<"ENDSCRT";
513: function checkAll(field) {
514: if (field.length > 0) {
515: for (i = 0; i < field.length; i++) {
516: field[i].checked = true ;
517: }
518: } else {
519: field.checked = true
520: }
521: }
522:
523: function uncheckAll(field) {
524: if (field.length > 0) {
525: for (i = 0; i < field.length; i++) {
526: field[i].checked = false ;
1.543 albertel 527: }
528: } else {
1.273 raeburn 529: field.checked = false ;
530: }
531: }
532: ENDSCRT
533: return $jscript;
534: }
535:
536:
1.42 matthew 537: =pod
1.36 matthew 538:
1.112 bowersj2 539: =item * linked_select_forms(...)
1.36 matthew 540:
541: linked_select_forms returns a string containing a <script></script> block
542: and html for two <select> menus. The select menus will be linked in that
543: changing the value of the first menu will result in new values being placed
544: in the second menu. The values in the select menu will appear in alphabetical
545: order.
546:
547: linked_select_forms takes the following ordered inputs:
548:
549: =over 4
550:
1.112 bowersj2 551: =item * $formname, the name of the <form> tag
1.36 matthew 552:
1.112 bowersj2 553: =item * $middletext, the text which appears between the <select> tags
1.36 matthew 554:
1.112 bowersj2 555: =item * $firstdefault, the default value for the first menu
1.36 matthew 556:
1.112 bowersj2 557: =item * $firstselectname, the name of the first <select> tag
1.36 matthew 558:
1.112 bowersj2 559: =item * $secondselectname, the name of the second <select> tag
1.36 matthew 560:
1.112 bowersj2 561: =item * $hashref, a reference to a hash containing the data for the menus.
1.36 matthew 562:
1.41 ng 563: =back
564:
1.36 matthew 565: Below is an example of such a hash. Only the 'text', 'default', and
566: 'select2' keys must appear as stated. keys(%menu) are the possible
567: values for the first select menu. The text that coincides with the
1.41 ng 568: first menu value is given in $menu{$choice1}->{'text'}. The values
1.36 matthew 569: and text for the second menu are given in the hash pointed to by
570: $menu{$choice1}->{'select2'}.
571:
1.112 bowersj2 572: my %menu = ( A1 => { text =>"Choice A1" ,
573: default => "B3",
574: select2 => {
575: B1 => "Choice B1",
576: B2 => "Choice B2",
577: B3 => "Choice B3",
578: B4 => "Choice B4"
579: }
580: },
581: A2 => { text =>"Choice A2" ,
582: default => "C2",
583: select2 => {
584: C1 => "Choice C1",
585: C2 => "Choice C2",
586: C3 => "Choice C3"
587: }
588: },
589: A3 => { text =>"Choice A3" ,
590: default => "D6",
591: select2 => {
592: D1 => "Choice D1",
593: D2 => "Choice D2",
594: D3 => "Choice D3",
595: D4 => "Choice D4",
596: D5 => "Choice D5",
597: D6 => "Choice D6",
598: D7 => "Choice D7"
599: }
600: }
601: );
1.36 matthew 602:
603: =cut
604:
605: sub linked_select_forms {
606: my ($formname,
607: $middletext,
608: $firstdefault,
609: $firstselectname,
610: $secondselectname,
611: $hashref
612: ) = @_;
613: my $second = "document.$formname.$secondselectname";
614: my $first = "document.$formname.$firstselectname";
615: # output the javascript to do the changing
616: my $result = '';
1.219 albertel 617: $result.="<script type=\"text/javascript\">\n";
1.36 matthew 618: $result.="var select2data = new Object();\n";
619: $" = '","';
620: my $debug = '';
621: foreach my $s1 (sort(keys(%$hashref))) {
622: $result.="select2data.d_$s1 = new Object();\n";
623: $result.="select2data.d_$s1.def = new String('".
624: $hashref->{$s1}->{'default'}."');\n";
625: $result.="select2data.d_$s1.values = new Array(";
626: my @s2values = sort(keys( %{ $hashref->{$s1}->{'select2'} } ));
627: $result.="\"@s2values\");\n";
628: $result.="select2data.d_$s1.texts = new Array(";
629: my @s2texts;
630: foreach my $value (@s2values) {
631: push @s2texts, $hashref->{$s1}->{'select2'}->{$value};
632: }
633: $result.="\"@s2texts\");\n";
634: }
635: $"=' ';
636: $result.= <<"END";
637:
638: function select1_changed() {
639: // Determine new choice
640: var newvalue = "d_" + $first.value;
641: // update select2
642: var values = select2data[newvalue].values;
643: var texts = select2data[newvalue].texts;
644: var select2def = select2data[newvalue].def;
645: var i;
646: // out with the old
647: for (i = 0; i < $second.options.length; i++) {
648: $second.options[i] = null;
649: }
650: // in with the nuclear
651: for (i=0;i<values.length; i++) {
652: $second.options[i] = new Option(values[i]);
1.143 matthew 653: $second.options[i].value = values[i];
1.36 matthew 654: $second.options[i].text = texts[i];
655: if (values[i] == select2def) {
656: $second.options[i].selected = true;
657: }
658: }
659: }
660: </script>
661: END
662: # output the initial values for the selection lists
663: $result .= "<select size=\"1\" name=\"$firstselectname\" onchange=\"select1_changed()\">\n";
664: foreach my $value (sort(keys(%$hashref))) {
665: $result.=" <option value=\"$value\" ";
1.253 albertel 666: $result.=" selected=\"selected\" " if ($value eq $firstdefault);
1.119 www 667: $result.=">".&mt($hashref->{$value}->{'text'})."</option>\n";
1.36 matthew 668: }
669: $result .= "</select>\n";
670: my %select2 = %{$hashref->{$firstdefault}->{'select2'}};
671: $result .= $middletext;
672: $result .= "<select size=\"1\" name=\"$secondselectname\">\n";
673: my $seconddefault = $hashref->{$firstdefault}->{'default'};
674: foreach my $value (sort(keys(%select2))) {
675: $result.=" <option value=\"$value\" ";
1.253 albertel 676: $result.=" selected=\"selected\" " if ($value eq $seconddefault);
1.119 www 677: $result.=">".&mt($select2{$value})."</option>\n";
1.36 matthew 678: }
679: $result .= "</select>\n";
680: # return $debug;
681: return $result;
682: } # end of sub linked_select_forms {
683:
1.45 matthew 684: =pod
1.44 bowersj2 685:
1.112 bowersj2 686: =item * help_open_topic($topic, $text, $stayOnPage, $width, $height)
1.44 bowersj2 687:
1.112 bowersj2 688: Returns a string corresponding to an HTML link to the given help
689: $topic, where $topic corresponds to the name of a .tex file in
690: /home/httpd/html/adm/help/tex, with underscores replaced by
691: spaces.
692:
693: $text will optionally be linked to the same topic, allowing you to
694: link text in addition to the graphic. If you do not want to link
695: text, but wish to specify one of the later parameters, pass an
696: empty string.
697:
698: $stayOnPage is a value that will be interpreted as a boolean. If true,
699: the link will not open a new window. If false, the link will open
700: a new window using Javascript. (Default is false.)
701:
702: $width and $height are optional numerical parameters that will
703: override the width and height of the popped up window, which may
704: be useful for certain help topics with big pictures included.
1.44 bowersj2 705:
706: =cut
707:
708: sub help_open_topic {
1.48 bowersj2 709: my ($topic, $text, $stayOnPage, $width, $height) = @_;
710: $text = "" if (not defined $text);
1.44 bowersj2 711: $stayOnPage = 0 if (not defined $stayOnPage);
1.258 albertel 712: if ($env{'browser.interface'} eq 'textual' ||
713: $env{'environment.remote'} eq 'off' ) {
1.79 www 714: $stayOnPage=1;
715: }
1.44 bowersj2 716: $width = 350 if (not defined $width);
717: $height = 400 if (not defined $height);
718: my $filename = $topic;
719: $filename =~ s/ /_/g;
720:
1.48 bowersj2 721: my $template = "";
722: my $link;
1.159 www 723:
724: $topic=~s/\W/\_/g;
1.44 bowersj2 725:
726: if (!$stayOnPage)
727: {
1.72 bowersj2 728: $link = "javascript:void(open('/adm/help/${filename}.hlp', 'Help_for_$topic', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
1.44 bowersj2 729: }
730: else
731: {
1.48 bowersj2 732: $link = "/adm/help/${filename}.hlp";
733: }
734:
735: # Add the text
736: if ($text ne "")
737: {
1.77 www 738: $template .=
739: "<table bgcolor='#3333AA' cellspacing='1' cellpadding='1' border='0'><tr>".
1.436 albertel 740: "<td bgcolor='#5555FF'><a target=\"_top\" href=\"$link\"><font color='#FFFFFF' size='2'>$text</font></a>";
1.48 bowersj2 741: }
742:
743: # Add the graphic
1.179 matthew 744: my $title = &mt('Online Help');
1.215 albertel 745: my $helpicon=&lonhttpdurl("/adm/help/gif/smallHelp.gif");
1.48 bowersj2 746: $template .= <<"ENDTEMPLATE";
1.436 albertel 747: <a target="_top" href="$link" title="$title"><img src="$helpicon" border="0" alt="(Help: $topic)" /></a>
1.44 bowersj2 748: ENDTEMPLATE
1.78 www 749: if ($text ne '') { $template.='</td></tr></table>' };
1.44 bowersj2 750: return $template;
751:
1.106 bowersj2 752: }
753:
754: # This is a quicky function for Latex cheatsheet editing, since it
755: # appears in at least four places
756: sub helpLatexCheatsheet {
757: my $other = shift;
758: my $addOther = '';
759: if ($other) {
760: $addOther = Apache::loncommon::help_open_topic($other, shift,
761: undef, undef, 600) .
762: '</td><td>';
763: }
764: return '<table><tr><td>'.
765: $addOther .
766: &Apache::loncommon::help_open_topic("Greek_Symbols",'Greek Symbols',
767: undef,undef,600)
768: .'</td><td>'.
769: &Apache::loncommon::help_open_topic("Other_Symbols",'Other Symbols',
770: undef,undef,600)
771: .'</td></tr></table>';
1.172 www 772: }
773:
1.430 albertel 774: sub general_help {
775: my $helptopic='Student_Intro';
776: if ($env{'request.role'}=~/^(ca|au)/) {
777: $helptopic='Authoring_Intro';
778: } elsif ($env{'request.role'}=~/^cc/) {
779: $helptopic='Course_Coordination_Intro';
780: }
781: return $helptopic;
782: }
783:
784: sub update_help_link {
785: my ($topic,$component_help,$faq,$bug,$stayOnPage) = @_;
786: my $origurl = $ENV{'REQUEST_URI'};
787: $origurl=~s|^/~|/priv/|;
788: my $timestamp = time;
789: foreach my $datum (\$topic,\$component_help,\$faq,\$bug,\$origurl) {
790: $$datum = &escape($$datum);
791: }
792:
793: my $banner_link = "/adm/helpmenu?page=banner&topic=$topic&component_help=$component_help&faq=$faq&bug=$bug&origurl=$origurl&stamp=$timestamp&stayonpage=$stayOnPage";
794: my $output .= <<"ENDOUTPUT";
795: <script type="text/javascript">
796: banner_link = '$banner_link';
797: </script>
798: ENDOUTPUT
799: return $output;
800: }
801:
802: # now just updates the help link and generates a blue icon
1.193 raeburn 803: sub help_open_menu {
1.430 albertel 804: my ($topic,$component_help,$faq,$bug,$stayOnPage,$width,$height,$text)
805: = @_;
806:
807: $stayOnPage = 0 if (not defined $stayOnPage);
808: if ($env{'browser.interface'} eq 'textual' ||
809: $env{'environment.remote'} eq 'off' ) {
810: $stayOnPage=1;
811: }
812: my $output;
813: if ($component_help) {
814: if (!$text) {
815: $output=&help_open_topic($component_help,undef,$stayOnPage,
816: $width,$height);
817: } else {
818: my $help_text;
819: $help_text=&unescape($topic);
820: $output='<table><tr><td>'.
821: &help_open_topic($component_help,$help_text,$stayOnPage,
822: $width,$height).'</td></tr></table>';
823: }
824: }
825: my $banner_link = &update_help_link($topic,$component_help,$faq,$bug,$stayOnPage);
826: return $output.$banner_link;
827: }
828:
829: sub top_nav_help {
830: my ($text) = @_;
831:
1.436 albertel 832: $text = &mt($text);
833:
834: my $stayOnPage =
835: ($env{'browser.interface'} eq 'textual' ||
836: $env{'environment.remote'} eq 'off' );
837: my $link= ($stayOnPage) ? "javascript:helpMenu('display')"
838: : "javascript:helpMenu('open')";
839: my $banner_link = &update_help_link(undef,undef,undef,undef,$stayOnPage);
840:
1.201 raeburn 841: my $title = &mt('Get help');
1.436 albertel 842:
843: return <<"END";
844: $banner_link
845: <a href="$link" title="$title">$text</a>
846: END
847: }
848:
849: sub help_menu_js {
850: my ($text) = @_;
851:
852: my $stayOnPage =
853: ($env{'browser.interface'} eq 'textual' ||
854: $env{'environment.remote'} eq 'off' );
855:
856: my $width = 620;
857: my $height = 600;
1.430 albertel 858: my $helptopic=&general_help();
859: my $details_link = '/adm/help/'.$helptopic.'.hlp';
1.261 albertel 860: my $nothing=&Apache::lonhtmlcommon::javascript_nothing();
1.331 albertel 861: my $start_page =
862: &Apache::loncommon::start_page('Help Menu', undef,
863: {'frameset' => 1,
864: 'js_ready' => 1,
865: 'add_entries' => {
866: 'border' => '0',
867: 'rows' => "105,*",},});
868: my $end_page =
869: &Apache::loncommon::end_page({'frameset' => 1,
870: 'js_ready' => 1,});
871:
1.436 albertel 872: my $template .= <<"ENDTEMPLATE";
873: <script type="text/javascript">
1.253 albertel 874: // <!-- BEGIN LON-CAPA Internal
875: // <![CDATA[
1.430 albertel 876: var banner_link = '';
1.243 raeburn 877: function helpMenu(target) {
878: var caller = this;
879: if (target == 'open') {
880: var newWindow = null;
881: try {
1.262 albertel 882: newWindow = window.open($nothing,"helpmenu","HEIGHT=$height,WIDTH=$width,resizable=yes,scrollbars=yes" )
1.243 raeburn 883: }
884: catch(error) {
885: writeHelp(caller);
886: return;
887: }
888: if (newWindow) {
889: caller = newWindow;
890: }
1.193 raeburn 891: }
1.243 raeburn 892: writeHelp(caller);
893: return;
894: }
895: function writeHelp(caller) {
1.430 albertel 896: caller.document.writeln('$start_page<frame name="bannerframe" src="'+banner_link+'" /><frame name="bodyframe" src="$details_link" /> $end_page')
1.243 raeburn 897: caller.document.close()
898: caller.focus()
1.193 raeburn 899: }
1.253 albertel 900: // ]]>
1.219 albertel 901: // END LON-CAPA Internal -->
1.436 albertel 902: </script>
1.193 raeburn 903: ENDTEMPLATE
904: return $template;
905: }
906:
1.172 www 907: sub help_open_bug {
908: my ($topic, $text, $stayOnPage, $width, $height) = @_;
1.258 albertel 909: unless ($env{'user.adv'}) { return ''; }
1.172 www 910: unless ($Apache::lonnet::perlvar{'BugzillaHost'}) { return ''; }
911: $text = "" if (not defined $text);
912: $stayOnPage = 0 if (not defined $stayOnPage);
1.258 albertel 913: if ($env{'browser.interface'} eq 'textual' ||
914: $env{'environment.remote'} eq 'off' ) {
1.172 www 915: $stayOnPage=1;
916: }
1.184 albertel 917: $width = 600 if (not defined $width);
918: $height = 600 if (not defined $height);
1.172 www 919:
920: $topic=~s/\W+/\+/g;
921: my $link='';
922: my $template='';
1.379 albertel 923: my $url=$Apache::lonnet::perlvar{'BugzillaHost'}.'enter_bug.cgi?product=LON-CAPA&bug_file_loc='.
924: &escape($ENV{'REQUEST_URI'}).'&component='.$topic;
1.172 www 925: if (!$stayOnPage)
926: {
927: $link = "javascript:void(open('$url', 'Bugzilla', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
928: }
929: else
930: {
931: $link = $url;
932: }
933: # Add the text
934: if ($text ne "")
935: {
936: $template .=
937: "<table bgcolor='#AA3333' cellspacing='1' cellpadding='1' border='0'><tr>".
1.436 albertel 938: "<td bgcolor='#FF5555'><a target=\"_top\" href=\"$link\"><font color='#FFFFFF' size='2'>$text</font></a>";
1.172 www 939: }
940:
941: # Add the graphic
1.179 matthew 942: my $title = &mt('Report a Bug');
1.215 albertel 943: my $bugicon=&lonhttpdurl("/adm/lonMisc/smallBug.gif");
1.172 www 944: $template .= <<"ENDTEMPLATE";
1.436 albertel 945: <a target="_top" href="$link" title="$title"><img src="$bugicon" border="0" alt="(Bug: $topic)" /></a>
1.172 www 946: ENDTEMPLATE
947: if ($text ne '') { $template.='</td></tr></table>' };
948: return $template;
949:
950: }
951:
952: sub help_open_faq {
953: my ($topic, $text, $stayOnPage, $width, $height) = @_;
1.258 albertel 954: unless ($env{'user.adv'}) { return ''; }
1.172 www 955: unless ($Apache::lonnet::perlvar{'FAQHost'}) { return ''; }
956: $text = "" if (not defined $text);
957: $stayOnPage = 0 if (not defined $stayOnPage);
1.258 albertel 958: if ($env{'browser.interface'} eq 'textual' ||
959: $env{'environment.remote'} eq 'off' ) {
1.172 www 960: $stayOnPage=1;
961: }
962: $width = 350 if (not defined $width);
963: $height = 400 if (not defined $height);
964:
965: $topic=~s/\W+/\+/g;
966: my $link='';
967: my $template='';
968: my $url=$Apache::lonnet::perlvar{'FAQHost'}.'/fom/cache/'.$topic.'.html';
969: if (!$stayOnPage)
970: {
971: $link = "javascript:void(open('$url', 'FAQ-O-Matic', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
972: }
973: else
974: {
975: $link = $url;
976: }
977:
978: # Add the text
979: if ($text ne "")
980: {
981: $template .=
1.173 www 982: "<table bgcolor='#337733' cellspacing='1' cellpadding='1' border='0'><tr>".
1.436 albertel 983: "<td bgcolor='#448844'><a target=\"_top\" href=\"$link\"><font color='#FFFFFF' size='2'>$text</font></a>";
1.172 www 984: }
985:
986: # Add the graphic
1.179 matthew 987: my $title = &mt('View the FAQ');
1.215 albertel 988: my $faqicon=&lonhttpdurl("/adm/lonMisc/smallFAQ.gif");
1.172 www 989: $template .= <<"ENDTEMPLATE";
1.436 albertel 990: <a target="_top" href="$link" title="$title"><img src="$faqicon" border="0" alt="(FAQ: $topic)" /></a>
1.172 www 991: ENDTEMPLATE
992: if ($text ne '') { $template.='</td></tr></table>' };
993: return $template;
994:
1.44 bowersj2 995: }
1.37 matthew 996:
1.180 matthew 997: ###############################################################
998: ###############################################################
999:
1.45 matthew 1000: =pod
1001:
1.256 matthew 1002: =item * change_content_javascript():
1003:
1004: This and the next function allow you to create small sections of an
1005: otherwise static HTML page that you can update on the fly with
1006: Javascript, even in Netscape 4.
1007:
1008: The Javascript fragment returned by this function (no E<lt>scriptE<gt> tag)
1009: must be written to the HTML page once. It will prove the Javascript
1010: function "change(name, content)". Calling the change function with the
1011: name of the section
1012: you want to update, matching the name passed to C<changable_area>, and
1013: the new content you want to put in there, will put the content into
1014: that area.
1015:
1016: B<Note>: Netscape 4 only reserves enough space for the changable area
1017: to contain room for the original contents. You need to "make space"
1018: for whatever changes you wish to make, and be B<sure> to check your
1019: code in Netscape 4. This feature in Netscape 4 is B<not> powerful;
1020: it's adequate for updating a one-line status display, but little more.
1021: This script will set the space to 100% width, so you only need to
1022: worry about height in Netscape 4.
1023:
1024: Modern browsers are much less limiting, and if you can commit to the
1025: user not using Netscape 4, this feature may be used freely with
1026: pretty much any HTML.
1027:
1028: =cut
1029:
1030: sub change_content_javascript {
1031: # If we're on Netscape 4, we need to use Layer-based code
1.258 albertel 1032: if ($env{'browser.type'} eq 'netscape' &&
1033: $env{'browser.version'} =~ /^4\./) {
1.256 matthew 1034: return (<<NETSCAPE4);
1035: function change(name, content) {
1036: doc = document.layers[name+"___escape"].layers[0].document;
1037: doc.open();
1038: doc.write(content);
1039: doc.close();
1040: }
1041: NETSCAPE4
1042: } else {
1043: # Otherwise, we need to use semi-standards-compliant code
1044: # (technically, "innerHTML" isn't standard but the equivalent
1045: # is really scary, and every useful browser supports it
1046: return (<<DOMBASED);
1047: function change(name, content) {
1048: element = document.getElementById(name);
1049: element.innerHTML = content;
1050: }
1051: DOMBASED
1052: }
1053: }
1054:
1055: =pod
1056:
1057: =item * changable_area($name, $origContent):
1058:
1059: This provides a "changable area" that can be modified on the fly via
1060: the Javascript code provided in C<change_content_javascript>. $name is
1061: the name you will use to reference the area later; do not repeat the
1062: same name on a given HTML page more then once. $origContent is what
1063: the area will originally contain, which can be left blank.
1064:
1065: =cut
1066:
1067: sub changable_area {
1068: my ($name, $origContent) = @_;
1069:
1.258 albertel 1070: if ($env{'browser.type'} eq 'netscape' &&
1071: $env{'browser.version'} =~ /^4\./) {
1.256 matthew 1072: # If this is netscape 4, we need to use the Layer tag
1073: return "<ilayer width='100%' id='${name}___escape' overflow='none'><layer width='100%' id='$name' overflow='none'>$origContent</layer></ilayer>";
1074: } else {
1075: return "<span id='$name'>$origContent</span>";
1076: }
1077: }
1078:
1079: =pod
1080:
1081: =back
1.542 raeburn 1082:
1.256 matthew 1083: =head1 Excel and CSV file utility routines
1084:
1085: =over 4
1086:
1087: =cut
1088:
1089: ###############################################################
1090: ###############################################################
1091:
1092: =pod
1093:
1.112 bowersj2 1094: =item * csv_translate($text)
1.37 matthew 1095:
1.185 www 1096: Translate $text to allow it to be output as a 'comma separated values'
1.37 matthew 1097: format.
1098:
1099: =cut
1100:
1.180 matthew 1101: ###############################################################
1102: ###############################################################
1.37 matthew 1103: sub csv_translate {
1104: my $text = shift;
1105: $text =~ s/\"/\"\"/g;
1.209 albertel 1106: $text =~ s/\n/ /g;
1.37 matthew 1107: return $text;
1108: }
1.180 matthew 1109:
1110: ###############################################################
1111: ###############################################################
1112:
1113: =pod
1114:
1115: =item * define_excel_formats
1116:
1117: Define some commonly used Excel cell formats.
1118:
1119: Currently supported formats:
1120:
1121: =over 4
1122:
1123: =item header
1124:
1125: =item bold
1126:
1127: =item h1
1128:
1129: =item h2
1130:
1131: =item h3
1132:
1.256 matthew 1133: =item h4
1134:
1135: =item i
1136:
1.180 matthew 1137: =item date
1138:
1139: =back
1140:
1141: Inputs: $workbook
1142:
1143: Returns: $format, a hash reference.
1144:
1145: =cut
1146:
1147: ###############################################################
1148: ###############################################################
1149: sub define_excel_formats {
1150: my ($workbook) = @_;
1151: my $format;
1152: $format->{'header'} = $workbook->add_format(bold => 1,
1153: bottom => 1,
1154: align => 'center');
1155: $format->{'bold'} = $workbook->add_format(bold=>1);
1156: $format->{'h1'} = $workbook->add_format(bold=>1, size=>18);
1157: $format->{'h2'} = $workbook->add_format(bold=>1, size=>16);
1158: $format->{'h3'} = $workbook->add_format(bold=>1, size=>14);
1.255 matthew 1159: $format->{'h4'} = $workbook->add_format(bold=>1, size=>12);
1.246 matthew 1160: $format->{'i'} = $workbook->add_format(italic=>1);
1.180 matthew 1161: $format->{'date'} = $workbook->add_format(num_format=>
1.207 matthew 1162: 'mm/dd/yyyy hh:mm:ss');
1.180 matthew 1163: return $format;
1164: }
1165:
1166: ###############################################################
1167: ###############################################################
1.113 bowersj2 1168:
1169: =pod
1170:
1.256 matthew 1171: =item * create_workbook
1.255 matthew 1172:
1173: Create an Excel worksheet. If it fails, output message on the
1174: request object and return undefs.
1175:
1176: Inputs: Apache request object
1177:
1178: Returns (undef) on failure,
1179: Excel worksheet object, scalar with filename, and formats
1180: from &Apache::loncommon::define_excel_formats on success
1181:
1182: =cut
1183:
1184: ###############################################################
1185: ###############################################################
1186: sub create_workbook {
1187: my ($r) = @_;
1188: #
1189: # Create the excel spreadsheet
1190: my $filename = '/prtspool/'.
1.258 albertel 1191: $env{'user.name'}.'_'.$env{'user.domain'}.'_'.
1.255 matthew 1192: time.'_'.rand(1000000000).'.xls';
1193: my $workbook = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
1194: if (! defined($workbook)) {
1195: $r->log_error("Error creating excel spreadsheet $filename: $!");
1196: $r->print('<p>'.&mt("Unable to create new Excel file. ".
1197: "This error has been logged. ".
1198: "Please alert your LON-CAPA administrator").
1199: '</p>');
1200: return (undef);
1201: }
1202: #
1203: $workbook->set_tempdir('/home/httpd/perl/tmp');
1204: #
1205: my $format = &Apache::loncommon::define_excel_formats($workbook);
1206: return ($workbook,$filename,$format);
1207: }
1208:
1209: ###############################################################
1210: ###############################################################
1211:
1212: =pod
1213:
1.256 matthew 1214: =item * create_text_file
1.113 bowersj2 1215:
1.542 raeburn 1216: Create a file to write to and eventually make available to the user.
1.256 matthew 1217: If file creation fails, outputs an error message on the request object and
1218: return undefs.
1.113 bowersj2 1219:
1.256 matthew 1220: Inputs: Apache request object, and file suffix
1.113 bowersj2 1221:
1.256 matthew 1222: Returns (undef) on failure,
1223: Filehandle and filename on success.
1.113 bowersj2 1224:
1225: =cut
1226:
1.256 matthew 1227: ###############################################################
1228: ###############################################################
1229: sub create_text_file {
1230: my ($r,$suffix) = @_;
1231: if (! defined($suffix)) { $suffix = 'txt'; };
1232: my $fh;
1233: my $filename = '/prtspool/'.
1.258 albertel 1234: $env{'user.name'}.'_'.$env{'user.domain'}.'_'.
1.256 matthew 1235: time.'_'.rand(1000000000).'.'.$suffix;
1236: $fh = Apache::File->new('>/home/httpd'.$filename);
1237: if (! defined($fh)) {
1238: $r->log_error("Couldn't open $filename for output $!");
1239: $r->print("Problems occured in creating the output file. ".
1240: "This error has been logged. ".
1241: "Please alert your LON-CAPA administrator.");
1.113 bowersj2 1242: }
1.256 matthew 1243: return ($fh,$filename)
1.113 bowersj2 1244: }
1245:
1246:
1.256 matthew 1247: =pod
1.113 bowersj2 1248:
1249: =back
1250:
1251: =cut
1.37 matthew 1252:
1253: ###############################################################
1.33 matthew 1254: ## Home server <option> list generating code ##
1255: ###############################################################
1.35 matthew 1256:
1.169 www 1257: # ------------------------------------------
1258:
1259: sub domain_select {
1260: my ($name,$value,$multiple)=@_;
1261: my %domains=map {
1.514 albertel 1262: $_ => $_.' '. &Apache::lonnet::domain($_,'description')
1.512 albertel 1263: } &Apache::lonnet::all_domains();
1.169 www 1264: if ($multiple) {
1265: $domains{''}=&mt('Any domain');
1.550 ! albertel 1266: $domains{'select_form_order'} = [sort {lc($a) cmp lc($b) } (keys(%domains))];
1.287 albertel 1267: return &multiple_select_form($name,$value,4,\%domains);
1.169 www 1268: } else {
1.550 ! albertel 1269: $domains{'select_form_order'} = [sort {lc($a) cmp lc($b) } (keys(%domains))];
1.169 www 1270: return &select_form($name,$value,%domains);
1271: }
1272: }
1273:
1.282 albertel 1274: #-------------------------------------------
1275:
1276: =pod
1277:
1.519 raeburn 1278: =head1 Routines for form select boxes
1279:
1280: =over 4
1281:
1282: =cut
1283:
1.287 albertel 1284: =item * multiple_select_form($name,$value,$size,$hash,$order)
1.282 albertel 1285:
1286: Returns a string containing a <select> element int multiple mode
1287:
1288:
1289: Args:
1290: $name - name of the <select> element
1.506 raeburn 1291: $value - scalar or array ref of values that should already be selected
1.282 albertel 1292: $size - number of rows long the select element is
1.283 albertel 1293: $hash - the elements should be 'option' => 'shown text'
1.282 albertel 1294: (shown text should already have been &mt())
1.506 raeburn 1295: $order - (optional) array ref of the order to show the elements in
1.283 albertel 1296:
1.282 albertel 1297: =cut
1298:
1299: #-------------------------------------------
1.169 www 1300: sub multiple_select_form {
1.284 albertel 1301: my ($name,$value,$size,$hash,$order)=@_;
1.169 www 1302: my %selected = map { $_ => 1 } ref($value)?@{$value}:($value);
1303: my $output='';
1.191 matthew 1304: if (! defined($size)) {
1305: $size = 4;
1.283 albertel 1306: if (scalar(keys(%$hash))<4) {
1307: $size = scalar(keys(%$hash));
1.191 matthew 1308: }
1309: }
1.169 www 1310: $output.="\n<select name='$name' size='$size' multiple='1'>";
1.501 banghart 1311: my @order;
1.506 raeburn 1312: if (ref($order) eq 'ARRAY') {
1313: @order = @{$order};
1314: } else {
1315: @order = sort(keys(%$hash));
1.501 banghart 1316: }
1317: if (exists($$hash{'select_form_order'})) {
1318: @order = @{$$hash{'select_form_order'}};
1319: }
1320:
1.284 albertel 1321: foreach my $key (@order) {
1.356 albertel 1322: $output.='<option value="'.&HTML::Entities::encode($key,'"<>&').'" ';
1.284 albertel 1323: $output.='selected="selected" ' if ($selected{$key});
1324: $output.='>'.$hash->{$key}."</option>\n";
1.169 www 1325: }
1326: $output.="</select>\n";
1327: return $output;
1328: }
1329:
1.88 www 1330: #-------------------------------------------
1331:
1332: =pod
1333:
1.112 bowersj2 1334: =item * select_form($defdom,$name,%hash)
1.88 www 1335:
1336: Returns a string containing a <select name='$name' size='1'> form to
1337: allow a user to select options from a hash option_name => displayed text.
1338: See lonrights.pm for an example invocation and use.
1339:
1340: =cut
1341:
1342: #-------------------------------------------
1343: sub select_form {
1344: my ($def,$name,%hash) = @_;
1345: my $selectform = "<select name=\"$name\" size=\"1\">\n";
1.128 albertel 1346: my @keys;
1347: if (exists($hash{'select_form_order'})) {
1348: @keys=@{$hash{'select_form_order'}};
1349: } else {
1350: @keys=sort(keys(%hash));
1351: }
1.356 albertel 1352: foreach my $key (@keys) {
1353: $selectform.=
1354: '<option value="'.&HTML::Entities::encode($key,'"<>&').'" '.
1355: ($key eq $def ? 'selected="selected" ' : '').
1356: ">".&mt($hash{$key})."</option>\n";
1.88 www 1357: }
1358: $selectform.="</select>";
1359: return $selectform;
1360: }
1361:
1.475 www 1362: # For display filters
1363:
1364: sub display_filter {
1365: if (!$env{'form.show'}) { $env{'form.show'}=10; }
1.477 www 1366: if (!$env{'form.displayfilter'}) { $env{'form.displayfilter'}='currentfolder'; }
1.475 www 1367: return '<nobr><label>'.&mt('Records [_1]',
1368: &Apache::lonmeta::selectbox('show',$env{'form.show'},undef,
1369: (&mt('all'),10,20,50,100,1000,10000))).
1.478 www 1370: '</label></nobr> <nobr>'.
1.475 www 1371: &mt('Filter [_1]',
1.477 www 1372: &select_form($env{'form.displayfilter'},
1373: 'displayfilter',
1374: ('currentfolder' => 'Current folder/page',
1375: 'containing' => 'Containing phrase',
1376: 'none' => 'None'))).
1.478 www 1377: '<input type="text" name="containingphrase" size="30" value="'.&HTML::Entities::encode($env{'form.containingphrase'}).'" /></nobr>';
1.475 www 1378: }
1379:
1.167 www 1380: sub gradeleveldescription {
1381: my $gradelevel=shift;
1382: my %gradelevels=(0 => 'Not specified',
1383: 1 => 'Grade 1',
1384: 2 => 'Grade 2',
1385: 3 => 'Grade 3',
1386: 4 => 'Grade 4',
1387: 5 => 'Grade 5',
1388: 6 => 'Grade 6',
1389: 7 => 'Grade 7',
1390: 8 => 'Grade 8',
1391: 9 => 'Grade 9',
1392: 10 => 'Grade 10',
1393: 11 => 'Grade 11',
1394: 12 => 'Grade 12',
1395: 13 => 'Grade 13',
1396: 14 => '100 Level',
1397: 15 => '200 Level',
1398: 16 => '300 Level',
1399: 17 => '400 Level',
1400: 18 => 'Graduate Level');
1401: return &mt($gradelevels{$gradelevel});
1402: }
1403:
1.163 www 1404: sub select_level_form {
1405: my ($deflevel,$name)=@_;
1406: unless ($deflevel) { $deflevel=0; }
1.167 www 1407: my $selectform = "<select name=\"$name\" size=\"1\">\n";
1408: for (my $i=0; $i<=18; $i++) {
1409: $selectform.="<option value=\"$i\" ".
1.253 albertel 1410: ($i==$deflevel ? 'selected="selected" ' : '').
1.167 www 1411: ">".&gradeleveldescription($i)."</option>\n";
1412: }
1413: $selectform.="</select>";
1414: return $selectform;
1.163 www 1415: }
1.167 www 1416:
1.35 matthew 1417: #-------------------------------------------
1418:
1.45 matthew 1419: =pod
1420:
1.112 bowersj2 1421: =item * select_dom_form($defdom,$name,$includeempty)
1.35 matthew 1422:
1423: Returns a string containing a <select name='$name' size='1'> form to
1424: allow a user to select the domain to preform an operation in.
1425: See loncreateuser.pm for an example invocation and use.
1426:
1.90 www 1427: If the $includeempty flag is set, it also includes an empty choice ("no domain
1428: selected");
1429:
1.35 matthew 1430: =cut
1431:
1432: #-------------------------------------------
1.34 matthew 1433: sub select_dom_form {
1.90 www 1434: my ($defdom,$name,$includeempty) = @_;
1.550 ! albertel 1435: my @domains = sort {lc($a) cmp lc($b)} (&Apache::lonnet::all_domains());
1.90 www 1436: if ($includeempty) { @domains=('',@domains); }
1.34 matthew 1437: my $selectdomain = "<select name=\"$name\" size=\"1\">\n";
1.356 albertel 1438: foreach my $dom (@domains) {
1439: $selectdomain.="<option value=\"$dom\" ".
1440: ($dom eq $defdom ? 'selected="selected" ' : '').
1441: ">$dom</option>\n";
1.34 matthew 1442: }
1443: $selectdomain.="</select>";
1444: return $selectdomain;
1445: }
1446:
1.35 matthew 1447: #-------------------------------------------
1448:
1.45 matthew 1449: =pod
1450:
1.112 bowersj2 1451: =item * home_server_option_list($domain)
1.35 matthew 1452:
1453: returns a string which contains an <option> list to be used in a
1454: <select> form input. See loncreateuser.pm for an example.
1455:
1456: =cut
1457:
1458: #-------------------------------------------
1.33 matthew 1459: sub home_server_option_list {
1460: my $domain = shift;
1.513 albertel 1461: my %servers = &Apache::lonnet::get_servers($domain,'library');
1.33 matthew 1462: my $result = '';
1.356 albertel 1463: foreach my $hostid (sort(keys(%servers))) {
1.33 matthew 1464: $result.=
1.356 albertel 1465: '<option value="'.$hostid.'">'.
1466: $hostid.' '.$servers{$hostid}."</option>\n";
1.33 matthew 1467: }
1468: return $result;
1469: }
1.112 bowersj2 1470:
1471: =pod
1472:
1.534 albertel 1473: =back
1474:
1.112 bowersj2 1475: =cut
1.87 matthew 1476:
1477: ###############################################################
1.112 bowersj2 1478: ## Decoding User Agent ##
1.87 matthew 1479: ###############################################################
1480:
1481: =pod
1482:
1.112 bowersj2 1483: =head1 Decoding the User Agent
1484:
1485: =over 4
1486:
1487: =item * &decode_user_agent()
1.87 matthew 1488:
1489: Inputs: $r
1490:
1491: Outputs:
1492:
1493: =over 4
1494:
1.112 bowersj2 1495: =item * $httpbrowser
1.87 matthew 1496:
1.112 bowersj2 1497: =item * $clientbrowser
1.87 matthew 1498:
1.112 bowersj2 1499: =item * $clientversion
1.87 matthew 1500:
1.112 bowersj2 1501: =item * $clientmathml
1.87 matthew 1502:
1.112 bowersj2 1503: =item * $clientunicode
1.87 matthew 1504:
1.112 bowersj2 1505: =item * $clientos
1.87 matthew 1506:
1507: =back
1508:
1.157 matthew 1509: =back
1510:
1.87 matthew 1511: =cut
1512:
1513: ###############################################################
1514: ###############################################################
1515: sub decode_user_agent {
1.247 albertel 1516: my ($r)=@_;
1.87 matthew 1517: my @browsertype=split(/\&/,$Apache::lonnet::perlvar{"lonBrowsDet"});
1518: my %mathcap=split(/\&/,$$Apache::lonnet::perlvar{"lonMathML"});
1519: my $httpbrowser=$ENV{"HTTP_USER_AGENT"};
1.247 albertel 1520: if (!$httpbrowser && $r) { $httpbrowser=$r->header_in('User-Agent'); }
1.87 matthew 1521: my $clientbrowser='unknown';
1522: my $clientversion='0';
1523: my $clientmathml='';
1524: my $clientunicode='0';
1525: for (my $i=0;$i<=$#browsertype;$i++) {
1526: my ($bname,$match,$notmatch,$vreg,$minv,$univ)=split(/\:/,$browsertype[$i]);
1527: if (($httpbrowser=~/$match/i) && ($httpbrowser!~/$notmatch/i)) {
1528: $clientbrowser=$bname;
1529: $httpbrowser=~/$vreg/i;
1530: $clientversion=$1;
1531: $clientmathml=($clientversion>=$minv);
1532: $clientunicode=($clientversion>=$univ);
1533: }
1534: }
1535: my $clientos='unknown';
1536: if (($httpbrowser=~/linux/i) ||
1537: ($httpbrowser=~/unix/i) ||
1538: ($httpbrowser=~/ux/i) ||
1539: ($httpbrowser=~/solaris/i)) { $clientos='unix'; }
1540: if (($httpbrowser=~/vax/i) ||
1541: ($httpbrowser=~/vms/i)) { $clientos='vms'; }
1542: if ($httpbrowser=~/next/i) { $clientos='next'; }
1543: if (($httpbrowser=~/mac/i) ||
1544: ($httpbrowser=~/powerpc/i)) { $clientos='mac'; }
1545: if ($httpbrowser=~/win/i) { $clientos='win'; }
1546: if ($httpbrowser=~/embed/i) { $clientos='pda'; }
1547: return ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,
1548: $clientunicode,$clientos,);
1549: }
1550:
1.32 matthew 1551: ###############################################################
1552: ## Authentication changing form generation subroutines ##
1553: ###############################################################
1554: ##
1555: ## All of the authform_xxxxxxx subroutines take their inputs in a
1556: ## hash, and have reasonable default values.
1557: ##
1558: ## formname = the name given in the <form> tag.
1.35 matthew 1559: #-------------------------------------------
1560:
1.45 matthew 1561: =pod
1562:
1.112 bowersj2 1563: =head1 Authentication Routines
1564:
1565: =over 4
1566:
1567: =item * authform_xxxxxx
1.35 matthew 1568:
1569: The authform_xxxxxx subroutines provide javascript and html forms which
1570: handle some of the conveniences required for authentication forms.
1571: This is not an optimal method, but it works.
1572:
1573: See loncreateuser.pm for invocation and use examples.
1574:
1575: =over 4
1576:
1.112 bowersj2 1577: =item * authform_header
1.35 matthew 1578:
1.112 bowersj2 1579: =item * authform_authorwarning
1.35 matthew 1580:
1.112 bowersj2 1581: =item * authform_nochange
1.35 matthew 1582:
1.112 bowersj2 1583: =item * authform_kerberos
1.35 matthew 1584:
1.112 bowersj2 1585: =item * authform_internal
1.35 matthew 1586:
1.112 bowersj2 1587: =item * authform_filesystem
1.35 matthew 1588:
1589: =back
1590:
1.157 matthew 1591: =back
1592:
1.35 matthew 1593: =cut
1594:
1595: #-------------------------------------------
1.32 matthew 1596: sub authform_header{
1597: my %in = (
1598: formname => 'cu',
1.80 albertel 1599: kerb_def_dom => '',
1.32 matthew 1600: @_,
1601: );
1602: $in{'formname'} = 'document.' . $in{'formname'};
1603: my $result='';
1.80 albertel 1604:
1605: #---------------------------------------------- Code for upper case translation
1606: my $Javascript_toUpperCase;
1607: unless ($in{kerb_def_dom}) {
1608: $Javascript_toUpperCase =<<"END";
1609: switch (choice) {
1610: case 'krb': currentform.elements[choicearg].value =
1611: currentform.elements[choicearg].value.toUpperCase();
1612: break;
1613: default:
1614: }
1615: END
1616: } else {
1617: $Javascript_toUpperCase = "";
1618: }
1619:
1.165 raeburn 1620: my $radioval = "'nochange'";
1.174 matthew 1621: if (exists($in{'curr_authtype'}) &&
1622: defined($in{'curr_authtype'}) &&
1623: $in{'curr_authtype'} ne '') {
1624: $radioval = "'$in{'curr_authtype'}arg'";
1625: }
1.165 raeburn 1626: my $argfield = 'null';
1627: if ( grep/^mode$/,(keys %in) ) {
1628: if ($in{'mode'} eq 'modifycourse') {
1629: if ( grep/^curr_authtype$/,(keys %in) ) {
1630: $radioval = "'$in{'curr_authtype'}'";
1631: }
1632: if ( grep/^curr_autharg$/,(keys %in) ) {
1633: unless ($in{'curr_autharg'} eq '') {
1634: $argfield = "'$in{'curr_autharg'}'";
1635: }
1636: }
1637: }
1638: }
1639:
1.32 matthew 1640: $result.=<<"END";
1641: var current = new Object();
1.165 raeburn 1642: current.radiovalue = $radioval;
1643: current.argfield = $argfield;
1.32 matthew 1644:
1645: function changed_radio(choice,currentform) {
1646: var choicearg = choice + 'arg';
1647: // If a radio button in changed, we need to change the argfield
1648: if (current.radiovalue != choice) {
1649: current.radiovalue = choice;
1650: if (current.argfield != null) {
1651: currentform.elements[current.argfield].value = '';
1652: }
1653: if (choice == 'nochange') {
1654: current.argfield = null;
1655: } else {
1656: current.argfield = choicearg;
1657: switch(choice) {
1658: case 'krb':
1659: currentform.elements[current.argfield].value =
1660: "$in{'kerb_def_dom'}";
1661: break;
1662: default:
1663: break;
1664: }
1665: }
1666: }
1667: return;
1668: }
1.22 www 1669:
1.32 matthew 1670: function changed_text(choice,currentform) {
1671: var choicearg = choice + 'arg';
1672: if (currentform.elements[choicearg].value !='') {
1.80 albertel 1673: $Javascript_toUpperCase
1.32 matthew 1674: // clear old field
1675: if ((current.argfield != choicearg) && (current.argfield != null)) {
1676: currentform.elements[current.argfield].value = '';
1677: }
1678: current.argfield = choicearg;
1679: }
1680: set_auth_radio_buttons(choice,currentform);
1681: return;
1.20 www 1682: }
1.32 matthew 1683:
1684: function set_auth_radio_buttons(newvalue,currentform) {
1685: var i=0;
1686: while (i < currentform.login.length) {
1687: if (currentform.login[i].value == newvalue) { break; }
1688: i++;
1689: }
1690: if (i == currentform.login.length) {
1691: return;
1692: }
1693: current.radiovalue = newvalue;
1694: currentform.login[i].checked = true;
1695: return;
1696: }
1697: END
1698: return $result;
1699: }
1700:
1701: sub authform_authorwarning{
1702: my $result='';
1.144 matthew 1703: $result='<i>'.
1704: &mt('As a general rule, only authors or co-authors should be '.
1705: 'filesystem authenticated '.
1706: '(which allows access to the server filesystem).')."</i>\n";
1.32 matthew 1707: return $result;
1708: }
1709:
1710: sub authform_nochange{
1711: my %in = (
1712: formname => 'document.cu',
1713: kerb_def_dom => 'MSU.EDU',
1714: @_,
1715: );
1.281 albertel 1716: my $result = '<label>'.&mt('[_1] Do not change login data',
1.144 matthew 1717: '<input type="radio" name="login" value="nochange" '.
1718: 'checked="checked" onclick="'.
1.281 albertel 1719: "javascript:changed_radio('nochange',$in{'formname'});".'" />').
1720: '</label>';
1.32 matthew 1721: return $result;
1722: }
1723:
1724: sub authform_kerberos{
1725: my %in = (
1726: formname => 'document.cu',
1727: kerb_def_dom => 'MSU.EDU',
1.80 albertel 1728: kerb_def_auth => 'krb4',
1.32 matthew 1729: @_,
1730: );
1.165 raeburn 1731: my ($check4,$check5,$krbarg);
1.80 albertel 1732: if ($in{'kerb_def_auth'} eq 'krb5') {
1733: $check5 = " checked=\"on\"";
1734: } else {
1735: $check4 = " checked=\"on\"";
1736: }
1.165 raeburn 1737: $krbarg = $in{'kerb_def_dom'};
1738:
1739: my $krbcheck = "";
1740: if ( grep/^curr_authtype$/,(keys %in) ) {
1741: if ($in{'curr_authtype'} =~ m/^krb/) {
1742: $krbcheck = " checked=\"on\"";
1743: if ( grep/^curr_autharg$/,(keys %in) ) {
1744: $krbarg = $in{'curr_autharg'};
1745: }
1746: }
1747: }
1748:
1.144 matthew 1749: my $jscall = "javascript:changed_radio('krb',$in{'formname'});";
1750: my $result .= &mt
1751: ('[_1] Kerberos authenticated with domain [_2] '.
1.281 albertel 1752: '[_3] Version 4 [_4] Version 5 [_5]',
1753: '<label><input type="radio" name="login" value="krb" '.
1.165 raeburn 1754: 'onclick="'.$jscall.'" onchange="'.$jscall.'"'.$krbcheck.' />',
1.281 albertel 1755: '</label><input type="text" size="10" name="krbarg" '.
1.165 raeburn 1756: 'value="'.$krbarg.'" '.
1.144 matthew 1757: 'onchange="'.$jscall.'" />',
1.281 albertel 1758: '<label><input type="radio" name="krbver" value="4" '.$check4.' />',
1759: '</label><label><input type="radio" name="krbver" value="5" '.$check5.' />',
1760: '</label>');
1.32 matthew 1761: return $result;
1762: }
1763:
1764: sub authform_internal{
1765: my %args = (
1766: formname => 'document.cu',
1767: kerb_def_dom => 'MSU.EDU',
1768: @_,
1769: );
1.165 raeburn 1770:
1771: my $intcheck = "";
1772: my $intarg = 'value=""';
1773: if ( grep/^curr_authtype$/,(keys %args) ) {
1774: if ($args{'curr_authtype'} eq 'int') {
1775: $intcheck = " checked=\"on\"";
1776: if ( grep/^curr_autharg$/,(keys %args) ) {
1777: $intarg = "value=\"$args{'curr_autharg'}\"";
1778: }
1779: }
1780: }
1781:
1.144 matthew 1782: my $jscall = "javascript:changed_radio('int',$args{'formname'});";
1783: my $result.=&mt
1784: ('[_1] Internally authenticated (with initial password [_2])',
1.281 albertel 1785: '<label><input type="radio" name="login" value="int" '.$intcheck.
1.165 raeburn 1786: ' onchange="'.$jscall.'" onclick="'.$jscall.'" />',
1.281 albertel 1787: '</label><input type="text" size="10" name="intarg" '.$intarg.
1.165 raeburn 1788: ' onchange="'.$jscall.'" />');
1.32 matthew 1789: return $result;
1790: }
1791:
1792: sub authform_local{
1793: my %in = (
1794: formname => 'document.cu',
1795: kerb_def_dom => 'MSU.EDU',
1796: @_,
1797: );
1.165 raeburn 1798:
1799: my $loccheck = "";
1800: my $locarg = 'value=""';
1801: if ( grep/^curr_authtype$/,(keys %in) ) {
1802: if ($in{'curr_authtype'} eq 'loc') {
1803: $loccheck = " checked=\"on\"";
1804: if ( grep/^curr_autharg$/,(keys %in) ) {
1805: $locarg = "value=\"$in{'curr_autharg'}\"";
1806: }
1807: }
1808: }
1809:
1.144 matthew 1810: my $jscall = "javascript:changed_radio('loc',$in{'formname'});";
1.160 matthew 1811: my $result.=&mt('[_1] Local Authentication with argument [_2]',
1.281 albertel 1812: '<label><input type="radio" name="login" value="loc" '.$loccheck.
1.165 raeburn 1813: ' onchange="'.$jscall.'" onclick="'.$jscall.'" />',
1.281 albertel 1814: '</label><input type="text" size="10" name="locarg" '.$locarg.
1.165 raeburn 1815: ' onchange="'.$jscall.'" />');
1.32 matthew 1816: return $result;
1817: }
1818:
1819: sub authform_filesystem{
1820: my %in = (
1821: formname => 'document.cu',
1822: kerb_def_dom => 'MSU.EDU',
1823: @_,
1824: );
1.144 matthew 1825: my $jscall = "javascript:changed_radio('fsys',$in{'formname'});";
1826: my $result.= &mt
1827: ('[_1] Filesystem Authenticated (with initial password [_2])',
1.281 albertel 1828: '<label><input type="radio" name="login" value="fsys" '.
1.144 matthew 1829: 'onchange="'.$jscall.'" onclick="'.$jscall.'" />',
1.281 albertel 1830: '</label><input type="text" size="10" name="fsysarg" value="" '.
1.144 matthew 1831: 'onchange="'.$jscall.'" />');
1.32 matthew 1832: return $result;
1833: }
1834:
1.80 albertel 1835: ###############################################################
1836: ## Get Authentication Defaults for Domain ##
1837: ###############################################################
1838:
1839: =pod
1840:
1.112 bowersj2 1841: =head1 Domains and Authentication
1842:
1843: Returns default authentication type and an associated argument as
1844: listed in file 'domain.tab'.
1845:
1846: =over 4
1847:
1848: =item * get_auth_defaults
1.80 albertel 1849:
1850: get_auth_defaults($target_domain) returns the default authentication
1851: type and an associated argument (initial password or a kerberos domain).
1852: These values are stored in lonTabs/domain.tab
1853:
1854: ($def_auth, $def_arg) = &get_auth_defaults($target_domain);
1855:
1856: If target_domain is not found in domain.tab, returns nothing ('').
1857:
1858: =cut
1859:
1860: #-------------------------------------------
1861: sub get_auth_defaults {
1862: my $domain=shift;
1.514 albertel 1863: return (&Apache::lonnet::domain($domain,'auth_def'),
1864: &Apache::lonnet::domain($domain,'auth_arg_def'));
1865:
1.80 albertel 1866: }
1867: ###############################################################
1868: ## End Get Authentication Defaults for Domain ##
1869: ###############################################################
1870:
1871: ###############################################################
1872: ## Get Kerberos Defaults for Domain ##
1873: ###############################################################
1874: ##
1875: ## Returns default kerberos version and an associated argument
1876: ## as listed in file domain.tab. If not listed, provides
1877: ## appropriate default domain and kerberos version.
1878: ##
1879: #-------------------------------------------
1880:
1881: =pod
1882:
1.112 bowersj2 1883: =item * get_kerberos_defaults
1.80 albertel 1884:
1885: get_kerberos_defaults($target_domain) returns the default kerberos
1886: version and domain. If not found in domain.tabs, it defaults to
1887: version 4 and the domain of the server.
1888:
1889: ($def_version, $def_krb_domain) = &get_kerberos_defaults($target_domain);
1890:
1891: =cut
1892:
1893: #-------------------------------------------
1894: sub get_kerberos_defaults {
1895: my $domain=shift;
1896: my ($krbdef,$krbdefdom) =
1897: &Apache::loncommon::get_auth_defaults($domain);
1898: unless ($krbdef =~/^krb/ && $krbdefdom) {
1899: $ENV{'SERVER_NAME'}=~/(\w+\.\w+)$/;
1900: my $krbdefdom=$1;
1901: $krbdefdom=~tr/a-z/A-Z/;
1902: $krbdef = "krb4";
1903: }
1904: return ($krbdef,$krbdefdom);
1905: }
1.112 bowersj2 1906:
1907: =pod
1908:
1909: =back
1910:
1911: =cut
1.32 matthew 1912:
1.46 matthew 1913: ###############################################################
1914: ## Thesaurus Functions ##
1915: ###############################################################
1.20 www 1916:
1.46 matthew 1917: =pod
1.20 www 1918:
1.112 bowersj2 1919: =head1 Thesaurus Functions
1920:
1921: =over 4
1922:
1923: =item * initialize_keywords
1.46 matthew 1924:
1925: Initializes the package variable %Keywords if it is empty. Uses the
1926: package variable $thesaurus_db_file.
1927:
1928: =cut
1929:
1930: ###################################################
1931:
1932: sub initialize_keywords {
1933: return 1 if (scalar keys(%Keywords));
1934: # If we are here, %Keywords is empty, so fill it up
1935: # Make sure the file we need exists...
1936: if (! -e $thesaurus_db_file) {
1937: &Apache::lonnet::logthis("Attempt to access $thesaurus_db_file".
1938: " failed because it does not exist");
1939: return 0;
1940: }
1941: # Set up the hash as a database
1942: my %thesaurus_db;
1943: if (! tie(%thesaurus_db,'GDBM_File',
1.53 albertel 1944: $thesaurus_db_file,&GDBM_READER(),0640)){
1.46 matthew 1945: &Apache::lonnet::logthis("Could not tie \%thesaurus_db to ".
1946: $thesaurus_db_file);
1947: return 0;
1948: }
1949: # Get the average number of appearances of a word.
1950: my $avecount = $thesaurus_db{'average.count'};
1951: # Put keywords (those that appear > average) into %Keywords
1952: while (my ($word,$data)=each (%thesaurus_db)) {
1953: my ($count,undef) = split /:/,$data;
1954: $Keywords{$word}++ if ($count > $avecount);
1955: }
1956: untie %thesaurus_db;
1957: # Remove special values from %Keywords.
1.356 albertel 1958: foreach my $value ('total.count','average.count') {
1959: delete($Keywords{$value}) if (exists($Keywords{$value}));
1.46 matthew 1960: }
1961: return 1;
1962: }
1963:
1964: ###################################################
1965:
1966: =pod
1967:
1.112 bowersj2 1968: =item * keyword($word)
1.46 matthew 1969:
1970: Returns true if $word is a keyword. A keyword is a word that appears more
1971: than the average number of times in the thesaurus database. Calls
1972: &initialize_keywords
1973:
1974: =cut
1975:
1976: ###################################################
1.20 www 1977:
1978: sub keyword {
1.46 matthew 1979: return if (!&initialize_keywords());
1980: my $word=lc(shift());
1981: $word=~s/\W//g;
1982: return exists($Keywords{$word});
1.20 www 1983: }
1.46 matthew 1984:
1985: ###############################################################
1986:
1987: =pod
1.20 www 1988:
1.112 bowersj2 1989: =item * get_related_words
1.46 matthew 1990:
1.160 matthew 1991: Look up a word in the thesaurus. Takes a scalar argument and returns
1.46 matthew 1992: an array of words. If the keyword is not in the thesaurus, an empty array
1993: will be returned. The order of the words returned is determined by the
1994: database which holds them.
1995:
1996: Uses global $thesaurus_db_file.
1997:
1998: =cut
1999:
2000: ###############################################################
2001: sub get_related_words {
2002: my $keyword = shift;
2003: my %thesaurus_db;
2004: if (! -e $thesaurus_db_file) {
2005: &Apache::lonnet::logthis("Attempt to access $thesaurus_db_file ".
2006: "failed because the file does not exist");
2007: return ();
2008: }
2009: if (! tie(%thesaurus_db,'GDBM_File',
1.53 albertel 2010: $thesaurus_db_file,&GDBM_READER(),0640)){
1.46 matthew 2011: return ();
2012: }
2013: my @Words=();
1.429 www 2014: my $count=0;
1.46 matthew 2015: if (exists($thesaurus_db{$keyword})) {
1.356 albertel 2016: # The first element is the number of times
2017: # the word appears. We do not need it now.
1.429 www 2018: my (undef,@RelatedWords) = (split(/:/,$thesaurus_db{$keyword}));
2019: my (undef,$mostfrequentcount)=split(/\,/,$RelatedWords[0]);
2020: my $threshold=$mostfrequentcount/10;
2021: foreach my $possibleword (@RelatedWords) {
2022: my ($word,$wordcount)=split(/\,/,$possibleword);
2023: if ($wordcount>$threshold) {
2024: push(@Words,$word);
2025: $count++;
2026: if ($count>10) { last; }
2027: }
1.20 www 2028: }
2029: }
1.46 matthew 2030: untie %thesaurus_db;
2031: return @Words;
1.14 harris41 2032: }
1.46 matthew 2033:
1.112 bowersj2 2034: =pod
2035:
2036: =back
2037:
2038: =cut
1.61 www 2039:
2040: # -------------------------------------------------------------- Plaintext name
1.81 albertel 2041: =pod
2042:
1.112 bowersj2 2043: =head1 User Name Functions
2044:
2045: =over 4
2046:
1.226 albertel 2047: =item * plainname($uname,$udom,$first)
1.81 albertel 2048:
1.112 bowersj2 2049: Takes a users logon name and returns it as a string in
1.226 albertel 2050: "first middle last generation" form
2051: if $first is set to 'lastname' then it returns it as
2052: 'lastname generation, firstname middlename' if their is a lastname
1.81 albertel 2053:
2054: =cut
1.61 www 2055:
1.295 www 2056:
1.81 albertel 2057: ###############################################################
1.61 www 2058: sub plainname {
1.226 albertel 2059: my ($uname,$udom,$first)=@_;
1.537 albertel 2060: return if (!defined($uname) || !defined($udom));
1.295 www 2061: my %names=&getnames($uname,$udom);
1.226 albertel 2062: my $name=&Apache::lonnet::format_name($names{'firstname'},
2063: $names{'middlename'},
2064: $names{'lastname'},
2065: $names{'generation'},$first);
2066: $name=~s/^\s+//;
1.62 www 2067: $name=~s/\s+$//;
2068: $name=~s/\s+/ /g;
1.353 albertel 2069: if ($name !~ /\S/) { $name=$uname.':'.$udom; }
1.62 www 2070: return $name;
1.61 www 2071: }
1.66 www 2072:
2073: # -------------------------------------------------------------------- Nickname
1.81 albertel 2074: =pod
2075:
1.112 bowersj2 2076: =item * nickname($uname,$udom)
1.81 albertel 2077:
2078: Gets a users name and returns it as a string as
2079:
2080: ""nickname""
1.66 www 2081:
1.81 albertel 2082: if the user has a nickname or
2083:
2084: "first middle last generation"
2085:
2086: if the user does not
2087:
2088: =cut
1.66 www 2089:
2090: sub nickname {
2091: my ($uname,$udom)=@_;
1.537 albertel 2092: return if (!defined($uname) || !defined($udom));
1.295 www 2093: my %names=&getnames($uname,$udom);
1.68 albertel 2094: my $name=$names{'nickname'};
1.66 www 2095: if ($name) {
2096: $name='"'.$name.'"';
2097: } else {
2098: $name=$names{'firstname'}.' '.$names{'middlename'}.' '.
2099: $names{'lastname'}.' '.$names{'generation'};
2100: $name=~s/\s+$//;
2101: $name=~s/\s+/ /g;
2102: }
2103: return $name;
2104: }
2105:
1.295 www 2106: sub getnames {
2107: my ($uname,$udom)=@_;
1.537 albertel 2108: return if (!defined($uname) || !defined($udom));
1.433 albertel 2109: if ($udom eq 'public' && $uname eq 'public') {
2110: return ('lastname' => &mt('Public'));
2111: }
1.295 www 2112: my $id=$uname.':'.$udom;
2113: my ($names,$cached)=&Apache::lonnet::is_cached_new('namescache',$id);
2114: if ($cached) {
2115: return %{$names};
2116: } else {
2117: my %loadnames=&Apache::lonnet::get('environment',
2118: ['firstname','middlename','lastname','generation','nickname'],
2119: $udom,$uname);
2120: &Apache::lonnet::do_cache_new('namescache',$id,\%loadnames);
2121: return %loadnames;
2122: }
2123: }
1.61 www 2124:
1.542 raeburn 2125: # -------------------------------------------------------------------- getemails
2126: =pod
2127:
2128: =item * getemails($uname,$udom)
2129:
2130: Gets a user's email information and returns it as a hash with keys:
2131: notification, critnotification, permanentemail
2132:
2133: For notification and critnotification, values are comma-separated lists
2134: of e-mail address(es); for permanentemail, value is a single e-mail address.
2135:
2136: =cut
2137:
1.466 albertel 2138: sub getemails {
2139: my ($uname,$udom)=@_;
2140: if ($udom eq 'public' && $uname eq 'public') {
2141: return;
2142: }
1.467 www 2143: if (!$udom) { $udom=$env{'user.domain'}; }
2144: if (!$uname) { $uname=$env{'user.name'}; }
1.466 albertel 2145: my $id=$uname.':'.$udom;
2146: my ($names,$cached)=&Apache::lonnet::is_cached_new('emailscache',$id);
2147: if ($cached) {
2148: return %{$names};
2149: } else {
2150: my %loadnames=&Apache::lonnet::get('environment',
2151: ['notification','critnotification',
2152: 'permanentemail'],
2153: $udom,$uname);
2154: &Apache::lonnet::do_cache_new('emailscache',$id,\%loadnames);
2155: return %loadnames;
2156: }
2157: }
2158:
1.61 www 2159: # ------------------------------------------------------------------ Screenname
1.81 albertel 2160:
2161: =pod
2162:
1.112 bowersj2 2163: =item * screenname($uname,$udom)
1.81 albertel 2164:
2165: Gets a users screenname and returns it as a string
2166:
2167: =cut
1.61 www 2168:
2169: sub screenname {
2170: my ($uname,$udom)=@_;
1.258 albertel 2171: if ($uname eq $env{'user.name'} &&
2172: $udom eq $env{'user.domain'}) {return $env{'environment.screenname'};}
1.212 albertel 2173: my %names=&Apache::lonnet::get('environment',['screenname'],$udom,$uname);
1.68 albertel 2174: return $names{'screenname'};
1.62 www 2175: }
2176:
1.212 albertel 2177:
1.62 www 2178: # ------------------------------------------------------------- Message Wrapper
2179:
2180: sub messagewrapper {
1.369 www 2181: my ($link,$username,$domain,$subject,$text)=@_;
1.62 www 2182: return
1.441 albertel 2183: '<a href="/adm/email?compose=individual&'.
2184: 'recname='.$username.'&recdom='.$domain.
2185: '&subject='.&escape($subject).'&text='.&escape($text).'" '.
1.200 matthew 2186: 'title="'.&mt('Send message').'">'.$link.'</a>';
1.74 www 2187: }
2188: # --------------------------------------------------------------- Notes Wrapper
2189:
2190: sub noteswrapper {
2191: my ($link,$un,$do)=@_;
2192: return
2193: "<a href='/adm/email?recordftf=retrieve&recname=$un&recdom=$do'>$link</a>";
1.62 www 2194: }
2195: # ------------------------------------------------------------- Aboutme Wrapper
2196:
2197: sub aboutmewrapper {
1.166 www 2198: my ($link,$username,$domain,$target)=@_;
1.447 raeburn 2199: if (!defined($username) && !defined($domain)) {
2200: return;
2201: }
1.205 www 2202: return '<a href="/adm/'.$domain.'/'.$username.'/aboutme"'.
1.454 banghart 2203: ($target?' target="$target"':'').' title="'.&mt("View this user's personal page").'">'.$link.'</a>';
1.62 www 2204: }
2205:
2206: # ------------------------------------------------------------ Syllabus Wrapper
2207:
2208:
2209: sub syllabuswrapper {
1.109 matthew 2210: my ($linktext,$coursedir,$domain,$fontcolor)=@_;
2211: if ($fontcolor) {
2212: $linktext='<font color="'.$fontcolor.'">'.$linktext.'</font>';
2213: }
1.208 matthew 2214: return qq{<a href="/public/$domain/$coursedir/syllabus">$linktext</a>};
1.61 www 2215: }
1.14 harris41 2216:
1.208 matthew 2217: sub track_student_link {
1.268 albertel 2218: my ($linktext,$sname,$sdom,$target,$start) = @_;
2219: my $link ="/adm/trackstudent?";
1.208 matthew 2220: my $title = 'View recent activity';
2221: if (defined($sname) && $sname !~ /^\s*$/ &&
2222: defined($sdom) && $sdom !~ /^\s*$/) {
1.268 albertel 2223: $link .= "selected_student=$sname:$sdom";
1.208 matthew 2224: $title .= ' of this student';
1.268 albertel 2225: }
1.208 matthew 2226: if (defined($target) && $target !~ /^\s*$/) {
2227: $target = qq{target="$target"};
2228: } else {
2229: $target = '';
2230: }
1.268 albertel 2231: if ($start) { $link.='&start='.$start; }
1.448 albertel 2232:
2233: return qq{<a href="$link" title="$title" $target>$linktext</a>}.
2234: &help_open_topic('View_recent_activity');
1.208 matthew 2235: }
2236:
1.508 www 2237: # ===================================================== Display a student photo
2238:
2239:
1.509 albertel 2240: sub student_image_tag {
1.508 www 2241: my ($domain,$user)=@_;
2242: my $imgsrc=&Apache::lonnet::studentphoto($domain,$user,'jpg');
2243: if (($imgsrc) && ($imgsrc ne '/adm/lonKaputt/lonlogo_broken.gif')) {
2244: return '<img src="'.$imgsrc.'" align="right" />';
2245: } else {
2246: return '';
2247: }
2248: }
2249:
1.112 bowersj2 2250: =pod
2251:
2252: =back
2253:
2254: =head1 Access .tab File Data
2255:
2256: =over 4
2257:
2258: =item * languageids()
2259:
2260: returns list of all language ids
2261:
2262: =cut
2263:
1.14 harris41 2264: sub languageids {
1.16 harris41 2265: return sort(keys(%language));
1.14 harris41 2266: }
2267:
1.112 bowersj2 2268: =pod
2269:
2270: =item * languagedescription()
2271:
2272: returns description of a specified language id
2273:
2274: =cut
2275:
1.14 harris41 2276: sub languagedescription {
1.125 www 2277: my $code=shift;
2278: return ($supported_language{$code}?'* ':'').
2279: $language{$code}.
1.126 www 2280: ($supported_language{$code}?' ('.&mt('interface available').')':'');
1.145 www 2281: }
2282:
2283: sub plainlanguagedescription {
2284: my $code=shift;
2285: return $language{$code};
2286: }
2287:
2288: sub supportedlanguagecode {
2289: my $code=shift;
2290: return $supported_language{$code};
1.97 www 2291: }
2292:
1.112 bowersj2 2293: =pod
2294:
2295: =item * copyrightids()
2296:
2297: returns list of all copyrights
2298:
2299: =cut
2300:
2301: sub copyrightids {
2302: return sort(keys(%cprtag));
2303: }
2304:
2305: =pod
2306:
2307: =item * copyrightdescription()
2308:
2309: returns description of a specified copyright id
2310:
2311: =cut
2312:
2313: sub copyrightdescription {
1.166 www 2314: return &mt($cprtag{shift(@_)});
1.112 bowersj2 2315: }
1.197 matthew 2316:
2317: =pod
2318:
1.192 taceyjo1 2319: =item * source_copyrightids()
2320:
2321: returns list of all source copyrights
2322:
2323: =cut
2324:
2325: sub source_copyrightids {
2326: return sort(keys(%scprtag));
2327: }
2328:
2329: =pod
2330:
2331: =item * source_copyrightdescription()
2332:
2333: returns description of a specified source copyright id
2334:
2335: =cut
2336:
2337: sub source_copyrightdescription {
2338: return &mt($scprtag{shift(@_)});
2339: }
1.112 bowersj2 2340:
2341: =pod
2342:
2343: =item * filecategories()
2344:
2345: returns list of all file categories
2346:
2347: =cut
2348:
2349: sub filecategories {
2350: return sort(keys(%category_extensions));
2351: }
2352:
2353: =pod
2354:
2355: =item * filecategorytypes()
2356:
2357: returns list of file types belonging to a given file
2358: category
2359:
2360: =cut
2361:
2362: sub filecategorytypes {
1.356 albertel 2363: my ($cat) = @_;
2364: return @{$category_extensions{lc($cat)}};
1.112 bowersj2 2365: }
2366:
2367: =pod
2368:
2369: =item * fileembstyle()
2370:
2371: returns embedding style for a specified file type
2372:
2373: =cut
2374:
2375: sub fileembstyle {
2376: return $fe{lc(shift(@_))};
1.169 www 2377: }
2378:
1.351 www 2379: sub filemimetype {
2380: return $fm{lc(shift(@_))};
2381: }
2382:
1.169 www 2383:
2384: sub filecategoryselect {
2385: my ($name,$value)=@_;
1.189 matthew 2386: return &select_form($value,$name,
1.169 www 2387: '' => &mt('Any category'),
2388: map { $_,$_ } sort(keys(%category_extensions)));
1.112 bowersj2 2389: }
2390:
2391: =pod
2392:
2393: =item * filedescription()
2394:
2395: returns description for a specified file type
2396:
2397: =cut
2398:
2399: sub filedescription {
1.188 matthew 2400: my $file_description = $fd{lc(shift())};
2401: $file_description =~ s:([\[\]]):~$1:g;
2402: return &mt($file_description);
1.112 bowersj2 2403: }
2404:
2405: =pod
2406:
2407: =item * filedescriptionex()
2408:
2409: returns description for a specified file type with
2410: extra formatting
2411:
2412: =cut
2413:
2414: sub filedescriptionex {
2415: my $ex=shift;
1.188 matthew 2416: my $file_description = $fd{lc($ex)};
2417: $file_description =~ s:([\[\]]):~$1:g;
2418: return '.'.$ex.' '.&mt($file_description);
1.112 bowersj2 2419: }
2420:
2421: # End of .tab access
2422: =pod
2423:
2424: =back
2425:
2426: =cut
2427:
2428: # ------------------------------------------------------------------ File Types
2429: sub fileextensions {
2430: return sort(keys(%fe));
2431: }
2432:
1.97 www 2433: # ----------------------------------------------------------- Display Languages
2434: # returns a hash with all desired display languages
2435: #
2436:
2437: sub display_languages {
2438: my %languages=();
1.356 albertel 2439: foreach my $lang (&preferred_languages()) {
2440: $languages{$lang}=1;
1.97 www 2441: }
2442: &get_unprocessed_cgi($ENV{'QUERY_STRING'},['displaylanguage']);
1.258 albertel 2443: if ($env{'form.displaylanguage'}) {
1.356 albertel 2444: foreach my $lang (split(/\s*(\,|\;|\:)\s*/,$env{'form.displaylanguage'})) {
2445: $languages{$lang}=1;
1.97 www 2446: }
2447: }
2448: return %languages;
1.14 harris41 2449: }
2450:
1.117 www 2451: sub preferred_languages {
2452: my @languages=();
1.258 albertel 2453: if ($env{'course.'.$env{'request.course.id'}.'.languages'}) {
1.117 www 2454: @languages=(@languages,split(/\s*(\,|\;|\:)\s*/,
1.258 albertel 2455: $env{'course.'.$env{'request.course.id'}.'.languages'}));
1.177 www 2456: }
1.258 albertel 2457: if ($env{'environment.languages'}) {
1.459 albertel 2458: @languages=(@languages,
2459: split(/\s*(\,|\;|\:)\s*/,$env{'environment.languages'}));
1.118 www 2460: }
1.162 www 2461: my $browser=(split(/\;/,$ENV{'HTTP_ACCEPT_LANGUAGE'}))[0];
2462: if ($browser) {
2463: @languages=(@languages,split(/\s*(\,|\;|\:)\s*/,$browser));
2464: }
1.514 albertel 2465: if (&Apache::lonnet::domain($env{'user.domain'},'lang_def')) {
1.118 www 2466: @languages=(@languages,
1.514 albertel 2467: &Apache::lonnet::domain($env{'user.domain'},
2468: 'lang_def'));
1.118 www 2469: }
1.514 albertel 2470: if (&Apache::lonnet::domain($env{'request.role.domain'},'lang_def')) {
1.118 www 2471: @languages=(@languages,
1.514 albertel 2472: &Apache::lonnet::domain($env{'request.role.domain'},
2473: 'lang_def'));
1.118 www 2474: }
1.514 albertel 2475: if (&Apache::lonnet::domain($Apache::lonnet::perlvar{'lonDefDomain'},
2476: 'lang_def')) {
1.118 www 2477: @languages=(@languages,
1.514 albertel 2478: &Apache::lonnet::domain($Apache::lonnet::perlvar{'lonDefDomain'},
2479: 'lang_def'));
1.118 www 2480: }
2481: # turn "en-ca" into "en-ca,en"
2482: my @genlanguages;
1.356 albertel 2483: foreach my $lang (@languages) {
2484: unless ($lang=~/\w/) { next; }
2485: push (@genlanguages,$lang);
2486: if ($lang=~/(\-|\_)/) {
2487: push(@genlanguages,(split(/(\-|\_)/,$lang))[0]);
1.118 www 2488: }
2489: }
2490: return @genlanguages;
1.117 www 2491: }
2492:
1.112 bowersj2 2493: ###############################################################
2494: ## Student Answer Attempts ##
2495: ###############################################################
2496:
2497: =pod
2498:
2499: =head1 Alternate Problem Views
2500:
2501: =over 4
2502:
2503: =item * get_previous_attempt($symb, $username, $domain, $course,
2504: $getattempt, $regexp, $gradesub)
2505:
2506: Return string with previous attempt on problem. Arguments:
2507:
2508: =over 4
2509:
2510: =item * $symb: Problem, including path
2511:
2512: =item * $username: username of the desired student
2513:
2514: =item * $domain: domain of the desired student
1.14 harris41 2515:
1.112 bowersj2 2516: =item * $course: Course ID
1.14 harris41 2517:
1.112 bowersj2 2518: =item * $getattempt: Leave blank for all attempts, otherwise put
2519: something
1.14 harris41 2520:
1.112 bowersj2 2521: =item * $regexp: if string matches this regexp, the string will be
2522: sent to $gradesub
1.14 harris41 2523:
1.112 bowersj2 2524: =item * $gradesub: routine that processes the string if it matches $regexp
1.14 harris41 2525:
1.112 bowersj2 2526: =back
1.14 harris41 2527:
1.112 bowersj2 2528: The output string is a table containing all desired attempts, if any.
1.16 harris41 2529:
1.112 bowersj2 2530: =cut
1.1 albertel 2531:
2532: sub get_previous_attempt {
1.43 ng 2533: my ($symb,$username,$domain,$course,$getattempt,$regexp,$gradesub)=@_;
1.1 albertel 2534: my $prevattempts='';
1.43 ng 2535: no strict 'refs';
1.1 albertel 2536: if ($symb) {
1.3 albertel 2537: my (%returnhash)=
2538: &Apache::lonnet::restore($symb,$course,$domain,$username);
1.1 albertel 2539: if ($returnhash{'version'}) {
2540: my %lasthash=();
2541: my $version;
2542: for ($version=1;$version<=$returnhash{'version'};$version++) {
1.356 albertel 2543: foreach my $key (sort(split(/\:/,$returnhash{$version.':keys'}))) {
2544: $lasthash{$key}=$returnhash{$version.':'.$key};
1.19 harris41 2545: }
1.1 albertel 2546: }
1.43 ng 2547: $prevattempts='<table border="0" width="100%"><tr><td bgcolor="#777777">';
1.40 ng 2548: $prevattempts.='<table border="0" width="100%"><tr bgcolor="#e6ffff"><td>History</td>';
1.356 albertel 2549: foreach my $key (sort(keys(%lasthash))) {
2550: my ($ign,@parts) = split(/\./,$key);
1.41 ng 2551: if ($#parts > 0) {
1.31 albertel 2552: my $data=$parts[-1];
2553: pop(@parts);
1.40 ng 2554: $prevattempts.='<td>Part '.join('.',@parts).'<br />'.$data.' </td>';
1.31 albertel 2555: } else {
1.41 ng 2556: if ($#parts == 0) {
2557: $prevattempts.='<th>'.$parts[0].'</th>';
2558: } else {
2559: $prevattempts.='<th>'.$ign.'</th>';
2560: }
1.31 albertel 2561: }
1.16 harris41 2562: }
1.40 ng 2563: if ($getattempt eq '') {
2564: for ($version=1;$version<=$returnhash{'version'};$version++) {
2565: $prevattempts.='</tr><tr bgcolor="#ffffe6"><td>Transaction '.$version.'</td>';
1.356 albertel 2566: foreach my $key (sort(keys(%lasthash))) {
1.40 ng 2567: my $value;
1.356 albertel 2568: if ($key =~ /timestamp/) {
2569: $value=scalar(localtime($returnhash{$version.':'.$key}));
1.40 ng 2570: } else {
1.356 albertel 2571: $value=$returnhash{$version.':'.$key};
1.40 ng 2572: }
1.369 www 2573: $prevattempts.='<td>'.&unescape($value).' </td>';
1.40 ng 2574: }
2575: }
1.1 albertel 2576: }
1.40 ng 2577: $prevattempts.='</tr><tr bgcolor="#ffffe6"><td>Current</td>';
1.356 albertel 2578: foreach my $key (sort(keys(%lasthash))) {
1.5 albertel 2579: my $value;
1.356 albertel 2580: if ($key =~ /timestamp/) {
2581: $value=scalar(localtime($lasthash{$key}));
1.5 albertel 2582: } else {
1.356 albertel 2583: $value=$lasthash{$key};
1.5 albertel 2584: }
1.369 www 2585: $value=&unescape($value);
1.356 albertel 2586: if ($key =~/$regexp$/ && (defined &$gradesub)) {$value = &$gradesub($value)}
1.40 ng 2587: $prevattempts.='<td>'.$value.' </td>';
1.16 harris41 2588: }
1.40 ng 2589: $prevattempts.='</tr></table></td></tr></table>';
1.1 albertel 2590: } else {
2591: $prevattempts='Nothing submitted - no attempts.';
2592: }
2593: } else {
2594: $prevattempts='No data.';
2595: }
1.10 albertel 2596: }
2597:
1.107 albertel 2598: sub relative_to_absolute {
2599: my ($url,$output)=@_;
2600: my $parser=HTML::TokeParser->new(\$output);
2601: my $token;
2602: my $thisdir=$url;
2603: my @rlinks=();
2604: while ($token=$parser->get_token) {
2605: if ($token->[0] eq 'S') {
2606: if ($token->[1] eq 'a') {
2607: if ($token->[2]->{'href'}) {
2608: $rlinks[$#rlinks+1]=$token->[2]->{'href'};
2609: }
2610: } elsif ($token->[1] eq 'img' || $token->[1] eq 'embed' ) {
2611: $rlinks[$#rlinks+1]=$token->[2]->{'src'};
2612: } elsif ($token->[1] eq 'base') {
2613: $thisdir=$token->[2]->{'href'};
2614: }
2615: }
2616: }
2617: $thisdir=~s-/[^/]*$--;
1.356 albertel 2618: foreach my $link (@rlinks) {
2619: unless (($link=~/^http:\/\//i) ||
2620: ($link=~/^\//) ||
2621: ($link=~/^javascript:/i) ||
2622: ($link=~/^mailto:/i) ||
2623: ($link=~/^\#/)) {
2624: my $newlocation=&Apache::lonnet::hreflocation($thisdir,$link);
2625: $output=~s/(\"|\'|\=\s*)\Q$link\E(\"|\'|\s|\>)/$1$newlocation$2/;
1.107 albertel 2626: }
2627: }
2628: # -------------------------------------------------- Deal with Applet codebases
2629: $output=~s/(\<applet[^\>]+)(codebase\=[^\S\>]+)*([^\>]*)\>/$1.($2?$2:' codebase="'.$thisdir.'"').$3.'>'/gei;
2630: return $output;
2631: }
2632:
1.112 bowersj2 2633: =pod
2634:
2635: =item * get_student_view
2636:
2637: show a snapshot of what student was looking at
2638:
2639: =cut
2640:
1.10 albertel 2641: sub get_student_view {
1.186 albertel 2642: my ($symb,$username,$domain,$courseid,$target,$moreenv) = @_;
1.114 www 2643: my ($map,$id,$feedurl) = &Apache::lonnet::decode_symb($symb);
1.186 albertel 2644: my (%form);
1.10 albertel 2645: my @elements=('symb','courseid','domain','username');
2646: foreach my $element (@elements) {
1.186 albertel 2647: $form{'grade_'.$element}=eval '$'.$element #'
1.10 albertel 2648: }
1.186 albertel 2649: if (defined($moreenv)) {
2650: %form=(%form,%{$moreenv});
2651: }
1.236 albertel 2652: if (defined($target)) { $form{'grade_target'} = $target; }
1.107 albertel 2653: $feedurl=&Apache::lonnet::clutter($feedurl);
1.186 albertel 2654: my $userview=&Apache::lonnet::ssi_body($feedurl,%form);
1.11 albertel 2655: $userview=~s/\<body[^\>]*\>//gi;
2656: $userview=~s/\<\/body\>//gi;
2657: $userview=~s/\<html\>//gi;
2658: $userview=~s/\<\/html\>//gi;
2659: $userview=~s/\<head\>//gi;
2660: $userview=~s/\<\/head\>//gi;
2661: $userview=~s/action\s*\=/would_be_action\=/gi;
1.107 albertel 2662: $userview=&relative_to_absolute($feedurl,$userview);
1.11 albertel 2663: return $userview;
2664: }
2665:
1.112 bowersj2 2666: =pod
2667:
2668: =item * get_student_answers()
2669:
2670: show a snapshot of how student was answering problem
2671:
2672: =cut
2673:
1.11 albertel 2674: sub get_student_answers {
1.100 sakharuk 2675: my ($symb,$username,$domain,$courseid,%form) = @_;
1.114 www 2676: my ($map,$id,$feedurl) = &Apache::lonnet::decode_symb($symb);
1.186 albertel 2677: my (%moreenv);
1.11 albertel 2678: my @elements=('symb','courseid','domain','username');
2679: foreach my $element (@elements) {
1.186 albertel 2680: $moreenv{'grade_'.$element}=eval '$'.$element #'
1.10 albertel 2681: }
1.186 albertel 2682: $moreenv{'grade_target'}='answer';
2683: %moreenv=(%form,%moreenv);
1.497 raeburn 2684: $feedurl = &Apache::lonnet::clutter($feedurl);
2685: my $userview=&Apache::lonnet::ssi($feedurl,%moreenv);
1.10 albertel 2686: return $userview;
1.1 albertel 2687: }
1.116 albertel 2688:
2689: =pod
2690:
2691: =item * &submlink()
2692:
1.242 albertel 2693: Inputs: $text $uname $udom $symb $target
1.116 albertel 2694:
2695: Returns: A link to grades.pm such as to see the SUBM view of a student
2696:
2697: =cut
2698:
2699: ###############################################
2700: sub submlink {
1.242 albertel 2701: my ($text,$uname,$udom,$symb,$target)=@_;
1.116 albertel 2702: if (!($uname && $udom)) {
2703: (my $cursymb, my $courseid,$udom,$uname)=
1.463 albertel 2704: &Apache::lonnet::whichuser($symb);
1.116 albertel 2705: if (!$symb) { $symb=$cursymb; }
2706: }
1.254 matthew 2707: if (!$symb) { $symb=&Apache::lonnet::symbread(); }
1.369 www 2708: $symb=&escape($symb);
1.242 albertel 2709: if ($target) { $target="target=\"$target\""; }
2710: return '<a href="/adm/grades?&command=submission&'.
2711: 'symb='.$symb.'&student='.$uname.
2712: '&userdom='.$udom.'" '.$target.'>'.$text.'</a>';
2713: }
2714: ##############################################
2715:
2716: =pod
2717:
2718: =item * &pgrdlink()
2719:
2720: Inputs: $text $uname $udom $symb $target
2721:
2722: Returns: A link to grades.pm such as to see the PGRD view of a student
2723:
2724: =cut
2725:
2726: ###############################################
2727: sub pgrdlink {
2728: my $link=&submlink(@_);
2729: $link=~s/(&command=submission)/$1&showgrading=yes/;
2730: return $link;
2731: }
2732: ##############################################
2733:
2734: =pod
2735:
2736: =item * &pprmlink()
2737:
2738: Inputs: $text $uname $udom $symb $target
2739:
2740: Returns: A link to parmset.pm such as to see the PPRM view of a
1.283 albertel 2741: student and a specific resource
1.242 albertel 2742:
2743: =cut
2744:
2745: ###############################################
2746: sub pprmlink {
2747: my ($text,$uname,$udom,$symb,$target)=@_;
2748: if (!($uname && $udom)) {
2749: (my $cursymb, my $courseid,$udom,$uname)=
1.463 albertel 2750: &Apache::lonnet::whichuser($symb);
1.242 albertel 2751: if (!$symb) { $symb=$cursymb; }
2752: }
1.254 matthew 2753: if (!$symb) { $symb=&Apache::lonnet::symbread(); }
1.369 www 2754: $symb=&escape($symb);
1.242 albertel 2755: if ($target) { $target="target=\"$target\""; }
2756: return '<a href="/adm/parmset?&command=set&'.
2757: 'symb='.$symb.'&uname='.$uname.
2758: '&udom='.$udom.'" '.$target.'>'.$text.'</a>';
1.116 albertel 2759: }
2760: ##############################################
1.37 matthew 2761:
1.112 bowersj2 2762: =pod
2763:
2764: =back
2765:
2766: =cut
2767:
1.37 matthew 2768: ###############################################
1.51 www 2769:
2770:
2771: sub timehash {
2772: my @ltime=localtime(shift);
2773: return ( 'seconds' => $ltime[0],
2774: 'minutes' => $ltime[1],
2775: 'hours' => $ltime[2],
2776: 'day' => $ltime[3],
2777: 'month' => $ltime[4]+1,
2778: 'year' => $ltime[5]+1900,
2779: 'weekday' => $ltime[6],
2780: 'dayyear' => $ltime[7]+1,
2781: 'dlsav' => $ltime[8] );
2782: }
2783:
1.370 www 2784: sub utc_string {
2785: my ($date)=@_;
1.371 www 2786: return strftime("%Y%m%dT%H%M%SZ",gmtime($date));
1.370 www 2787: }
2788:
1.51 www 2789: sub maketime {
2790: my %th=@_;
2791: return POSIX::mktime(
2792: ($th{'seconds'},$th{'minutes'},$th{'hours'},
1.210 www 2793: $th{'day'},$th{'month'}-1,$th{'year'}-1900,0,0,-1));
1.70 www 2794: }
2795:
2796: #########################################
1.51 www 2797:
2798: sub findallcourses {
1.482 raeburn 2799: my ($roles,$uname,$udom) = @_;
1.355 albertel 2800: my %roles;
2801: if (ref($roles)) { %roles = map { $_ => 1 } @{$roles}; }
1.348 albertel 2802: my %courses;
1.51 www 2803: my $now=time;
1.482 raeburn 2804: if (!defined($uname)) {
2805: $uname = $env{'user.name'};
2806: }
2807: if (!defined($udom)) {
2808: $udom = $env{'user.domain'};
2809: }
2810: if (($uname ne $env{'user.name'}) || ($udom ne $env{'user.domain'})) {
2811: my %roleshash = &Apache::lonnet::dump('roles',$udom,$uname);
2812: if (!%roles) {
2813: %roles = (
2814: cc => 1,
2815: in => 1,
2816: ep => 1,
2817: ta => 1,
2818: cr => 1,
2819: st => 1,
2820: );
2821: }
2822: foreach my $entry (keys(%roleshash)) {
2823: my ($trole,$tend,$tstart) = split(/_/,$roleshash{$entry});
2824: if ($trole =~ /^cr/) {
2825: next if (!exists($roles{$trole}) && !exists($roles{'cr'}));
2826: } else {
2827: next if (!exists($roles{$trole}));
2828: }
2829: if ($tend) {
2830: next if ($tend < $now);
2831: }
2832: if ($tstart) {
2833: next if ($tstart > $now);
2834: }
2835: my ($cdom,$cnum,$sec,$cnumpart,$secpart,$role,$realsec);
2836: (undef,$cdom,$cnumpart,$secpart) = split(/\//,$entry);
2837: if ($secpart eq '') {
2838: ($cnum,$role) = split(/_/,$cnumpart);
2839: $sec = 'none';
2840: $realsec = '';
2841: } else {
2842: $cnum = $cnumpart;
2843: ($sec,$role) = split(/_/,$secpart);
2844: $realsec = $sec;
1.490 raeburn 2845: }
1.482 raeburn 2846: $courses{$cdom.'_'.$cnum}{$sec} = $trole.'/'.$cdom.'/'.$cnum.'/'.$realsec;
2847: }
2848: } else {
2849: foreach my $key (keys(%env)) {
1.483 albertel 2850: if ( $key=~m{^user\.role\.(\w+)\./($match_domain)/($match_courseid)/?(\w*)$} ||
2851: $key=~m{^user\.role\.(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_courseid)/?(\w*)$}) {
1.482 raeburn 2852: my ($role,$cdom,$cnum,$sec) = ($1,$2,$3,$4);
2853: next if ($role eq 'ca' || $role eq 'aa');
2854: next if (%roles && !exists($roles{$role}));
2855: my ($starttime,$endtime)=split(/\./,$env{$key});
2856: my $active=1;
2857: if ($starttime) {
2858: if ($now<$starttime) { $active=0; }
2859: }
2860: if ($endtime) {
2861: if ($now>$endtime) { $active=0; }
2862: }
2863: if ($active) {
2864: if ($sec eq '') {
2865: $sec = 'none';
2866: }
2867: $courses{$cdom.'_'.$cnum}{$sec} =
2868: $role.'/'.$cdom.'/'.$cnum.'/'.$sec;
1.474 raeburn 2869: }
2870: }
1.51 www 2871: }
2872: }
1.474 raeburn 2873: return %courses;
1.51 www 2874: }
1.37 matthew 2875:
1.54 www 2876: ###############################################
1.474 raeburn 2877:
2878: sub blockcheck {
1.482 raeburn 2879: my ($setters,$activity,$uname,$udom) = @_;
1.490 raeburn 2880:
2881: if (!defined($udom)) {
2882: $udom = $env{'user.domain'};
2883: }
2884: if (!defined($uname)) {
2885: $uname = $env{'user.name'};
2886: }
2887:
2888: # If uname and udom are for a course, check for blocks in the course.
2889:
2890: if (&Apache::lonnet::is_course($udom,$uname)) {
2891: my %records = &Apache::lonnet::dump('comm_block',$udom,$uname);
1.502 raeburn 2892: my ($startblock,$endblock)=&get_blocks($setters,$activity,$udom,$uname);
1.490 raeburn 2893: return ($startblock,$endblock);
2894: }
1.474 raeburn 2895:
1.502 raeburn 2896: my $startblock = 0;
2897: my $endblock = 0;
1.482 raeburn 2898: my %live_courses = &findallcourses(undef,$uname,$udom);
1.474 raeburn 2899:
1.490 raeburn 2900: # If uname is for a user, and activity is course-specific, i.e.,
2901: # boards, chat or groups, check for blocking in current course only.
1.474 raeburn 2902:
1.490 raeburn 2903: if (($activity eq 'boards' || $activity eq 'chat' ||
2904: $activity eq 'groups') && ($env{'request.course.id'})) {
2905: foreach my $key (keys(%live_courses)) {
2906: if ($key ne $env{'request.course.id'}) {
2907: delete($live_courses{$key});
2908: }
2909: }
2910: }
2911:
2912: my $otheruser = 0;
2913: my %own_courses;
2914: if ((($uname ne $env{'user.name'})) || ($udom ne $env{'user.domain'})) {
2915: # Resource belongs to user other than current user.
2916: $otheruser = 1;
2917: # Gather courses for current user
2918: %own_courses =
2919: &findallcourses(undef,$env{'user.name'},$env{'user.domain'});
2920: }
2921:
2922: # Gather active course roles - course coordinator, instructor,
2923: # exam proctor, ta, student, or custom role.
1.474 raeburn 2924:
2925: foreach my $course (keys(%live_courses)) {
1.482 raeburn 2926: my ($cdom,$cnum);
2927: if ((defined($env{'course.'.$course.'.domain'})) && (defined($env{'course.'.$course.'.num'}))) {
2928: $cdom = $env{'course.'.$course.'.domain'};
2929: $cnum = $env{'course.'.$course.'.num'};
2930: } else {
1.490 raeburn 2931: ($cdom,$cnum) = split(/_/,$course);
1.482 raeburn 2932: }
2933: my $no_ownblock = 0;
2934: my $no_userblock = 0;
1.533 raeburn 2935: if ($otheruser && $activity ne 'com') {
1.490 raeburn 2936: # Check if current user has 'evb' priv for this
2937: if (defined($own_courses{$course})) {
2938: foreach my $sec (keys(%{$own_courses{$course}})) {
2939: my $checkrole = 'cm./'.$cdom.'/'.$cnum;
2940: if ($sec ne 'none') {
2941: $checkrole .= '/'.$sec;
2942: }
2943: if (&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) {
2944: $no_ownblock = 1;
2945: last;
2946: }
2947: }
2948: }
2949: # if they have 'evb' priv and are currently not playing student
2950: next if (($no_ownblock) &&
2951: ($env{'request.role'} !~ m{^st\./$cdom/$cnum}));
2952: }
1.474 raeburn 2953: foreach my $sec (keys(%{$live_courses{$course}})) {
1.482 raeburn 2954: my $checkrole = 'cm./'.$cdom.'/'.$cnum;
1.474 raeburn 2955: if ($sec ne 'none') {
1.482 raeburn 2956: $checkrole .= '/'.$sec;
1.474 raeburn 2957: }
1.490 raeburn 2958: if ($otheruser) {
2959: # Resource belongs to user other than current user.
2960: # Assemble privs for that user, and check for 'evb' priv.
1.482 raeburn 2961: my ($trole,$tdom,$tnum,$tsec);
2962: my $entry = $live_courses{$course}{$sec};
2963: if ($entry =~ /^cr/) {
2964: ($trole,$tdom,$tnum,$tsec) =
2965: ($entry =~ m|^(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_username)/?(\w*)$|);
2966: } else {
2967: ($trole,$tdom,$tnum,$tsec) = split(/\//,$entry);
2968: }
2969: my ($spec,$area,$trest,%allroles,%userroles);
2970: $area = '/'.$tdom.'/'.$tnum;
2971: $trest = $tnum;
2972: if ($tsec ne '') {
2973: $area .= '/'.$tsec;
2974: $trest .= '/'.$tsec;
2975: }
2976: $spec = $trole.'.'.$area;
2977: if ($trole =~ /^cr/) {
2978: &Apache::lonnet::custom_roleprivs(\%allroles,$trole,
2979: $tdom,$spec,$trest,$area);
2980: } else {
2981: &Apache::lonnet::standard_roleprivs(\%allroles,$trole,
2982: $tdom,$spec,$trest,$area);
2983: }
2984: my ($author,$adv) = &Apache::lonnet::set_userprivs(\%userroles,\%allroles);
1.486 raeburn 2985: if ($userroles{'user.priv.'.$checkrole} =~ /evb\&([^\:]*)/) {
2986: if ($1) {
2987: $no_userblock = 1;
2988: last;
2989: }
2990: }
1.490 raeburn 2991: } else {
2992: # Resource belongs to current user
2993: # Check for 'evb' priv via lonnet::allowed().
1.482 raeburn 2994: if (&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) {
2995: $no_ownblock = 1;
2996: last;
2997: }
1.474 raeburn 2998: }
2999: }
3000: # if they have the evb priv and are currently not playing student
1.482 raeburn 3001: next if (($no_ownblock) &&
1.491 albertel 3002: ($env{'request.role'} !~ m{^st\./\Q$cdom\E/\Q$cnum\E}));
1.482 raeburn 3003: next if ($no_userblock);
1.474 raeburn 3004:
1.490 raeburn 3005: # Retrieve blocking times and identity of blocker for course
3006: # of specified user, unless user has 'evb' privilege.
1.502 raeburn 3007:
3008: my ($start,$end)=&get_blocks($setters,$activity,$cdom,$cnum);
3009: if (($start != 0) &&
3010: (($startblock == 0) || ($startblock > $start))) {
3011: $startblock = $start;
3012: }
3013: if (($end != 0) &&
3014: (($endblock == 0) || ($endblock < $end))) {
3015: $endblock = $end;
3016: }
1.490 raeburn 3017: }
3018: return ($startblock,$endblock);
3019: }
3020:
3021: sub get_blocks {
3022: my ($setters,$activity,$cdom,$cnum) = @_;
3023: my $startblock = 0;
3024: my $endblock = 0;
3025: my $course = $cdom.'_'.$cnum;
3026: $setters->{$course} = {};
3027: $setters->{$course}{'staff'} = [];
3028: $setters->{$course}{'times'} = [];
3029: my %records = &Apache::lonnet::dump('comm_block',$cdom,$cnum);
3030: foreach my $record (keys(%records)) {
3031: my ($start,$end) = ($record =~ m/^(\d+)____(\d+)$/);
3032: if ($start <= time && $end >= time) {
3033: my ($staff_name,$staff_dom,$title,$blocks) =
3034: &parse_block_record($records{$record});
3035: if ($blocks->{$activity} eq 'on') {
3036: push(@{$$setters{$course}{'staff'}},[$staff_name,$staff_dom]);
3037: push(@{$$setters{$course}{'times'}}, [$start,$end]);
1.491 albertel 3038: if ( ($startblock == 0) || ($startblock > $start) ) {
3039: $startblock = $start;
1.490 raeburn 3040: }
1.491 albertel 3041: if ( ($endblock == 0) || ($endblock < $end) ) {
3042: $endblock = $end;
1.474 raeburn 3043: }
3044: }
3045: }
3046: }
3047: return ($startblock,$endblock);
3048: }
3049:
3050: sub parse_block_record {
3051: my ($record) = @_;
3052: my ($setuname,$setudom,$title,$blocks);
3053: if (ref($record) eq 'HASH') {
3054: ($setuname,$setudom) = split(/:/,$record->{'setter'});
3055: $title = &unescape($record->{'event'});
3056: $blocks = $record->{'blocks'};
3057: } else {
3058: my @data = split(/:/,$record,3);
3059: if (scalar(@data) eq 2) {
3060: $title = $data[1];
3061: ($setuname,$setudom) = split(/@/,$data[0]);
3062: } else {
3063: ($setuname,$setudom,$title) = @data;
3064: }
3065: $blocks = { 'com' => 'on' };
3066: }
3067: return ($setuname,$setudom,$title,$blocks);
3068: }
3069:
3070: sub build_block_table {
3071: my ($startblock,$endblock,$setters) = @_;
3072: my %lt = &Apache::lonlocal::texthash(
3073: 'cacb' => 'Currently active communication blocks',
3074: 'cour' => 'Course',
3075: 'dura' => 'Duration',
3076: 'blse' => 'Block set by'
3077: );
3078: my $output;
1.476 raeburn 3079: $output = '<br />'.$lt{'cacb'}.':<br />';
1.474 raeburn 3080: $output .= &start_data_table();
3081: $output .= '
3082: <tr>
3083: <th>'.$lt{'cour'}.'</th>
3084: <th>'.$lt{'dura'}.'</th>
3085: <th>'.$lt{'blse'}.'</th>
3086: </tr>
3087: ';
3088: foreach my $course (keys(%{$setters})) {
3089: my %courseinfo=&Apache::lonnet::coursedescription($course);
3090: for (my $i=0; $i<@{$$setters{$course}{staff}}; $i++) {
3091: my ($uname,$udom) = @{$$setters{$course}{staff}[$i]};
1.490 raeburn 3092: my $fullname = &plainname($uname,$udom);
3093: if (defined($env{'user.name'}) && defined($env{'user.domain'})
3094: && $env{'user.name'} ne 'public'
3095: && $env{'user.domain'} ne 'public') {
3096: $fullname = &aboutmewrapper($fullname,$uname,$udom);
3097: }
1.474 raeburn 3098: my ($openblock,$closeblock) = @{$$setters{$course}{times}[$i]};
3099: $openblock = &Apache::lonlocal::locallocaltime($openblock);
3100: $closeblock= &Apache::lonlocal::locallocaltime($closeblock);
3101: $output .= &Apache::loncommon::start_data_table_row().
3102: '<td>'.$courseinfo{'description'}.'</td>'.
3103: '<td>'.$openblock.' to '.$closeblock.'</td>'.
1.490 raeburn 3104: '<td>'.$fullname.'</td>'.
1.474 raeburn 3105: &Apache::loncommon::end_data_table_row();
3106: }
3107: }
3108: $output .= &end_data_table();
3109: }
3110:
1.490 raeburn 3111: sub blocking_status {
3112: my ($activity,$uname,$udom) = @_;
3113: my %setters;
3114: my ($blocked,$output,$ownitem,$is_course);
3115: my ($startblock,$endblock)=&blockcheck(\%setters,$activity,$uname,$udom);
3116: if ($startblock && $endblock) {
3117: $blocked = 1;
3118: if (wantarray) {
3119: my $category;
3120: if ($activity eq 'boards') {
3121: $category = 'Discussion posts in this course';
3122: } elsif ($activity eq 'blogs') {
3123: $category = 'Blogs';
3124: } elsif ($activity eq 'port') {
3125: if (defined($uname) && defined($udom)) {
3126: if ($uname eq $env{'user.name'} &&
3127: $udom eq $env{'user.domain'}) {
3128: $ownitem = 1;
3129: }
3130: }
3131: $is_course = &Apache::lonnet::is_course($udom,$uname);
3132: if ($ownitem) {
3133: $category = 'Your portfolio files';
3134: } elsif ($is_course) {
3135: my $coursedesc;
3136: foreach my $course (keys(%setters)) {
3137: my %courseinfo =
3138: &Apache::lonnet::coursedescription($course);
3139: $coursedesc = $courseinfo{'description'};
3140: }
3141: $category = "Group files in the course '$coursedesc'";
3142: } else {
3143: $category = 'Portfolio files belonging to ';
3144: if ($env{'user.name'} eq 'public' &&
3145: $env{'user.domain'} eq 'public') {
3146: $category .= &plainname($uname,$udom);
3147: } else {
3148: $category .= &aboutmewrapper(&plainname($uname,$udom),$uname,$udom);
3149: }
3150: }
3151: } elsif ($activity eq 'groups') {
3152: $category = 'Groups in this course';
3153: }
3154: my $showstart = &Apache::lonlocal::locallocaltime($startblock);
3155: my $showend = &Apache::lonlocal::locallocaltime($endblock);
3156: $output = '<br />'.&mt('[_1] will be inaccessible between [_2] and [_3] because communication is being blocked.',$category,$showstart,$showend).'<br />';
3157: if (!($activity eq 'port' && !($ownitem) && !($is_course))) {
3158: $output .= &build_block_table($startblock,$endblock,\%setters);
3159: }
3160: }
3161: }
3162: if (wantarray) {
3163: return ($blocked,$output);
3164: } else {
3165: return $blocked;
3166: }
3167: }
3168:
1.60 matthew 3169: ###############################################
3170:
3171: =pod
3172:
1.112 bowersj2 3173: =head1 Domain Template Functions
3174:
3175: =over 4
3176:
3177: =item * &determinedomain()
1.60 matthew 3178:
3179: Inputs: $domain (usually will be undef)
3180:
1.63 www 3181: Returns: Determines which domain should be used for designs
1.60 matthew 3182:
3183: =cut
1.54 www 3184:
1.60 matthew 3185: ###############################################
1.63 www 3186: sub determinedomain {
3187: my $domain=shift;
1.531 albertel 3188: if (! $domain) {
1.60 matthew 3189: # Determine domain if we have not been given one
3190: $domain = $Apache::lonnet::perlvar{'lonDefDomain'};
1.258 albertel 3191: if ($env{'user.domain'}) { $domain=$env{'user.domain'}; }
3192: if ($env{'request.role.domain'}) {
3193: $domain=$env{'request.role.domain'};
1.60 matthew 3194: }
3195: }
1.63 www 3196: return $domain;
3197: }
3198: ###############################################
1.517 raeburn 3199:
1.518 albertel 3200: sub devalidate_domconfig_cache {
3201: my ($udom)=@_;
3202: &Apache::lonnet::devalidate_cache_new('domainconfig',$udom);
3203: }
3204:
3205: # ---------------------- Get domain configuration for a domain
3206: sub get_domainconf {
3207: my ($udom) = @_;
3208: my $cachetime=1800;
3209: my ($result,$cached)=&Apache::lonnet::is_cached_new('domainconfig',$udom);
3210: if (defined($cached)) { return %{$result}; }
3211:
3212: my %domconfig = &Apache::lonnet::get_dom('configuration',
3213: ['login','rolecolors'],$udom);
3214: my %designhash;
3215: if (keys(%domconfig) > 0) {
3216: if (ref($domconfig{'login'}) eq 'HASH') {
3217: foreach my $key (keys(%{$domconfig{'login'}})) {
3218: $designhash{$udom.'.login.'.$key}=$domconfig{'login'}{$key};
3219: }
3220: }
3221: if (ref($domconfig{'rolecolors'}) eq 'HASH') {
3222: foreach my $role (keys(%{$domconfig{'rolecolors'}})) {
3223: if (ref($domconfig{'rolecolors'}{$role}) eq 'HASH') {
3224: foreach my $item (keys(%{$domconfig{'rolecolors'}{$role}})) {
3225: $designhash{$udom.'.'.$role.'.'.$item}=$domconfig{'rolecolors'}{$role}{$item};
3226: }
3227: }
3228: }
3229: }
3230: } else {
3231: my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors';
3232: my $designfile = $designdir.'/'.$udom.'.tab';
3233: if (-e $designfile) {
3234: if ( open (my $fh,"<$designfile") ) {
3235: while (my $line = <$fh>) {
3236: next if ($line =~ /^\#/);
3237: chomp($line);
3238: my ($key,$val)=(split(/\=/,$line));
3239: if ($val) { $designhash{$udom.'.'.$key}=$val; }
3240: }
3241: close($fh);
3242: }
3243: }
3244: if (-e '/home/httpd/html/adm/lonDomLogos/'.$udom.'.gif') {
1.519 raeburn 3245: $designhash{$udom.'.login.domlogo'} = "/adm/lonDomLogos/$udom.gif";
1.518 albertel 3246: }
3247: }
3248: &Apache::lonnet::do_cache_new('domainconfig',$udom,\%designhash,
3249: $cachetime);
3250: return %designhash;
3251: }
3252:
1.63 www 3253: =pod
3254:
1.112 bowersj2 3255: =item * &domainlogo()
1.63 www 3256:
3257: Inputs: $domain (usually will be undef)
3258:
3259: Returns: A link to a domain logo, if the domain logo exists.
3260: If the domain logo does not exist, a description of the domain.
3261:
3262: =cut
1.112 bowersj2 3263:
1.63 www 3264: ###############################################
3265: sub domainlogo {
1.517 raeburn 3266: my $domain = &determinedomain(shift);
1.518 albertel 3267: my %designhash = &get_domainconf($domain);
1.517 raeburn 3268: # See if there is a logo
3269: if ($designhash{$domain.'.login.domlogo'} ne '') {
1.519 raeburn 3270: my $imgsrc = $designhash{$domain.'.login.domlogo'};
1.538 albertel 3271: if ($imgsrc =~ m{^/(adm|res)/}) {
3272: if ($imgsrc =~ m{^/res/}) {
3273: my $local_name = &Apache::lonnet::filelocation('',$imgsrc);
3274: &Apache::lonnet::repcopy($local_name);
3275: }
3276: $imgsrc = &lonhttpdurl($imgsrc);
1.519 raeburn 3277: }
3278: return '<img src="'.$imgsrc.'" alt="'.$domain.'" />';
1.514 albertel 3279: } elsif (defined(&Apache::lonnet::domain($domain,'description'))) {
3280: return &Apache::lonnet::domain($domain,'description');
1.59 www 3281: } else {
1.60 matthew 3282: return '';
1.59 www 3283: }
3284: }
1.63 www 3285: ##############################################
3286:
3287: =pod
3288:
1.112 bowersj2 3289: =item * &designparm()
1.63 www 3290:
3291: Inputs: $which parameter; $domain (usually will be undef)
3292:
3293: Returns: value of designparamter $which
3294:
3295: =cut
1.112 bowersj2 3296:
1.397 albertel 3297:
1.400 albertel 3298: ##############################################
1.397 albertel 3299: sub designparm {
3300: my ($which,$domain)=@_;
1.258 albertel 3301: if ($env{'browser.blackwhite'} eq 'on') {
1.110 www 3302: if ($which=~/\.(font|alink|vlink|link)$/) {
3303: return '#000000';
3304: }
3305: if ($which=~/\.(pgbg|sidebg)$/) {
3306: return '#FFFFFF';
3307: }
3308: if ($which=~/\.tabbg$/) {
3309: return '#CCCCCC';
3310: }
3311: }
1.397 albertel 3312: if (exists($env{'environment.color.'.$which})) {
1.258 albertel 3313: return $env{'environment.color.'.$which};
1.96 www 3314: }
1.63 www 3315: $domain=&determinedomain($domain);
1.518 albertel 3316: my %domdesign = &get_domainconf($domain);
1.520 raeburn 3317: my $output;
1.517 raeburn 3318: if ($domdesign{$domain.'.'.$which} ne '') {
1.520 raeburn 3319: $output = $domdesign{$domain.'.'.$which};
1.63 www 3320: } else {
1.520 raeburn 3321: $output = $defaultdesign{$which};
3322: }
3323: if (($which =~ /^(student|coordinator|author|admin)\.img$/) ||
3324: ($which =~ /login\.(img|logo|domlogo)/)) {
1.538 albertel 3325: if ($output =~ m{^/(adm|res)/}) {
3326: if ($output =~ m{^/res/}) {
3327: my $local_name = &Apache::lonnet::filelocation('',$output);
3328: &Apache::lonnet::repcopy($local_name);
3329: }
1.520 raeburn 3330: $output = &lonhttpdurl($output);
3331: }
1.63 www 3332: }
1.520 raeburn 3333: return $output;
1.63 www 3334: }
1.59 www 3335:
1.60 matthew 3336: ###############################################
3337: ###############################################
3338:
3339: =pod
3340:
1.112 bowersj2 3341: =back
3342:
1.549 albertel 3343: =head1 HTML Helpers
1.112 bowersj2 3344:
3345: =over 4
3346:
3347: =item * &bodytag()
1.60 matthew 3348:
3349: Returns a uniform header for LON-CAPA web pages.
3350:
3351: Inputs:
3352:
1.112 bowersj2 3353: =over 4
3354:
3355: =item * $title, A title to be displayed on the page.
3356:
3357: =item * $function, the current role (can be undef).
3358:
3359: =item * $addentries, extra parameters for the <body> tag.
3360:
3361: =item * $bodyonly, if defined, only return the <body> tag.
3362:
3363: =item * $domain, if defined, force a given domain.
3364:
3365: =item * $forcereg, if page should register as content page (relevant for
1.86 www 3366: text interface only)
1.60 matthew 3367:
1.326 albertel 3368: =item * $customtitle, alternate text to use instead of $title
3369: in the title box that appears, this text
3370: is not auto translated like the $title is
1.309 albertel 3371:
3372: =item * $notopbar, if true, keep the 'what is this' info but remove the
3373: navigational links
1.317 albertel 3374:
1.338 albertel 3375: =item * $bgcolor, used to override the bgcolor on a webpage to a specific value
3376:
3377: =item * $notitle, if true keep the nav controls, but remove the title bar
3378:
1.361 albertel 3379: =item * $no_inline_link, if true and in remote mode, don't show the
3380: 'Switch To Inline Menu' link
3381:
1.460 albertel 3382: =item * $args, optional argument valid values are
3383: no_auto_mt_title -> prevents &mt()ing the title arg
3384:
1.112 bowersj2 3385: =back
3386:
1.60 matthew 3387: Returns: A uniform header for LON-CAPA web pages.
3388: If $bodyonly is nonzero, a string containing a <body> tag will be returned.
3389: If $bodyonly is undef or zero, an html string containing a <body> tag and
3390: other decorations will be returned.
3391:
3392: =cut
3393:
1.54 www 3394: sub bodytag {
1.309 albertel 3395: my ($title,$function,$addentries,$bodyonly,$domain,$forcereg,$customtitle,
1.460 albertel 3396: $notopbar,$bgcolor,$notitle,$no_inline_link,$args)=@_;
1.339 albertel 3397:
1.460 albertel 3398: if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
1.339 albertel 3399:
1.183 matthew 3400: $function = &get_users_function() if (!$function);
1.339 albertel 3401: my $img = &designparm($function.'.img',$domain);
3402: my $font = &designparm($function.'.font',$domain);
3403: my $pgbg = $bgcolor || &designparm($function.'.pgbg',$domain);
3404:
3405: my %design = ( 'style' => 'margin-top: 0px',
1.535 albertel 3406: 'bgcolor' => $pgbg,
1.339 albertel 3407: 'text' => $font,
3408: 'alink' => &designparm($function.'.alink',$domain),
3409: 'vlink' => &designparm($function.'.vlink',$domain),
3410: 'link' => &designparm($function.'.link',$domain),);
1.438 albertel 3411: @design{keys(%$addentries)} = @$addentries{keys(%$addentries)};
1.339 albertel 3412:
1.63 www 3413: # role and realm
1.378 raeburn 3414: my ($role,$realm) = split(/\./,$env{'request.role'},2);
3415: if ($role eq 'ca') {
1.479 albertel 3416: my ($rdom,$rname) = ($realm =~ m{^/($match_domain)/($match_username)$});
1.500 albertel 3417: $realm = &plainname($rname,$rdom);
1.378 raeburn 3418: }
1.55 www 3419: # realm
1.258 albertel 3420: if ($env{'request.course.id'}) {
1.378 raeburn 3421: if ($env{'request.role'} !~ /^cr/) {
3422: $role = &Apache::lonnet::plaintext($role,&course_type());
3423: }
1.359 albertel 3424: $realm = $env{'course.'.$env{'request.course.id'}.'.description'};
1.378 raeburn 3425: } else {
3426: $role = &Apache::lonnet::plaintext($role);
1.54 www 3427: }
1.433 albertel 3428:
1.359 albertel 3429: if (!$realm) { $realm=' '; }
1.55 www 3430: # Set messages
1.60 matthew 3431: my $messages=&domainlogo($domain);
1.101 www 3432: # Port for miniserver
1.83 albertel 3433: my $lonhttpdPort=$Apache::lonnet::perlvar{'lonhttpdPort'};
3434: if (!defined($lonhttpdPort)) { $lonhttpdPort='8080'; }
1.330 albertel 3435:
1.438 albertel 3436: my $extra_body_attr = &make_attr_string($forcereg,\%design);
1.329 albertel 3437:
1.101 www 3438: # construct main body tag
1.359 albertel 3439: my $bodytag = "<body $extra_body_attr>".
3440: &Apache::lontexconvert::init_math_support();
1.252 albertel 3441:
1.530 albertel 3442: if ($bodyonly) {
1.60 matthew 3443: return $bodytag;
1.258 albertel 3444: } elsif ($env{'browser.interface'} eq 'textual') {
1.95 www 3445: # Accessibility
1.224 raeburn 3446:
1.337 albertel 3447: $bodytag.=&Apache::lonmenu::menubuttons($forcereg,$forcereg);
1.338 albertel 3448: if (!$notitle) {
1.337 albertel 3449: $bodytag.='<h1>LON-CAPA: '.$title.'</h1>';
3450: }
3451: return $bodytag;
1.359 albertel 3452: }
3453:
1.410 albertel 3454: my $name = &plainname($env{'user.name'},$env{'user.domain'});
1.433 albertel 3455: if ($env{'user.name'} eq 'public' && $env{'user.domain'} eq 'public') {
3456: undef($role);
1.434 albertel 3457: } else {
3458: $name = &aboutmewrapper($name,$env{'user.name'},$env{'user.domain'});
1.433 albertel 3459: }
1.359 albertel 3460:
3461: my $roleinfo=(<<ENDROLE);
3462: <td class="LC_title_bar_who">
3463: <div class="LC_title_bar_name">
1.410 albertel 3464: $name
1.361 albertel 3465:
1.359 albertel 3466: </div>
3467: <div class="LC_title_bar_role">
1.361 albertel 3468: $role
1.359 albertel 3469: </div>
3470: <div class="LC_title_bar_realm">
1.361 albertel 3471: $realm
1.359 albertel 3472: </div>
1.206 albertel 3473: </td>
3474: ENDROLE
1.235 raeburn 3475:
1.359 albertel 3476: my $titleinfo = '<span class="LC_title_bar_title">'.$title.'</span>';
3477: if ($customtitle) {
3478: $titleinfo = $customtitle;
3479: }
3480: #
3481: # Extra info if you are the DC
3482: my $dc_info = '';
3483: if ($env{'user.adv'} && exists($env{'user.role.dc./'.
3484: $env{'course.'.$env{'request.course.id'}.
3485: '.domain'}.'/'})) {
3486: my $cid = $env{'request.course.id'};
3487: $dc_info.= $cid.' '.$env{'course.'.$cid.'.internal.coursecode'};
1.380 www 3488: $dc_info =~ s/\s+$//;
1.359 albertel 3489: $dc_info = '('.$dc_info.')';
3490: }
3491:
3492: if ($env{'environment.remote'} eq 'off') {
3493: # No Remote
1.258 albertel 3494: if ($env{'request.state'} eq 'construct') {
1.359 albertel 3495: $forcereg=1;
3496: }
3497:
3498: if (!$customtitle && $env{'request.state'} eq 'construct') {
3499: # this is for resources; directories have customtitle, and crumbs
3500: # and select recent are created in lonpubdir.pm
1.229 albertel 3501: my ($uname,$thisdisfn)=
1.258 albertel 3502: ($env{'request.filename'} =~ m|^/home/([^/]+)/public_html/(.*)|);
1.229 albertel 3503: my $formaction='/priv/'.$uname.'/'.$thisdisfn;
3504: $formaction=~s/\/+/\//g;
3505:
1.359 albertel 3506: my $parentpath = '';
3507: my $lastitem = '';
3508: if ($thisdisfn =~ m-(.+/)([^/]*)$-) {
3509: $parentpath = $1;
3510: $lastitem = $2;
3511: } else {
3512: $lastitem = $thisdisfn;
3513: }
3514: $titleinfo =
1.401 albertel 3515: &Apache::loncommon::help_open_menu('','',3,'Authoring').
1.359 albertel 3516: '<b>Construction Space</b>: '.
3517: '<form name="dirs" method="post" action="'.$formaction
3518: .'" target="_top"><tt><b>'
3519: .&Apache::lonhtmlcommon::crumbs($uname.'/'.$parentpath,'_top','/priv','','+1',1)."<font size=\"+1\">$lastitem</font></b></tt><br />"
3520: .&Apache::lonhtmlcommon::select_recent('construct','recent','this.form.action=this.form.recent.value;this.form.submit()')
3521: .'</form>'
3522: .&Apache::lonmenu::constspaceform();
1.235 raeburn 3523: }
1.359 albertel 3524:
1.337 albertel 3525: my $titletable;
1.338 albertel 3526: if (!$notitle) {
1.337 albertel 3527: $titletable =
1.359 albertel 3528: '<table id="LC_title_bar">'.
3529: "<tr><td> $titleinfo $dc_info</td>".$roleinfo.
3530: '</tr></table>';
1.337 albertel 3531: }
1.359 albertel 3532: if ($notopbar) {
3533: $bodytag .= $titletable;
3534: } else {
3535: if ($env{'request.state'} eq 'construct') {
1.337 albertel 3536: $bodytag .= &Apache::lonmenu::menubuttons($forcereg,$forcereg,
3537: $titletable);
1.272 raeburn 3538: } else {
1.336 albertel 3539: $bodytag .= &Apache::lonmenu::menubuttons($forcereg,$forcereg).
1.359 albertel 3540: $titletable;
1.272 raeburn 3541: }
1.235 raeburn 3542: }
3543: return $bodytag;
1.94 www 3544: }
1.95 www 3545:
1.93 www 3546: #
1.95 www 3547: # Top frame rendering, Remote is up
1.93 www 3548: #
1.359 albertel 3549:
1.517 raeburn 3550: my $imgsrc = $img;
3551: if ($img =~ /^\/adm/) {
3552: $imgsrc = 'http://'.$ENV{'HTTP_HOST'}.':'.$lonhttpdPort.$img;
3553: }
3554: my $upperleft='<img src="'.$imgsrc.'" alt="'.$function.'" />';
1.359 albertel 3555:
1.305 www 3556: # Explicit link to get inline menu
1.361 albertel 3557: my $menu= ($no_inline_link?''
3558: :'<br /><a href="/adm/remote?action=collapse">'.&mt('Switch to Inline Menu Mode').'</a>');
1.245 matthew 3559: #
1.338 albertel 3560: if ($notitle) {
1.337 albertel 3561: return $bodytag;
3562: }
1.94 www 3563: return(<<ENDBODY);
1.60 matthew 3564: $bodytag
1.359 albertel 3565: <table id="LC_title_bar" class="LC_with_remote">
1.368 albertel 3566: <tr><td class="LC_title_bar_role_logo">$upperleft</td>
1.359 albertel 3567: <td class="LC_title_bar_domain_logo">$messages </td>
1.54 www 3568: </tr>
1.359 albertel 3569: <tr><td>$titleinfo $dc_info $menu</td>
3570: $roleinfo
1.368 albertel 3571: </tr>
1.356 albertel 3572: </table>
1.54 www 3573: ENDBODY
1.182 matthew 3574: }
3575:
1.330 albertel 3576: sub make_attr_string {
3577: my ($register,$attr_ref) = @_;
3578:
3579: if ($attr_ref && !ref($attr_ref)) {
3580: die("addentries Must be a hash ref ".
3581: join(':',caller(1))." ".
3582: join(':',caller(0))." ");
3583: }
3584:
3585: if ($register) {
1.339 albertel 3586: my ($on_load,$on_unload);
3587: foreach my $key (keys(%{$attr_ref})) {
3588: if (lc($key) eq 'onload') {
3589: $on_load.=$attr_ref->{$key}.';';
3590: delete($attr_ref->{$key});
3591:
3592: } elsif (lc($key) eq 'onunload') {
3593: $on_unload.=$attr_ref->{$key}.';';
3594: delete($attr_ref->{$key});
3595: }
3596: }
3597: $attr_ref->{'onload'} =
3598: &Apache::lonmenu::loadevents(). $on_load;
3599: $attr_ref->{'onunload'}=
3600: &Apache::lonmenu::unloadevents().$on_unload;
3601: }
3602:
3603: # Accessibility font enhance
3604: if ($env{'browser.fontenhance'} eq 'on') {
3605: my $style;
3606: foreach my $key (keys(%{$attr_ref})) {
3607: if (lc($key) eq 'style') {
3608: $style.=$attr_ref->{$key}.';';
3609: delete($attr_ref->{$key});
3610: }
3611: }
3612: $attr_ref->{'style'}=$style.'; font-size: x-large;';
1.330 albertel 3613: }
1.339 albertel 3614:
3615: if ($env{'browser.blackwhite'} eq 'on') {
3616: delete($attr_ref->{'font'});
3617: delete($attr_ref->{'link'});
3618: delete($attr_ref->{'alink'});
3619: delete($attr_ref->{'vlink'});
3620: delete($attr_ref->{'bgcolor'});
3621: delete($attr_ref->{'background'});
3622: }
3623:
1.330 albertel 3624: my $attr_string;
3625: foreach my $attr (keys(%$attr_ref)) {
3626: $attr_string .= " $attr=\"".$attr_ref->{$attr}.'" ';
3627: }
3628: return $attr_string;
3629: }
3630:
3631:
1.182 matthew 3632: ###############################################
1.251 albertel 3633: ###############################################
3634:
3635: =pod
3636:
3637: =item * &endbodytag()
3638:
3639: Returns a uniform footer for LON-CAPA web pages.
3640:
1.306 albertel 3641: Inputs: none
1.251 albertel 3642:
3643: =cut
3644:
3645: sub endbodytag {
3646: my $endbodytag='</body>';
1.269 albertel 3647: $endbodytag=&Apache::lontexconvert::jsMath_process()."\n".$endbodytag;
1.315 albertel 3648: if ( exists( $env{'internal.head.redirect'} ) ) {
3649: $endbodytag=
3650: "<br /><a href=\"$env{'internal.head.redirect'}\">".
3651: &mt('Continue').'</a>'.
3652: $endbodytag;
3653: }
1.251 albertel 3654: return $endbodytag;
3655: }
3656:
1.352 albertel 3657: =pod
3658:
3659: =item * &standard_css()
3660:
3661: Returns a style sheet
3662:
3663: Inputs: (all optional)
3664: domain -> force to color decorate a page for a specific
3665: domain
3666: function -> force usage of a specific rolish color scheme
3667: bgcolor -> override the default page bgcolor
3668:
3669: =cut
3670:
1.343 albertel 3671: sub standard_css {
1.345 albertel 3672: my ($function,$domain,$bgcolor) = @_;
1.352 albertel 3673: $function = &get_users_function() if (!$function);
3674: my $img = &designparm($function.'.img', $domain);
3675: my $tabbg = &designparm($function.'.tabbg', $domain);
3676: my $font = &designparm($function.'.font', $domain);
1.345 albertel 3677: my $sidebg = &designparm($function.'.sidebg',$domain);
1.382 albertel 3678: my $pgbg_or_bgcolor =
3679: $bgcolor ||
1.352 albertel 3680: &designparm($function.'.pgbg', $domain);
1.382 albertel 3681: my $pgbg = &designparm($function.'.pgbg', $domain);
1.352 albertel 3682: my $alink = &designparm($function.'.alink', $domain);
3683: my $vlink = &designparm($function.'.vlink', $domain);
3684: my $link = &designparm($function.'.link', $domain);
3685:
3686: my $sans = 'Arial,Helvetica,sans-serif';
1.395 albertel 3687: my $mono = 'monospace';
1.352 albertel 3688: my $data_table_head = $tabbg;
3689: my $data_table_light = '#EEEEEE';
1.470 banghart 3690: my $data_table_dark = '#DDDDDD';
3691: my $data_table_darker = '#CCCCCC';
1.349 albertel 3692: my $data_table_highlight = '#FFFF00';
1.352 albertel 3693: my $mail_new = '#FFBB77';
3694: my $mail_new_hover = '#DD9955';
3695: my $mail_read = '#BBBB77';
3696: my $mail_read_hover = '#999944';
3697: my $mail_replied = '#AAAA88';
3698: my $mail_replied_hover = '#888855';
3699: my $mail_other = '#99BBBB';
3700: my $mail_other_hover = '#669999';
1.391 albertel 3701: my $table_header = '#DDDDDD';
1.489 raeburn 3702: my $feedback_link_bg = '#BBBBBB';
1.392 albertel 3703:
3704: my $border = ($env{'browser.type'} eq 'explorer') ? '0px 2px 0px 2px'
3705: : '0px 3px 0px 4px';
1.448 albertel 3706:
1.523 albertel 3707:
1.343 albertel 3708: return <<END;
1.345 albertel 3709: h1, h2, h3, th { font-family: $sans }
1.343 albertel 3710: a:focus { color: red; background: yellow }
1.510 albertel 3711: table.thinborder,
1.523 albertel 3712:
1.510 albertel 3713: table.thinborder tr th {
3714: border-style: solid;
3715: border-width: 1px;
3716: background: $tabbg;
3717: }
1.523 albertel 3718: table.thinborder tr td {
1.510 albertel 3719: border-style: solid;
3720: border-width: 1px
3721: }
1.426 albertel 3722:
1.343 albertel 3723: form, .inline { display: inline; }
3724: .center { text-align: center; }
1.395 albertel 3725: .LC_filename {font-family: $mono;}
1.350 albertel 3726: .LC_error {
3727: color: red;
3728: font-size: larger;
3729: }
1.457 albertel 3730: .LC_warning,
3731: .LC_diff_removed {
1.394 albertel 3732: color: red;
3733: }
1.532 albertel 3734:
3735: .LC_info,
1.457 albertel 3736: .LC_success,
3737: .LC_diff_added {
1.350 albertel 3738: color: green;
3739: }
1.543 albertel 3740: .LC_unknown {
3741: color: yellow;
3742: }
3743:
1.440 albertel 3744: .LC_icon {
3745: border: 0px;
3746: }
1.539 albertel 3747: .LC_indexer_icon {
3748: border: 0px;
3749: height: 22px;
3750: }
1.543 albertel 3751: .LC_docs_spacer {
3752: width: 25px;
3753: height: 1px;
3754: border: 0px;
3755: }
1.346 albertel 3756:
1.532 albertel 3757: .LC_internal_info {
3758: color: #999;
3759: }
3760:
1.458 albertel 3761: table.LC_pastsubmission {
3762: border: 1px solid black;
3763: margin: 2px;
3764: }
3765:
1.392 albertel 3766: table#LC_top_nav, table#LC_menubuttons {
1.345 albertel 3767: width: 100%;
3768: background: $pgbg;
1.392 albertel 3769: border: 2px;
1.402 albertel 3770: border-collapse: separate;
1.403 albertel 3771: padding: 0px;
1.345 albertel 3772: }
1.392 albertel 3773:
1.409 albertel 3774: table#LC_title_bar, table.LC_breadcrumbs, table#LC_nav_location,
1.393 albertel 3775: table#LC_title_bar.LC_with_remote {
1.359 albertel 3776: width: 100%;
1.392 albertel 3777: border-color: $pgbg;
3778: border-style: solid;
3779: border-width: $border;
3780:
1.379 albertel 3781: background: $pgbg;
3782: font-family: $sans;
1.392 albertel 3783: border-collapse: collapse;
1.403 albertel 3784: padding: 0px;
1.359 albertel 3785: }
1.392 albertel 3786:
1.409 albertel 3787: table.LC_docs_path {
3788: width: 100%;
3789: border: 0;
3790: background: $pgbg;
3791: font-family: $sans;
3792: border-collapse: collapse;
3793: padding: 0px;
3794: }
3795:
1.359 albertel 3796: table#LC_title_bar td {
3797: background: $tabbg;
3798: }
3799: table#LC_title_bar td.LC_title_bar_who {
3800: background: $tabbg;
3801: color: $font;
1.427 albertel 3802: font: small $sans;
1.359 albertel 3803: text-align: right;
3804: }
1.469 banghart 3805: span.LC_metadata {
3806: font-family: $sans;
3807: }
1.359 albertel 3808: span.LC_title_bar_title {
1.416 albertel 3809: font: bold x-large $sans;
1.359 albertel 3810: }
3811: table#LC_title_bar td.LC_title_bar_domain_logo {
3812: background: $sidebg;
3813: text-align: right;
1.368 albertel 3814: padding: 0px;
3815: }
3816: table#LC_title_bar td.LC_title_bar_role_logo {
3817: background: $sidebg;
3818: padding: 0px;
1.359 albertel 3819: }
3820:
1.346 albertel 3821: table#LC_menubuttons_mainmenu {
1.526 www 3822: width: 100%;
1.346 albertel 3823: border: 0px;
3824: border-spacing: 1px;
1.372 albertel 3825: padding: 0px 1px;
1.346 albertel 3826: margin: 0px;
3827: border-collapse: separate;
3828: }
3829: table#LC_menubuttons img, table#LC_menubuttons_mainmenu img {
3830: border: 0px;
3831: }
1.345 albertel 3832: table#LC_top_nav td {
3833: background: $tabbg;
1.392 albertel 3834: border: 0px;
1.407 albertel 3835: font-size: small;
1.345 albertel 3836: }
3837: table#LC_top_nav td a, div#LC_top_nav a {
3838: color: $font;
3839: font-family: $sans;
3840: }
1.364 albertel 3841: table#LC_top_nav td.LC_top_nav_logo {
3842: background: $tabbg;
1.432 albertel 3843: text-align: left;
1.408 albertel 3844: white-space: nowrap;
1.432 albertel 3845: width: 31px;
1.408 albertel 3846: }
3847: table#LC_top_nav td.LC_top_nav_logo img {
1.432 albertel 3848: border: 0px;
1.408 albertel 3849: vertical-align: bottom;
1.364 albertel 3850: }
1.432 albertel 3851: table#LC_top_nav td.LC_top_nav_exit,
3852: table#LC_top_nav td.LC_top_nav_help {
3853: width: 2.0em;
3854: }
1.442 albertel 3855: table#LC_top_nav td.LC_top_nav_login {
3856: width: 4.0em;
3857: text-align: center;
3858: }
1.409 albertel 3859: table.LC_breadcrumbs td, table.LC_docs_path td {
1.357 albertel 3860: background: $tabbg;
3861: color: $font;
3862: font-family: $sans;
1.358 albertel 3863: font-size: smaller;
1.357 albertel 3864: }
1.411 albertel 3865: table.LC_breadcrumbs td.LC_breadcrumbs_component,
1.409 albertel 3866: table.LC_docs_path td.LC_docs_path_component {
1.357 albertel 3867: background: $tabbg;
3868: color: $font;
3869: font-family: $sans;
3870: font-size: larger;
3871: text-align: right;
3872: }
1.383 albertel 3873: td.LC_table_cell_checkbox {
3874: text-align: center;
3875: }
3876:
1.522 albertel 3877: table#LC_mainmenu td.LC_mainmenu_column {
3878: vertical-align: top;
3879: }
3880:
1.346 albertel 3881: .LC_menubuttons_inline_text {
3882: color: $font;
3883: font-family: $sans;
3884: font-size: smaller;
3885: }
3886:
1.526 www 3887: .LC_menubuttons_link {
3888: text-decoration: none;
3889: }
3890:
1.522 albertel 3891: .LC_menubuttons_category {
1.521 www 3892: color: $font;
1.526 www 3893: background: $pgbg;
1.521 www 3894: font-family: $sans;
3895: font-size: larger;
3896: font-weight: bold;
3897: }
3898:
1.346 albertel 3899: td.LC_menubuttons_text {
1.526 www 3900: width: 90%;
1.346 albertel 3901: color: $font;
3902: font-family: $sans;
3903: }
1.526 www 3904:
1.346 albertel 3905: td.LC_menubuttons_img {
3906: }
1.526 www 3907:
1.346 albertel 3908: .LC_current_location {
3909: font-family: $sans;
3910: background: $tabbg;
3911: }
3912: .LC_new_mail {
3913: font-family: $sans;
3914: font-weight: bold;
3915: }
1.347 albertel 3916:
1.526 www 3917: .LC_rolesmenu_is {
3918: font-family: $sans;
3919: }
3920:
3921: .LC_rolesmenu_selected {
3922: font-family: $sans;
3923: }
3924:
3925: .LC_rolesmenu_future {
3926: font-family: $sans;
3927: }
3928:
3929:
3930: .LC_rolesmenu_will {
3931: font-family: $sans;
3932: }
3933:
3934: .LC_rolesmenu_will_not {
3935: font-family: $sans;
3936: }
3937:
3938: .LC_rolesmenu_expired {
3939: font-family: $sans;
3940: }
3941:
3942: .LC_rolesinfo {
3943: font-family: $sans;
3944: }
3945:
1.527 www 3946: .LC_dropadd_labeltext {
3947: font-family: $sans;
3948: text-align: right;
3949: }
3950:
3951: .LC_preferences_labeltext {
3952: font-family: $sans;
3953: text-align: right;
3954: }
3955:
1.440 albertel 3956: table.LC_aboutme_port {
3957: border: 0px;
3958: border-collapse: collapse;
3959: border-spacing: 0px;
3960: }
1.349 albertel 3961: table.LC_data_table, table.LC_mail_list {
1.347 albertel 3962: border: 1px solid #000000;
1.402 albertel 3963: border-collapse: separate;
1.426 albertel 3964: border-spacing: 1px;
1.347 albertel 3965: }
1.422 albertel 3966: .LC_data_table_dense {
3967: font-size: small;
3968: }
1.507 raeburn 3969: table.LC_nested_outer {
3970: border: 1px solid #000000;
3971: border-collapse: separate;
3972: border-spacing: 0px;
3973: width: 100%;
3974: }
3975: table.LC_nested {
3976: border: 0px;
3977: border-collapse: separate;
3978: border-spacing: 0px;
3979: width: 100%;
3980: }
1.523 albertel 3981: table.LC_data_table tr th, table.LC_calendar tr th, table.LC_mail_list tr th,
3982: table.LC_prior_tries tr th {
1.349 albertel 3983: font-weight: bold;
3984: background-color: $data_table_head;
1.421 albertel 3985: font-size: smaller;
1.347 albertel 3986: }
1.440 albertel 3987: table.LC_data_table tr td,
3988: table.LC_aboutme_port tr td {
1.349 albertel 3989: background-color: $data_table_light;
1.425 albertel 3990: padding: 2px;
1.347 albertel 3991: }
1.440 albertel 3992: table.LC_data_table tr.LC_even_row td,
3993: table.LC_aboutme_port tr.LC_even_row td {
1.349 albertel 3994: background-color: $data_table_dark;
1.347 albertel 3995: }
1.425 albertel 3996: table.LC_data_table tr.LC_data_table_highlight td {
3997: background-color: $data_table_darker;
3998: }
1.451 albertel 3999: table.LC_data_table tr.LC_empty_row td,
1.507 raeburn 4000: table.LC_nested tr.LC_empty_row td {
1.347 albertel 4001: background-color: #FFFFFF;
1.421 albertel 4002: font-weight: bold;
4003: font-style: italic;
4004: text-align: center;
4005: padding: 8px;
1.347 albertel 4006: }
1.507 raeburn 4007: table.LC_nested tr.LC_empty_row td {
1.465 albertel 4008: padding: 4ex
4009: }
1.507 raeburn 4010: table.LC_nested_outer tr th {
4011: font-weight: bold;
4012: background-color: $data_table_head;
4013: font-size: smaller;
4014: border-bottom: 1px solid #000000;
4015: }
4016: table.LC_nested_outer tr td.LC_subheader {
4017: background-color: $data_table_head;
4018: font-weight: bold;
4019: font-size: small;
4020: border-bottom: 1px solid #000000;
4021: text-align: right;
1.451 albertel 4022: }
1.507 raeburn 4023: table.LC_nested tr.LC_info_row td {
1.451 albertel 4024: background-color: #CCC;
4025: font-weight: bold;
4026: font-size: small;
1.507 raeburn 4027: text-align: center;
4028: }
4029: table.LC_nested tr.LC_info_row td.LC_left_item {
4030: text-align: left;
1.451 albertel 4031: }
1.507 raeburn 4032: table.LC_nested td {
1.451 albertel 4033: background-color: #FFF;
4034: font-size: small;
1.507 raeburn 4035: }
4036: table.LC_nested_outer tr th.LC_right_item,
4037: table.LC_nested tr.LC_info_row td.LC_right_item,
4038: table.LC_nested tr.LC_odd_row td.LC_right_item,
4039: table.LC_nested tr td.LC_right_item {
1.451 albertel 4040: text-align: right;
4041: }
4042:
1.507 raeburn 4043: table.LC_nested tr.LC_odd_row td {
1.451 albertel 4044: background-color: #EEE;
4045: }
4046:
1.473 raeburn 4047: table.LC_createuser {
4048: }
4049:
4050: table.LC_createuser tr.LC_section_row td {
4051: font-size: smaller;
4052: }
4053:
4054: table.LC_createuser tr.LC_info_row td {
4055: background-color: #CCC;
4056: font-weight: bold;
4057: text-align: center;
4058: }
4059:
1.349 albertel 4060: table.LC_calendar {
4061: border: 1px solid #000000;
4062: border-collapse: collapse;
4063: }
4064: table.LC_calendar_pickdate {
4065: font-size: xx-small;
4066: }
4067: table.LC_calendar tr td {
4068: border: 1px solid #000000;
4069: vertical-align: top;
4070: }
4071: table.LC_calendar tr td.LC_calendar_day_empty {
4072: background-color: $data_table_dark;
4073: }
4074: table.LC_calendar tr td.LC_calendar_day_current {
4075: background-color: $data_table_highlight;
4076: }
4077:
4078: table.LC_mail_list tr.LC_mail_new {
4079: background-color: $mail_new;
4080: }
4081: table.LC_mail_list tr.LC_mail_new:hover {
4082: background-color: $mail_new_hover;
4083: }
4084: table.LC_mail_list tr.LC_mail_read {
4085: background-color: $mail_read;
4086: }
4087: table.LC_mail_list tr.LC_mail_read:hover {
4088: background-color: $mail_read_hover;
4089: }
4090: table.LC_mail_list tr.LC_mail_replied {
4091: background-color: $mail_replied;
4092: }
4093: table.LC_mail_list tr.LC_mail_replied:hover {
4094: background-color: $mail_replied_hover;
4095: }
4096: table.LC_mail_list tr.LC_mail_other {
4097: background-color: $mail_other;
4098: }
4099: table.LC_mail_list tr.LC_mail_other:hover {
4100: background-color: $mail_other_hover;
4101: }
1.494 raeburn 4102: table.LC_mail_list tr.LC_mail_even {
4103: }
4104: table.LC_mail_list tr.LC_mail_odd {
4105: }
4106:
1.385 albertel 4107:
1.386 albertel 4108: table#LC_portfolio_actions {
4109: width: auto;
4110: background: $pgbg;
4111: border: 0px;
4112: border-spacing: 2px 2px;
4113: padding: 0px;
4114: margin: 0px;
4115: border-collapse: separate;
4116: }
4117: table#LC_portfolio_actions td.LC_label {
4118: background: $tabbg;
4119: text-align: right;
4120: }
4121: table#LC_portfolio_actions td.LC_value {
4122: background: $tabbg;
4123: }
1.385 albertel 4124:
1.391 albertel 4125: table#LC_cstr_controls {
4126: width: 100%;
4127: border-collapse: collapse;
4128: }
4129: table#LC_cstr_controls tr td {
4130: border: 4px solid $pgbg;
4131: padding: 4px;
4132: text-align: center;
4133: background: $tabbg;
4134: }
4135: table#LC_cstr_controls tr th {
4136: border: 4px solid $pgbg;
4137: background: $table_header;
4138: text-align: center;
4139: font-family: $sans;
4140: font-size: smaller;
4141: }
4142:
1.389 albertel 4143: table#LC_browser {
4144:
4145: }
4146: table#LC_browser tr th {
1.391 albertel 4147: background: $table_header;
1.389 albertel 4148: }
1.390 albertel 4149: table#LC_browser tr td {
4150: padding: 2px;
4151: }
1.389 albertel 4152: table#LC_browser tr.LC_browser_file,
4153: table#LC_browser tr.LC_browser_file_published {
4154: background: #CCFF88;
4155: }
4156: table#LC_browser tr.LC_browser_file_locked,
4157: table#LC_browser tr.LC_browser_file_unpublished {
4158: background: #FFAA99;
1.387 albertel 4159: }
1.389 albertel 4160: table#LC_browser tr.LC_browser_file_obsolete {
4161: background: #AAAAAA;
1.387 albertel 4162: }
1.455 albertel 4163: table#LC_browser tr.LC_browser_file_modified,
4164: table#LC_browser tr.LC_browser_file_metamodified {
1.389 albertel 4165: background: #FFFF77;
1.387 albertel 4166: }
1.389 albertel 4167: table#LC_browser tr.LC_browser_folder {
4168: background: #CCCCFF;
1.387 albertel 4169: }
1.388 albertel 4170: span.LC_current_location {
4171: font-size: x-large;
4172: background: $pgbg;
4173: }
1.387 albertel 4174:
1.395 albertel 4175: span.LC_parm_menu_item {
4176: font-size: larger;
4177: font-family: $sans;
4178: }
4179: span.LC_parm_scope_all {
4180: color: red;
4181: }
4182: span.LC_parm_scope_folder {
4183: color: green;
4184: }
4185: span.LC_parm_scope_resource {
4186: color: orange;
4187: }
4188: span.LC_parm_part {
4189: color: blue;
4190: }
4191: span.LC_parm_folder, span.LC_parm_symb {
4192: font-size: x-small;
4193: font-family: $mono;
4194: color: #AAAAAA;
4195: }
4196:
1.396 albertel 4197: td.LC_parm_overview_level_menu, td.LC_parm_overview_map_menu,
4198: td.LC_parm_overview_parm_selectors, td.LC_parm_overview_parm_restrictions {
4199: border: 1px solid black;
4200: border-collapse: collapse;
4201: }
4202: table.LC_parm_overview_restrictions td {
4203: border-width: 1px 4px 1px 4px;
4204: border-style: solid;
4205: border-color: $pgbg;
4206: text-align: center;
4207: }
4208: table.LC_parm_overview_restrictions th {
4209: background: $tabbg;
4210: border-width: 1px 4px 1px 4px;
4211: border-style: solid;
4212: border-color: $pgbg;
4213: }
1.398 albertel 4214: table#LC_helpmenu {
4215: border: 0px;
4216: height: 55px;
4217: border-spacing: 0px;
4218: }
4219:
4220: table#LC_helpmenu fieldset legend {
4221: font-size: larger;
4222: font-weight: bold;
4223: }
1.397 albertel 4224: table#LC_helpmenu_links {
4225: width: 100%;
4226: border: 1px solid black;
4227: background: $pgbg;
4228: padding: 0px;
4229: border-spacing: 1px;
4230: }
4231: table#LC_helpmenu_links tr td {
4232: padding: 1px;
4233: background: $tabbg;
1.399 albertel 4234: text-align: center;
4235: font-weight: bold;
1.397 albertel 4236: }
1.396 albertel 4237:
1.397 albertel 4238: table#LC_helpmenu_links a:link, table#LC_helpmenu_links a:visited,
4239: table#LC_helpmenu_links a:active {
4240: text-decoration: none;
4241: color: $font;
4242: }
4243: table#LC_helpmenu_links a:hover {
4244: text-decoration: underline;
4245: color: $vlink;
4246: }
1.396 albertel 4247:
1.417 albertel 4248: .LC_chrt_popup_exists {
4249: border: 1px solid #339933;
4250: margin: -1px;
4251: }
4252: .LC_chrt_popup_up {
4253: border: 1px solid yellow;
4254: margin: -1px;
4255: }
4256: .LC_chrt_popup {
4257: border: 1px solid #8888FF;
4258: background: #CCCCFF;
4259: }
4260:
1.421 albertel 4261: table.LC_pick_box {
4262: width: 100%;
4263: border-collapse: separate;
4264: background: white;
4265: border: 1px solid black;
4266: border-spacing: 1px;
4267: }
4268: table.LC_pick_box td.LC_pick_box_title {
4269: background: $tabbg;
4270: font-weight: bold;
4271: text-align: right;
4272: width: 184px;
4273: padding: 8px;
4274: }
1.424 albertel 4275: table.LC_pick_box td.LC_pick_box_separator {
1.421 albertel 4276: padding: 0px;
4277: height: 1px;
4278: background: black;
4279: }
4280: table.LC_pick_box td.LC_pick_box_submit {
4281: text-align: right;
4282: }
4283:
1.424 albertel 4284: table.LC_group_priv_box {
4285: background: white;
4286: border: 1px solid black;
4287: border-spacing: 1px;
4288: }
4289: table.LC_group_priv_box td.LC_pick_box_title {
4290: background: $tabbg;
4291: font-weight: bold;
4292: text-align: right;
4293: width: 184px;
4294: }
4295: table.LC_group_priv_box td.LC_groups_fixed {
4296: background: $data_table_light;
4297: text-align: center;
4298: }
4299: table.LC_group_priv_box td.LC_groups_optional {
4300: background: $data_table_dark;
4301: text-align: center;
4302: }
4303: table.LC_group_priv_box td.LC_groups_functionality {
4304: background: $data_table_darker;
4305: text-align: center;
4306: font-weight: bold;
4307: }
4308: table.LC_group_priv td {
4309: text-align: left;
4310: padding: 0px;
4311: }
4312:
1.421 albertel 4313: table.LC_notify_front_page {
4314: background: white;
4315: border: 1px solid black;
4316: padding: 8px;
4317: }
4318: table.LC_notify_front_page td {
4319: padding: 8px;
4320: }
1.424 albertel 4321: .LC_navbuttons {
4322: margin: 2ex 0ex 2ex 0ex;
4323: }
1.423 albertel 4324: .LC_topic_bar {
4325: font-family: $sans;
4326: font-weight: bold;
4327: width: 100%;
4328: background: $tabbg;
4329: vertical-align: middle;
4330: margin: 2ex 0ex 2ex 0ex;
4331: }
4332: .LC_topic_bar span {
4333: vertical-align: middle;
4334: }
4335: .LC_topic_bar img {
4336: vertical-align: bottom;
4337: }
4338: table.LC_course_group_status {
4339: margin: 20px;
4340: }
4341: table.LC_status_selector td {
4342: vertical-align: top;
4343: text-align: center;
1.424 albertel 4344: padding: 4px;
4345: }
4346: table.LC_descriptive_input td.LC_description {
4347: vertical-align: top;
4348: text-align: right;
4349: font-weight: bold;
1.423 albertel 4350: }
1.489 raeburn 4351: table.LC_feedback_link {
4352: background: $feedback_link_bg;
4353: }
4354: span.LC_feedback_link {
4355: background: $feedback_link_bg;
4356: font-size: larger;
4357: }
1.421 albertel 4358:
1.515 albertel 4359: table.LC_prior_tries {
1.524 albertel 4360: border: 1px solid #000000;
4361: border-collapse: separate;
4362: border-spacing: 1px;
1.515 albertel 4363: }
1.523 albertel 4364:
1.515 albertel 4365: table.LC_prior_tries td {
1.524 albertel 4366: padding: 2px;
1.515 albertel 4367: }
1.523 albertel 4368:
4369: .LC_answer_correct {
4370: background: #AAFFAA;
4371: color: black;
4372: }
4373: .LC_answer_charged_try {
4374: background: #FFAAAA ! important;
4375: color: black;
4376: }
4377: .LC_answer_not_charged_try,
4378: .LC_answer_no_grade,
4379: .LC_answer_late {
4380: background: #FFFFAA;
4381: color: black;
4382: }
4383: .LC_answer_previous {
4384: background: #AAAAFF;
4385: color: black;
4386: }
4387: .LC_answer_no_message {
4388: background: #FFFFFF;
4389: color: black;
4390: }
4391: .LC_answer_unknown {
4392: background: orange;
4393: color: black;
4394: }
4395:
4396:
1.529 albertel 4397: span.LC_prior_numerical,
4398: span.LC_prior_string,
4399: span.LC_prior_custom,
4400: span.LC_prior_reaction,
4401: span.LC_prior_math {
1.523 albertel 4402: font-family: monospace;
4403: white-space: pre;
4404: }
4405:
1.525 albertel 4406: span.LC_prior_string {
4407: font-family: monospace;
4408: white-space: pre;
4409: }
4410:
1.523 albertel 4411: table.LC_prior_option {
4412: width: 100%;
4413: border-collapse: collapse;
4414: }
1.528 albertel 4415: table.LC_prior_rank, table.LC_prior_match {
4416: border-collapse: collapse;
4417: }
4418: table.LC_prior_option tr td,
4419: table.LC_prior_rank tr td,
4420: table.LC_prior_match tr td {
1.524 albertel 4421: border: 1px solid #000000;
1.515 albertel 4422: }
4423:
1.519 raeburn 4424: span.LC_nobreak {
1.544 albertel 4425: white-space: nowrap;
1.519 raeburn 4426: }
4427:
1.545 albertel 4428: table.LC_docs_documents {
4429: background: #BBBBBB;
1.547 albertel 4430: border-width: 0px;
1.545 albertel 4431: border-collapse: collapse;
4432: }
4433:
4434: table.LC_docs_documents td.LC_docs_document {
4435: border: 2px solid black;
4436: padding: 4px;
4437: }
4438:
4439: .LC_docs_course_commands div {
4440: float: left;
4441: border: 4px solid #AAAAAA;
4442: padding: 4px;
4443: background: #DDDDCC;
4444: }
4445:
4446: .LC_docs_entry_move {
4447: border: 0px;
4448: border-collapse: collapse;
1.544 albertel 4449: }
4450:
1.545 albertel 4451: .LC_docs_entry_move td {
4452: border: 2px solid #BBBBBB;
4453: background: #DDDDDD;
4454: }
4455:
4456: .LC_docs_editor td.LC_docs_entry_commands {
4457: background: #DDDDDD;
4458: font-size: x-small;
4459: }
1.544 albertel 4460: .LC_docs_copy {
1.545 albertel 4461: color: #000099;
1.544 albertel 4462: }
4463: .LC_docs_cut {
1.545 albertel 4464: color: #550044;
1.544 albertel 4465: }
4466: .LC_docs_rename {
1.545 albertel 4467: color: #009900;
1.544 albertel 4468: }
4469: .LC_docs_remove {
1.545 albertel 4470: color: #990000;
4471: }
4472:
1.547 albertel 4473: .LC_docs_reinit_warn,
4474: .LC_docs_ext_edit {
4475: font-size: x-small;
4476: }
4477:
1.545 albertel 4478: .LC_docs_editor td.LC_docs_entry_title,
4479: .LC_docs_editor td.LC_docs_entry_icon {
4480: background: #FFFFBB;
4481: }
4482: .LC_docs_editor td.LC_docs_entry_parameter {
4483: background: #BBBBFF;
4484: font-size: x-small;
4485: white-space: nowrap;
4486: }
4487:
4488: table.LC_docs_adddocs td,
4489: table.LC_docs_adddocs th {
4490: border: 1px solid #BBBBBB;
4491: padding: 4px;
4492: background: #DDDDDD;
1.543 albertel 4493: }
4494:
1.343 albertel 4495: END
4496: }
4497:
1.306 albertel 4498: =pod
4499:
4500: =item * &headtag()
4501:
4502: Returns a uniform footer for LON-CAPA web pages.
4503:
1.307 albertel 4504: Inputs: $title - optional title for the head
4505: $head_extra - optional extra HTML to put inside the <head>
1.315 albertel 4506: $args - optional arguments
1.319 albertel 4507: force_register - if is true call registerurl so the remote is
4508: informed
1.415 albertel 4509: redirect -> array ref of
4510: 1- seconds before redirect occurs
4511: 2- url to redirect to
4512: 3- whether the side effect should occur
1.315 albertel 4513: (side effect of setting
4514: $env{'internal.head.redirect'} to the url
4515: redirected too)
1.352 albertel 4516: domain -> force to color decorate a page for a specific
4517: domain
4518: function -> force usage of a specific rolish color scheme
4519: bgcolor -> override the default page bgcolor
1.460 albertel 4520: no_auto_mt_title
4521: -> prevent &mt()ing the title arg
1.464 albertel 4522:
1.306 albertel 4523: =cut
4524:
4525: sub headtag {
1.313 albertel 4526: my ($title,$head_extra,$args) = @_;
1.306 albertel 4527:
1.363 albertel 4528: my $function = $args->{'function'} || &get_users_function();
4529: my $domain = $args->{'domain'} || &determinedomain();
4530: my $bgcolor = $args->{'bgcolor'} || &designparm($function.'.pgbg',$domain);
1.418 albertel 4531: my $url = join(':',$env{'user.name'},$env{'user.domain'},
1.458 albertel 4532: $Apache::lonnet::perlvar{'lonVersion'},
1.531 albertel 4533: #time(),
1.418 albertel 4534: $env{'environment.color.timestamp'},
1.363 albertel 4535: $function,$domain,$bgcolor);
4536:
1.369 www 4537: $url = '/adm/css/'.&escape($url).'.css';
1.363 albertel 4538:
1.308 albertel 4539: my $result =
4540: '<head>'.
1.461 albertel 4541: &font_settings();
1.319 albertel 4542:
1.461 albertel 4543: if (!$args->{'frameset'}) {
4544: $result .= &Apache::lonhtmlcommon::htmlareaheaders();
4545: }
1.319 albertel 4546: if ($args->{'force_register'}) {
4547: $result .= &Apache::lonmenu::registerurl(1);
4548: }
1.436 albertel 4549: if (!$args->{'no_nav_bar'}
4550: && !$args->{'only_body'}
4551: && !$args->{'frameset'}) {
4552: $result .= &help_menu_js();
4553: }
1.319 albertel 4554:
1.314 albertel 4555: if (ref($args->{'redirect'})) {
1.414 albertel 4556: my ($time,$url,$inhibit_continue) = @{$args->{'redirect'}};
1.315 albertel 4557: $url = &Apache::lonenc::check_encrypt($url);
1.414 albertel 4558: if (!$inhibit_continue) {
4559: $env{'internal.head.redirect'} = $url;
4560: }
1.313 albertel 4561: $result.=<<ADDMETA
4562: <meta http-equiv="pragma" content="no-cache" />
1.344 albertel 4563: <meta http-equiv="Refresh" content="$time; url=$url" />
1.313 albertel 4564: ADDMETA
4565: }
1.306 albertel 4566: if (!defined($title)) {
4567: $title = 'The LearningOnline Network with CAPA';
4568: }
1.460 albertel 4569: if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
4570: $result .= '<title> LON-CAPA '.$title.'</title>'
1.414 albertel 4571: .'<link rel="stylesheet" type="text/css" href="'.$url.'" />'
4572: .$head_extra;
1.306 albertel 4573: return $result;
4574: }
4575:
4576: =pod
4577:
1.340 albertel 4578: =item * &font_settings()
4579:
4580: Returns neccessary <meta> to set the proper encoding
4581:
4582: Inputs: none
4583:
4584: =cut
4585:
4586: sub font_settings {
4587: my $headerstring='';
4588: if (($env{'browser.os'} eq 'mac') && (!$env{'browser.mathml'})) {
4589: $headerstring.=
4590: '<meta Content-Type="text/html; charset=x-mac-roman" />';
4591: } elsif (!$env{'browser.mathml'} && $env{'browser.unicode'}) {
4592: $headerstring.=
4593: '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
4594: }
4595: return $headerstring;
4596: }
4597:
1.341 albertel 4598: =pod
4599:
4600: =item * &xml_begin()
4601:
4602: Returns the needed doctype and <html>
4603:
4604: Inputs: none
4605:
4606: =cut
4607:
4608: sub xml_begin {
4609: my $output='';
4610:
1.342 albertel 4611: &Apache::lonhtmlcommon::init_htmlareafields();
4612:
1.341 albertel 4613: if ($env{'browser.mathml'}) {
4614: $output='<?xml version="1.0"?>'
4615: #.'<?xml-stylesheet type="text/css" href="/adm/MathML/mathml.css"?>'."\n"
4616: # .'<!DOCTYPE html SYSTEM "/adm/MathML/mathml.dtd" '
4617:
4618: # .'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" [<!ENTITY mathns "http://www.w3.org/1998/Math/MathML">] >'
4619: .'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN" "http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd">'
4620: .'<html xmlns:math="http://www.w3.org/1998/Math/MathML" '
4621: .'xmlns="http://www.w3.org/1999/xhtml">';
4622: } else {
4623: $output='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html>';
4624: }
4625: return $output;
4626: }
1.340 albertel 4627:
4628: =pod
4629:
1.306 albertel 4630: =item * &endheadtag()
4631:
4632: Returns a uniform </head> for LON-CAPA web pages.
4633:
4634: Inputs: none
4635:
4636: =cut
4637:
4638: sub endheadtag {
4639: return '</head>';
4640: }
4641:
4642: =pod
4643:
4644: =item * &head()
4645:
4646: Returns a uniform complete <head>..</head> section for LON-CAPA web pages.
4647:
4648: Inputs: $title - optional title for the page
1.307 albertel 4649: $head_extra - optional extra HTML to put inside the <head>
1.405 albertel 4650:
1.306 albertel 4651: =cut
4652:
4653: sub head {
1.325 albertel 4654: my ($title,$head_extra,$args) = @_;
4655: return &headtag($title,$head_extra,$args).&endheadtag();
1.306 albertel 4656: }
4657:
4658: =pod
4659:
4660: =item * &start_page()
4661:
4662: Returns a complete <html> .. <body> section for LON-CAPA web pages.
4663:
4664: Inputs: $title - optional title for the page
4665: $head_extra - optional extra HTML to incude inside the <head>
1.315 albertel 4666: $args - additional optional args supported are:
1.317 albertel 4667: only_body -> is true will set &bodytag() onlybodytag
4668: arg on
4669: no_nav_bar -> is true will set &bodytag() notopbar arg on
4670: add_entries -> additional attributes to add to the <body>
4671: domain -> force to color decorate a page for a
4672: specific domain
4673: function -> force usage of a specific rolish color
4674: scheme
4675: redirect -> see &headtag()
4676: bgcolor -> override the default page bg color
4677: js_ready -> return a string ready for being used in
4678: a javascript writeln
1.320 albertel 4679: html_encode -> return a string ready for being used in
4680: a html attribute
1.317 albertel 4681: force_register -> if is true will turn on the &bodytag()
4682: $forcereg arg
1.326 albertel 4683: body_title -> alternate text to use instead of $title
4684: in the title box that appears, this text
4685: is not auto translated like the $title is
1.330 albertel 4686: frameset -> if true will start with a <frameset>
4687: rather than <body>
1.338 albertel 4688: no_title -> if true the title bar won't be shown
4689: skip_phases -> hash ref of
4690: head -> skip the <html><head> generation
4691: body -> skip all <body> generation
1.337 albertel 4692:
1.361 albertel 4693: no_inline_link -> if true and in remote mode, don't show the
4694: 'Switch To Inline Menu' link
4695:
1.460 albertel 4696: no_auto_mt_title -> prevent &mt()ing the title arg
4697:
1.306 albertel 4698: =cut
4699:
4700: sub start_page {
1.309 albertel 4701: my ($title,$head_extra,$args) = @_;
1.318 albertel 4702: #&Apache::lonnet::logthis("start_page ".join(':',caller(0)));
1.313 albertel 4703: my %head_args;
1.352 albertel 4704: foreach my $arg ('redirect','force_register','domain','function',
1.460 albertel 4705: 'bgcolor','frameset','no_nav_bar','only_body',
4706: 'no_auto_mt_title') {
1.319 albertel 4707: if (defined($args->{$arg})) {
1.324 raeburn 4708: $head_args{$arg} = $args->{$arg};
1.319 albertel 4709: }
1.313 albertel 4710: }
1.319 albertel 4711:
1.315 albertel 4712: $env{'internal.start_page'}++;
1.338 albertel 4713: my $result;
4714: if (! exists($args->{'skip_phases'}{'head'}) ) {
4715: $result.=
1.341 albertel 4716: &xml_begin().
1.338 albertel 4717: &headtag($title,$head_extra,\%head_args).&endheadtag();
4718: }
4719:
4720: if (! exists($args->{'skip_phases'}{'body'}) ) {
4721: if ($args->{'frameset'}) {
4722: my $attr_string = &make_attr_string($args->{'force_register'},
4723: $args->{'add_entries'});
4724: $result .= "\n<frameset $attr_string>\n";
4725: } else {
4726: $result .=
4727: &bodytag($title,
4728: $args->{'function'}, $args->{'add_entries'},
4729: $args->{'only_body'}, $args->{'domain'},
4730: $args->{'force_register'}, $args->{'body_title'},
4731: $args->{'no_nav_bar'}, $args->{'bgcolor'},
1.460 albertel 4732: $args->{'no_title'}, $args->{'no_inline_link'},
4733: $args);
1.338 albertel 4734: }
1.330 albertel 4735: }
1.338 albertel 4736:
1.315 albertel 4737: if ($args->{'js_ready'}) {
1.317 albertel 4738: $result = &js_ready($result);
1.315 albertel 4739: }
1.320 albertel 4740: if ($args->{'html_encode'}) {
4741: $result = &html_encode($result);
4742: }
1.315 albertel 4743: return $result;
1.306 albertel 4744: }
4745:
1.330 albertel 4746:
1.306 albertel 4747: =pod
4748:
4749: =item * &head()
4750:
4751: Returns a complete </body></html> section for LON-CAPA web pages.
4752:
1.315 albertel 4753: Inputs: $args - additional optional args supported are:
4754: js_ready -> return a string ready for being used in
4755: a javascript writeln
1.320 albertel 4756: html_encode -> return a string ready for being used in
4757: a html attribute
1.330 albertel 4758: frameset -> if true will start with a <frameset>
4759: rather than <body>
1.493 albertel 4760: dicsussion -> if true will get discussion from
4761: lonxml::xmlend
4762: (you can pass the target and parser arguments
4763: through optional 'target' and 'parser' args
4764: to this routine)
1.306 albertel 4765:
4766: =cut
4767:
4768: sub end_page {
1.315 albertel 4769: my ($args) = @_;
4770: $env{'internal.end_page'}++;
1.330 albertel 4771: my $result;
1.335 albertel 4772: if ($args->{'discussion'}) {
4773: my ($target,$parser);
4774: if (ref($args->{'discussion'})) {
4775: ($target,$parser) =($args->{'discussion'}{'target'},
4776: $args->{'discussion'}{'parser'});
4777: }
4778: $result .= &Apache::lonxml::xmlend($target,$parser);
4779: }
4780:
1.330 albertel 4781: if ($args->{'frameset'}) {
4782: $result .= '</frameset>';
4783: } else {
4784: $result .= &endbodytag();
4785: }
4786: $result .= "\n</html>";
4787:
1.315 albertel 4788: if ($args->{'js_ready'}) {
1.317 albertel 4789: $result = &js_ready($result);
1.315 albertel 4790: }
1.335 albertel 4791:
1.320 albertel 4792: if ($args->{'html_encode'}) {
4793: $result = &html_encode($result);
4794: }
1.335 albertel 4795:
1.315 albertel 4796: return $result;
4797: }
4798:
1.320 albertel 4799: sub html_encode {
4800: my ($result) = @_;
4801:
1.322 albertel 4802: $result = &HTML::Entities::encode($result,'<>&"');
1.320 albertel 4803:
4804: return $result;
4805: }
1.317 albertel 4806: sub js_ready {
4807: my ($result) = @_;
4808:
1.323 albertel 4809: $result =~ s/[\n\r]/ /xmsg;
4810: $result =~ s/\\/\\\\/xmsg;
4811: $result =~ s/'/\\'/xmsg;
1.372 albertel 4812: $result =~ s{</}{<\\/}xmsg;
1.317 albertel 4813:
4814: return $result;
4815: }
4816:
1.315 albertel 4817: sub validate_page {
4818: if ( exists($env{'internal.start_page'})
1.316 albertel 4819: && $env{'internal.start_page'} > 1) {
4820: &Apache::lonnet::logthis('start_page called multiple times '.
1.318 albertel 4821: $env{'internal.start_page'}.' '.
1.316 albertel 4822: $ENV{'request.filename'});
1.315 albertel 4823: }
4824: if ( exists($env{'internal.end_page'})
1.316 albertel 4825: && $env{'internal.end_page'} > 1) {
4826: &Apache::lonnet::logthis('end_page called multiple times '.
1.318 albertel 4827: $env{'internal.end_page'}.' '.
1.316 albertel 4828: $env{'request.filename'});
1.315 albertel 4829: }
4830: if ( exists($env{'internal.start_page'})
4831: && ! exists($env{'internal.end_page'})) {
1.316 albertel 4832: &Apache::lonnet::logthis('start_page called without end_page '.
4833: $env{'request.filename'});
1.315 albertel 4834: }
4835: if ( ! exists($env{'internal.start_page'})
4836: && exists($env{'internal.end_page'})) {
1.316 albertel 4837: &Apache::lonnet::logthis('end_page called without start_page'.
4838: $env{'request.filename'});
1.315 albertel 4839: }
1.306 albertel 4840: }
1.315 albertel 4841:
1.318 albertel 4842: sub simple_error_page {
4843: my ($r,$title,$msg) = @_;
4844: my $page =
4845: &Apache::loncommon::start_page($title).
4846: &mt($msg).
4847: &Apache::loncommon::end_page();
4848: if (ref($r)) {
4849: $r->print($page);
1.327 albertel 4850: return;
1.318 albertel 4851: }
4852: return $page;
4853: }
1.347 albertel 4854:
4855: {
4856: my $row_count;
4857: sub start_data_table {
1.422 albertel 4858: my ($add_class) = @_;
4859: my $css_class = (join(' ','LC_data_table',$add_class));
1.347 albertel 4860: undef($row_count);
1.422 albertel 4861: return '<table class="'.$css_class.'">'."\n";
1.347 albertel 4862: }
4863:
4864: sub end_data_table {
4865: undef($row_count);
1.389 albertel 4866: return '</table>'."\n";;
1.347 albertel 4867: }
4868:
4869: sub start_data_table_row {
1.422 albertel 4870: my ($add_class) = @_;
1.347 albertel 4871: $row_count++;
1.422 albertel 4872: my $css_class = ($row_count % 2)?'':'LC_even_row';
1.428 albertel 4873: $css_class = (join(' ',$css_class,$add_class));
1.422 albertel 4874: return '<tr class="'.$css_class.'">'."\n";;
1.347 albertel 4875: }
1.471 banghart 4876:
4877: sub continue_data_table_row {
4878: my ($add_class) = @_;
4879: my $css_class = ($row_count % 2)?'':'LC_even_row';
4880: $css_class = (join(' ',$css_class,$add_class));
4881: return '<tr class="'.$css_class.'">'."\n";;
4882: }
1.347 albertel 4883:
4884: sub end_data_table_row {
1.389 albertel 4885: return '</tr>'."\n";;
1.347 albertel 4886: }
1.367 www 4887:
1.421 albertel 4888: sub start_data_table_empty_row {
4889: $row_count++;
4890: return '<tr class="LC_empty_row" >'."\n";;
4891: }
4892:
4893: sub end_data_table_empty_row {
4894: return '</tr>'."\n";;
4895: }
4896:
1.367 www 4897: sub start_data_table_header_row {
1.389 albertel 4898: return '<tr class="LC_header_row">'."\n";;
1.367 www 4899: }
4900:
4901: sub end_data_table_header_row {
1.389 albertel 4902: return '</tr>'."\n";;
1.367 www 4903: }
1.347 albertel 4904: }
4905:
1.548 albertel 4906: =pod
4907:
4908: =item * &inhibit_menu_check($arg)
4909:
4910: Checks for a inhibitmenu state and generates output to preserve it
4911:
4912: Inputs: $arg - can be any of
4913: - undef - in which case the return value is a string
4914: to add into arguments list of a uri
4915: - 'input' - in which case the return value is a HTML
4916: <form> <input> field of type hidden to
4917: preserve the value
4918: - a url - in which case the return value is the url with
4919: the neccesary cgi args added to preserve the
4920: inhibitmenu state
4921: - a ref to a url - no return value, but the string is
4922: updated to include the neccessary cgi
4923: args to preserve the inhibitmenu state
4924:
4925: =cut
4926:
4927: sub inhibit_menu_check {
4928: my ($arg) = @_;
4929: &get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['inhibitmenu']);
4930: if ($arg eq 'input') {
4931: if ($env{'form.inhibitmenu'}) {
4932: return '<input type="hidden" name="inhibitmenu" value="'.$env{'form.inhibitmenu'}.'" />';
4933: } else {
4934: return
4935: }
4936: }
4937: if ($env{'form.inhibitmenu'}) {
4938: if (ref($arg)) {
4939: $$arg .= '?inhibitmenu='.$env{'form.inhibitmenu'};
4940: } elsif ($arg eq '') {
4941: $arg .= 'inhibitmenu='.$env{'form.inhibitmenu'};
4942: } else {
4943: $arg .= '?inhibitmenu='.$env{'form.inhibitmenu'};
4944: }
4945: }
4946: if (!ref($arg)) {
4947: return $arg;
4948: }
4949: }
4950:
1.251 albertel 4951: ###############################################
1.182 matthew 4952:
4953: =pod
4954:
1.549 albertel 4955: =back
4956:
4957: =head1 User Information Routines
4958:
4959: =over 4
4960:
1.405 albertel 4961: =item * &get_users_function()
1.182 matthew 4962:
4963: Used by &bodytag to determine the current users primary role.
4964: Returns either 'student','coordinator','admin', or 'author'.
4965:
4966: =cut
4967:
4968: ###############################################
4969: sub get_users_function {
4970: my $function = 'student';
1.258 albertel 4971: if ($env{'request.role'}=~/^(cc|in|ta|ep)/) {
1.182 matthew 4972: $function='coordinator';
4973: }
1.258 albertel 4974: if ($env{'request.role'}=~/^(su|dc|ad|li)/) {
1.182 matthew 4975: $function='admin';
4976: }
1.258 albertel 4977: if (($env{'request.role'}=~/^(au|ca)/) ||
1.182 matthew 4978: ($ENV{'REQUEST_URI'}=~/^(\/priv|\~)/)) {
4979: $function='author';
4980: }
4981: return $function;
1.54 www 4982: }
1.99 www 4983:
4984: ###############################################
4985:
1.233 raeburn 4986: =pod
4987:
1.542 raeburn 4988: =item * &check_user_status()
1.274 raeburn 4989:
4990: Determines current status of supplied role for a
4991: specific user. Roles can be active, previous or future.
4992:
4993: Inputs:
4994: user's domain, user's username, course's domain,
1.375 raeburn 4995: course's number, optional section ID.
1.274 raeburn 4996:
4997: Outputs:
4998: role status: active, previous or future.
4999:
5000: =cut
5001:
5002: sub check_user_status {
1.412 raeburn 5003: my ($udom,$uname,$cdom,$crs,$role,$sec) = @_;
1.274 raeburn 5004: my %userinfo = &Apache::lonnet::dump('roles',$udom,$uname);
5005: my @uroles = keys %userinfo;
5006: my $srchstr;
5007: my $active_chk = 'none';
1.412 raeburn 5008: my $now = time;
1.274 raeburn 5009: if (@uroles > 0) {
1.412 raeburn 5010: if (($role eq 'cc') || ($sec eq '') || (!defined($sec))) {
1.274 raeburn 5011: $srchstr = '/'.$cdom.'/'.$crs.'_'.$role;
5012: } else {
1.412 raeburn 5013: $srchstr = '/'.$cdom.'/'.$crs.'/'.$sec.'_'.$role;
5014: }
5015: if (grep/^\Q$srchstr\E$/,@uroles) {
1.274 raeburn 5016: my $role_end = 0;
5017: my $role_start = 0;
5018: $active_chk = 'active';
1.412 raeburn 5019: if ($userinfo{$srchstr} =~ m/^\Q$role\E_(\d+)/) {
5020: $role_end = $1;
5021: if ($userinfo{$srchstr} =~ m/^\Q$role\E_\Q$role_end\E_(\d+)$/) {
5022: $role_start = $1;
1.274 raeburn 5023: }
5024: }
5025: if ($role_start > 0) {
1.412 raeburn 5026: if ($now < $role_start) {
1.274 raeburn 5027: $active_chk = 'future';
5028: }
5029: }
5030: if ($role_end > 0) {
1.412 raeburn 5031: if ($now > $role_end) {
1.274 raeburn 5032: $active_chk = 'previous';
5033: }
5034: }
5035: }
5036: }
5037: return $active_chk;
5038: }
5039:
5040: ###############################################
5041:
5042: =pod
5043:
1.405 albertel 5044: =item * &get_sections()
1.233 raeburn 5045:
5046: Determines all the sections for a course including
5047: sections with students and sections containing other roles.
1.419 raeburn 5048: Incoming parameters:
5049:
5050: 1. domain
5051: 2. course number
5052: 3. reference to array containing roles for which sections should
5053: be gathered (optional).
5054: 4. reference to array containing status types for which sections
5055: should be gathered (optional).
5056:
5057: If the third argument is undefined, sections are gathered for any role.
5058: If the fourth argument is undefined, sections are gathered for any status.
5059: Permissible values are 'active' or 'future' or 'previous'.
1.233 raeburn 5060:
1.374 raeburn 5061: Returns section hash (keys are section IDs, values are
5062: number of users in each section), subject to the
1.419 raeburn 5063: optional roles filter, optional status filter
1.233 raeburn 5064:
5065: =cut
5066:
5067: ###############################################
5068: sub get_sections {
1.419 raeburn 5069: my ($cdom,$cnum,$possible_roles,$possible_status) = @_;
1.366 albertel 5070: if (!defined($cdom) || !defined($cnum)) {
5071: my $cid = $env{'request.course.id'};
5072:
5073: return if (!defined($cid));
5074:
5075: $cdom = $env{'course.'.$cid.'.domain'};
5076: $cnum = $env{'course.'.$cid.'.num'};
5077: }
5078:
5079: my %sectioncount;
1.419 raeburn 5080: my $now = time;
1.240 albertel 5081:
1.366 albertel 5082: if (!defined($possible_roles) || (grep(/^st$/,@$possible_roles))) {
1.276 albertel 5083: my ($classlist) = &Apache::loncoursedata::get_classlist($cdom,$cnum);
1.240 albertel 5084: my $sec_index = &Apache::loncoursedata::CL_SECTION();
5085: my $status_index = &Apache::loncoursedata::CL_STATUS();
1.419 raeburn 5086: my $start_index = &Apache::loncoursedata::CL_START();
5087: my $end_index = &Apache::loncoursedata::CL_END();
5088: my $status;
1.366 albertel 5089: while (my ($student,$data) = each(%$classlist)) {
1.419 raeburn 5090: my ($section,$stu_status,$start,$end) = ($data->[$sec_index],
5091: $data->[$status_index],
5092: $data->[$start_index],
5093: $data->[$end_index]);
5094: if ($stu_status eq 'Active') {
5095: $status = 'active';
5096: } elsif ($end < $now) {
5097: $status = 'previous';
5098: } elsif ($start > $now) {
5099: $status = 'future';
5100: }
5101: if ($section ne '-1' && $section !~ /^\s*$/) {
5102: if ((!defined($possible_status)) || (($status ne '') &&
5103: (grep/^\Q$status\E$/,@{$possible_status}))) {
5104: $sectioncount{$section}++;
5105: }
1.240 albertel 5106: }
5107: }
5108: }
5109: my %courseroles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
5110: foreach my $user (sort(keys(%courseroles))) {
5111: if ($user !~ /^(\w{2})/) { next; }
5112: my ($role) = ($user =~ /^(\w{2})/);
5113: if ($possible_roles && !(grep(/^$role$/,@$possible_roles))) { next; }
1.419 raeburn 5114: my ($section,$status);
1.240 albertel 5115: if ($role eq 'cr' &&
5116: $user =~ m-^$role/[^/]*/[^/]*/[^/]*:[^:]*:[^:]*:(\w+)-) {
5117: $section=$1;
5118: }
5119: if ($user =~ /^$role:[^:]*:[^:]*:(\w+)/) { $section=$1; }
5120: if (!defined($section) || $section eq '-1') { next; }
1.419 raeburn 5121: my ($end,$start) = ($courseroles{$user} =~ /^([^:]*):([^:]*)$/);
5122: if ($end == -1 && $start == -1) {
5123: next; #deleted role
5124: }
5125: if (!defined($possible_status)) {
5126: $sectioncount{$section}++;
5127: } else {
5128: if ((!$end || $end >= $now) && (!$start || $start <= $now)) {
5129: $status = 'active';
5130: } elsif ($end < $now) {
5131: $status = 'future';
5132: } elsif ($start > $now) {
5133: $status = 'previous';
5134: }
5135: if (($status ne '') && (grep/^\Q$status\E$/,@{$possible_status})) {
5136: $sectioncount{$section}++;
5137: }
5138: }
1.233 raeburn 5139: }
1.366 albertel 5140: return %sectioncount;
1.233 raeburn 5141: }
5142:
1.274 raeburn 5143: ###############################################
1.294 raeburn 5144:
5145: =pod
1.405 albertel 5146:
5147: =item * &get_course_users()
5148:
1.275 raeburn 5149: Retrieves usernames:domains for users in the specified course
5150: with specific role(s), and access status.
5151:
5152: Incoming parameters:
1.277 albertel 5153: 1. course domain
5154: 2. course number
5155: 3. access status: users must have - either active,
1.275 raeburn 5156: previous, future, or all.
1.277 albertel 5157: 4. reference to array of permissible roles
1.288 raeburn 5158: 5. reference to array of section restrictions (optional)
5159: 6. reference to results object (hash of hashes).
5160: 7. reference to optional userdata hash
1.275 raeburn 5161: Keys of top level hash are roles.
5162: Keys of inner hashes are username:domain, with
5163: values set to access type.
1.288 raeburn 5164: Optional userdata hash returns an array with arguments in the
5165: same order as loncoursedata::get_classlist() for student data.
5166:
5167: Entries for end, start, section and status are blank because
5168: of the possibility of multiple values for non-student roles.
5169:
1.275 raeburn 5170: =cut
1.405 albertel 5171:
1.275 raeburn 5172: ###############################################
1.405 albertel 5173:
1.275 raeburn 5174: sub get_course_users {
1.288 raeburn 5175: my ($cdom,$cnum,$types,$roles,$sections,$users,$userdata) = @_;
5176: my %idx = ();
1.419 raeburn 5177: my %seclists;
1.288 raeburn 5178:
5179: $idx{udom} = &Apache::loncoursedata::CL_SDOM();
5180: $idx{uname} = &Apache::loncoursedata::CL_SNAME();
5181: $idx{end} = &Apache::loncoursedata::CL_END();
5182: $idx{start} = &Apache::loncoursedata::CL_START();
5183: $idx{id} = &Apache::loncoursedata::CL_ID();
5184: $idx{section} = &Apache::loncoursedata::CL_SECTION();
5185: $idx{fullname} = &Apache::loncoursedata::CL_FULLNAME();
5186: $idx{status} = &Apache::loncoursedata::CL_STATUS();
5187:
1.290 albertel 5188: if (grep(/^st$/,@{$roles})) {
1.276 albertel 5189: my ($classlist,$keylist)=&Apache::loncoursedata::get_classlist($cdom,$cnum);
1.278 raeburn 5190: my $now = time;
1.277 albertel 5191: foreach my $student (keys(%{$classlist})) {
1.288 raeburn 5192: my $match = 0;
1.412 raeburn 5193: my $secmatch = 0;
1.419 raeburn 5194: my $section = $$classlist{$student}[$idx{section}];
5195: if ($section eq '') {
5196: $section = 'none';
5197: }
1.291 albertel 5198: if ((ref($sections) eq 'ARRAY') && (@{$sections} > 0)) {
1.420 albertel 5199: if (grep(/^all$/,@{$sections})) {
1.412 raeburn 5200: $secmatch = 1;
5201: } elsif ($$classlist{$student}[$idx{section}] eq '') {
1.420 albertel 5202: if (grep(/^none$/,@{$sections})) {
1.412 raeburn 5203: $secmatch = 1;
5204: }
5205: } else {
1.419 raeburn 5206: if (grep(/^\Q$section\E$/,@{$sections})) {
1.412 raeburn 5207: $secmatch = 1;
5208: }
1.290 albertel 5209: }
1.412 raeburn 5210: if (!$secmatch) {
5211: next;
5212: }
1.419 raeburn 5213: }
1.420 albertel 5214: push(@{$seclists{$student}},$section);
1.275 raeburn 5215: if (defined($$types{'active'})) {
1.288 raeburn 5216: if ($$classlist{$student}[$idx{status}] eq 'Active') {
1.275 raeburn 5217: push(@{$$users{st}{$student}},'active');
1.288 raeburn 5218: $match = 1;
1.275 raeburn 5219: }
5220: }
5221: if (defined($$types{'previous'})) {
1.288 raeburn 5222: if ($$classlist{$student}[$idx{end}] <= $now) {
1.275 raeburn 5223: push(@{$$users{st}{$student}},'previous');
1.288 raeburn 5224: $match = 1;
1.275 raeburn 5225: }
5226: }
5227: if (defined($$types{'future'})) {
1.288 raeburn 5228: if (($$classlist{$student}[$idx{start}] > $now) && ($$classlist{$student}[$idx{end}] > $now) || ($$classlist{$student}[$idx{end}] == 0) || ($$classlist{$student}[$idx{end}] eq '')) {
1.275 raeburn 5229: push(@{$$users{st}{$student}},'future');
1.288 raeburn 5230: $match = 1;
1.275 raeburn 5231: }
5232: }
1.412 raeburn 5233: if ($match && ref($userdata) eq 'HASH') {
1.288 raeburn 5234: $$userdata{$student} = $$classlist{$student};
5235: }
1.275 raeburn 5236: }
5237: }
1.412 raeburn 5238: if ((@{$roles} > 1) || ((@{$roles} == 1) && ($$roles[0] ne "st"))) {
1.439 raeburn 5239: my %coursepersonnel = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
5240: my $now = time;
5241: foreach my $person (sort(keys(%coursepersonnel))) {
1.288 raeburn 5242: my $match = 0;
1.412 raeburn 5243: my $secmatch = 0;
1.439 raeburn 5244: my $status;
1.412 raeburn 5245: my ($role,$user,$usec) = ($person =~ /^([^:]*):([^:]+:[^:]+):([^:]*)/);
1.275 raeburn 5246: $user =~ s/:$//;
1.439 raeburn 5247: my ($end,$start) = split(/:/,$coursepersonnel{$person});
5248: if ($end == -1 || $start == -1) {
5249: next;
5250: }
5251: if (($role) && ((grep(/^\Q$role\E$/,@{$roles})) ||
5252: (grep(/^cr$/,@{$roles}) && $role =~ /^cr\//))) {
1.412 raeburn 5253: my ($uname,$udom) = split(/:/,$user);
5254: if ((ref($sections) eq 'ARRAY') && (@{$sections} > 0)) {
1.420 albertel 5255: if (grep(/^all$/,@{$sections})) {
1.412 raeburn 5256: $secmatch = 1;
5257: } elsif ($usec eq '') {
1.420 albertel 5258: if (grep(/^none$/,@{$sections})) {
1.412 raeburn 5259: $secmatch = 1;
5260: }
5261: } else {
5262: if (grep(/^\Q$usec\E$/,@{$sections})) {
5263: $secmatch = 1;
5264: }
5265: }
5266: if (!$secmatch) {
5267: next;
5268: }
1.288 raeburn 5269: }
1.419 raeburn 5270: if ($usec eq '') {
5271: $usec = 'none';
5272: }
1.275 raeburn 5273: if ($uname ne '' && $udom ne '') {
1.503 raeburn 5274: if ($end > 0 && $end < $now) {
1.439 raeburn 5275: $status = 'previous';
5276: } elsif ($start > $now) {
5277: $status = 'future';
5278: } else {
5279: $status = 'active';
5280: }
1.277 albertel 5281: foreach my $type (keys(%{$types})) {
1.275 raeburn 5282: if ($status eq $type) {
1.420 albertel 5283: if (!grep(/^\Q$type\E$/,@{$$users{$role}{$user}})) {
1.419 raeburn 5284: push(@{$$users{$role}{$user}},$type);
5285: }
1.288 raeburn 5286: $match = 1;
5287: }
5288: }
1.419 raeburn 5289: if (($match) && (ref($userdata) eq 'HASH')) {
5290: if (!exists($$userdata{$uname.':'.$udom})) {
5291: &get_user_info($udom,$uname,\%idx,$userdata);
5292: }
1.420 albertel 5293: if (!grep(/^\Q$usec\E$/,@{$seclists{$uname.':'.$udom}})) {
1.419 raeburn 5294: push(@{$seclists{$uname.':'.$udom}},$usec);
5295: }
1.275 raeburn 5296: }
5297: }
5298: }
5299: }
1.290 albertel 5300: if (grep(/^ow$/,@{$roles})) {
1.279 raeburn 5301: if ((defined($cdom)) && (defined($cnum))) {
5302: my %csettings = &Apache::lonnet::get('environment',['internal.courseowner'],$cdom,$cnum);
5303: if ( defined($csettings{'internal.courseowner'}) ) {
5304: my $owner = $csettings{'internal.courseowner'};
1.439 raeburn 5305: if ($owner !~ /^[^:]+:[^:]+$/) {
5306: $owner = $owner.':'.$cdom;
5307: }
5308: @{$$users{'ow'}{$owner}} = 'any';
1.290 albertel 5309: if (defined($userdata) &&
5310: !exists($$userdata{$owner.':'.$cdom})) {
5311: &get_user_info($cdom,$owner,\%idx,$userdata);
1.420 albertel 5312: if (!grep(/^none$/,@{$seclists{$owner.':'.$cdom}})) {
1.419 raeburn 5313: push(@{$seclists{$owner.':'.$cdom}},'none');
5314: }
1.290 albertel 5315: }
1.279 raeburn 5316: }
5317: }
5318: }
1.419 raeburn 5319: foreach my $user (keys(%seclists)) {
5320: @{$seclists{$user}} = (sort {$a <=> $b} @{$seclists{$user}});
5321: $$userdata{$user}[$idx{section}] = join(',',@{$seclists{$user}});
5322: }
1.275 raeburn 5323: }
5324: return;
5325: }
5326:
1.288 raeburn 5327: sub get_user_info {
5328: my ($udom,$uname,$idx,$userdata) = @_;
1.289 albertel 5329: $$userdata{$uname.':'.$udom}[$$idx{fullname}] =
5330: &plainname($uname,$udom,'lastname');
1.291 albertel 5331: $$userdata{$uname.':'.$udom}[$$idx{uname}] = $uname;
1.297 raeburn 5332: $$userdata{$uname.':'.$udom}[$$idx{udom}] = $udom;
1.288 raeburn 5333: return;
5334: }
1.275 raeburn 5335:
1.472 raeburn 5336: ###############################################
5337:
5338: =pod
5339:
5340: =item * &get_user_quota()
5341:
5342: Retrieves quota assigned for storage of portfolio files for a user
5343:
5344: Incoming parameters:
5345: 1. user's username
5346: 2. user's domain
5347:
5348: Returns:
1.536 raeburn 5349: 1. Disk quota (in Mb) assigned to student.
5350: 2. (Optional) Type of setting: custom or default
5351: (individually assigned or default for user's
5352: institutional status).
5353: 3. (Optional) - User's institutional status (e.g., faculty, staff
5354: or student - types as defined in localenroll::inst_usertypes
5355: for user's domain, which determines default quota for user.
5356: 4. (Optional) - Default quota which would apply to the user.
1.472 raeburn 5357:
5358: If a value has been stored in the user's environment,
1.536 raeburn 5359: it will return that, otherwise it returns the maximal default
5360: defined for the user's instituional status(es) in the domain.
1.472 raeburn 5361:
5362: =cut
5363:
5364: ###############################################
5365:
5366:
5367: sub get_user_quota {
5368: my ($uname,$udom) = @_;
1.536 raeburn 5369: my ($quota,$quotatype,$settingstatus,$defquota);
1.472 raeburn 5370: if (!defined($udom)) {
5371: $udom = $env{'user.domain'};
5372: }
5373: if (!defined($uname)) {
5374: $uname = $env{'user.name'};
5375: }
5376: if (($udom eq '' || $uname eq '') ||
5377: ($udom eq 'public') && ($uname eq 'public')) {
5378: $quota = 0;
1.536 raeburn 5379: $quotatype = 'default';
5380: $defquota = 0;
1.472 raeburn 5381: } else {
1.536 raeburn 5382: my $inststatus;
1.472 raeburn 5383: if ($udom eq $env{'user.domain'} && $uname eq $env{'user.name'}) {
5384: $quota = $env{'environment.portfolioquota'};
1.536 raeburn 5385: $inststatus = $env{'environment.inststatus'};
1.472 raeburn 5386: } else {
1.536 raeburn 5387: my %userenv =
5388: &Apache::lonnet::get('environment',['portfolioquota',
5389: 'inststatus'],$udom,$uname);
1.472 raeburn 5390: my ($tmp) = keys(%userenv);
5391: if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
5392: $quota = $userenv{'portfolioquota'};
1.536 raeburn 5393: $inststatus = $userenv{'inststatus'};
1.472 raeburn 5394: } else {
5395: undef(%userenv);
5396: }
5397: }
1.536 raeburn 5398: ($defquota,$settingstatus) = &default_quota($udom,$inststatus);
1.472 raeburn 5399: if ($quota eq '') {
1.536 raeburn 5400: $quota = $defquota;
5401: $quotatype = 'default';
5402: } else {
5403: $quotatype = 'custom';
1.472 raeburn 5404: }
5405: }
1.536 raeburn 5406: if (wantarray) {
5407: return ($quota,$quotatype,$settingstatus,$defquota);
5408: } else {
5409: return $quota;
5410: }
1.472 raeburn 5411: }
5412:
5413: ###############################################
5414:
5415: =pod
5416:
5417: =item * &default_quota()
5418:
1.536 raeburn 5419: Retrieves default quota assigned for storage of user portfolio files,
5420: given an (optional) user's institutional status.
1.472 raeburn 5421:
5422: Incoming parameters:
5423: 1. domain
1.536 raeburn 5424: 2. (Optional) institutional status(es). This is a : separated list of
5425: status types (e.g., faculty, staff, student etc.)
5426: which apply to the user for whom the default is being retrieved.
5427: If the institutional status string in undefined, the domain
5428: default quota will be returned.
1.472 raeburn 5429:
5430: Returns:
5431: 1. Default disk quota (in Mb) for user portfolios in the domain.
1.536 raeburn 5432: 2. (Optional) institutional type which determined the value of the
5433: default quota.
1.472 raeburn 5434:
5435: If a value has been stored in the domain's configuration db,
5436: it will return that, otherwise it returns 20 (for backwards
5437: compatibility with domains which have not set up a configuration
5438: db file; the original statically defined portfolio quota was 20 Mb).
5439:
1.536 raeburn 5440: If the user's status includes multiple types (e.g., staff and student),
5441: the largest default quota which applies to the user determines the
5442: default quota returned.
5443:
1.472 raeburn 5444: =cut
5445:
5446: ###############################################
5447:
5448:
5449: sub default_quota {
1.536 raeburn 5450: my ($udom,$inststatus) = @_;
5451: my ($defquota,$settingstatus);
5452: my %quotahash = &Apache::lonnet::get_dom('configuration',
5453: ['quota'],$udom);
5454: if (ref($quotahash{'quota'}) eq 'HASH') {
5455: if ($inststatus ne '') {
5456: my @statuses = split(/:/,$inststatus);
5457: foreach my $item (@statuses) {
5458: if ($quotahash{'quota'}{$item} ne '') {
5459: if ($defquota eq '') {
5460: $defquota = $quotahash{'quota'}{$item};
5461: $settingstatus = $item;
5462: } elsif ($quotahash{'quota'}{$item} > $defquota) {
5463: $defquota = $quotahash{'quota'}{$item};
5464: $settingstatus = $item;
5465: }
5466: }
5467: }
5468: }
5469: if ($defquota eq '') {
5470: $defquota = $quotahash{'quota'}{'default'};
5471: $settingstatus = 'default';
5472: }
5473: } else {
5474: $settingstatus = 'default';
5475: $defquota = 20;
5476: }
5477: if (wantarray) {
5478: return ($defquota,$settingstatus);
1.472 raeburn 5479: } else {
1.536 raeburn 5480: return $defquota;
1.472 raeburn 5481: }
5482: }
5483:
1.384 raeburn 5484: sub get_secgrprole_info {
5485: my ($cdom,$cnum,$needroles,$type) = @_;
5486: my %sections_count = &get_sections($cdom,$cnum);
5487: my @sections = (sort {$a <=> $b} keys(%sections_count));
5488: my %curr_groups = &Apache::longroup::coursegroups($cdom,$cnum);
5489: my @groups = sort(keys(%curr_groups));
5490: my $allroles = [];
5491: my $rolehash;
5492: my $accesshash = {
5493: active => 'Currently has access',
5494: future => 'Will have future access',
5495: previous => 'Previously had access',
5496: };
5497: if ($needroles) {
5498: $rolehash = {'all' => 'all'};
1.385 albertel 5499: my %user_roles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
5500: if (&Apache::lonnet::error(%user_roles)) {
5501: undef(%user_roles);
5502: }
5503: foreach my $item (keys(%user_roles)) {
1.384 raeburn 5504: my ($role)=split(/\:/,$item,2);
5505: if ($role eq 'cr') { next; }
5506: if ($role =~ /^cr/) {
5507: $$rolehash{$role} = (split('/',$role))[3];
5508: } else {
5509: $$rolehash{$role} = &Apache::lonnet::plaintext($role,$type);
5510: }
5511: }
5512: foreach my $key (sort(keys(%{$rolehash}))) {
5513: push(@{$allroles},$key);
5514: }
5515: push (@{$allroles},'st');
5516: $$rolehash{'st'} = &Apache::lonnet::plaintext('st',$type);
5517: }
5518: return (\@sections,\@groups,$allroles,$rolehash,$accesshash);
5519: }
5520:
1.112 bowersj2 5521: =pod
5522:
1.549 albertel 5523: =back
5524:
5525: =head1 HTTP Helpers
5526:
5527: =over 4
5528:
1.112 bowersj2 5529: =item * get_unprocessed_cgi($query,$possible_names)
5530:
1.258 albertel 5531: Modify the %env hash to contain unprocessed CGI form parameters held in
1.112 bowersj2 5532: $query. The parameters listed in $possible_names (an array reference),
1.258 albertel 5533: will be set in $env{'form.name'} if they do not already exist.
1.112 bowersj2 5534:
5535: Typically called with $ENV{'QUERY_STRING'} as the first parameter.
5536: $possible_names is an ref to an array of form element names. As an example:
5537: get_unprocessed_cgi($ENV{'QUERY_STRING'},['uname','udom']);
1.258 albertel 5538: will result in $env{'form.uname'} and $env{'form.udom'} being set.
1.112 bowersj2 5539:
5540: =cut
1.1 albertel 5541:
1.6 albertel 5542: sub get_unprocessed_cgi {
1.25 albertel 5543: my ($query,$possible_names)= @_;
1.26 matthew 5544: # $Apache::lonxml::debug=1;
1.356 albertel 5545: foreach my $pair (split(/&/,$query)) {
5546: my ($name, $value) = split(/=/,$pair);
1.369 www 5547: $name = &unescape($name);
1.25 albertel 5548: if (!defined($possible_names) || (grep {$_ eq $name} @$possible_names)) {
5549: $value =~ tr/+/ /;
5550: $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
1.258 albertel 5551: unless (defined($env{'form.'.$name})) { &add_to_env('form.'.$name,$value) };
1.25 albertel 5552: }
1.16 harris41 5553: }
1.6 albertel 5554: }
5555:
1.112 bowersj2 5556: =pod
5557:
5558: =item * cacheheader()
5559:
5560: returns cache-controlling header code
5561:
5562: =cut
5563:
1.7 albertel 5564: sub cacheheader {
1.258 albertel 5565: unless ($env{'request.method'} eq 'GET') { return ''; }
1.216 albertel 5566: my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime);
5567: my $output .='<meta HTTP-EQUIV="Expires" CONTENT="'.$date.'" />
1.7 albertel 5568: <meta HTTP-EQUIV="Cache-control" CONTENT="no-cache" />
5569: <meta HTTP-EQUIV="Pragma" CONTENT="no-cache" />';
1.216 albertel 5570: return $output;
1.7 albertel 5571: }
5572:
1.112 bowersj2 5573: =pod
5574:
5575: =item * no_cache($r)
5576:
5577: specifies header code to not have cache
5578:
5579: =cut
5580:
1.9 albertel 5581: sub no_cache {
1.216 albertel 5582: my ($r) = @_;
5583: if ($ENV{'REQUEST_METHOD'} ne 'GET' &&
1.258 albertel 5584: $env{'request.method'} ne 'GET') { return ''; }
1.216 albertel 5585: my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime(time));
5586: $r->no_cache(1);
5587: $r->header_out("Expires" => $date);
5588: $r->header_out("Pragma" => "no-cache");
1.123 www 5589: }
5590:
5591: sub content_type {
1.181 albertel 5592: my ($r,$type,$charset) = @_;
1.299 foxr 5593: if ($r) {
5594: # Note that printout.pl calls this with undef for $r.
5595: &no_cache($r);
5596: }
1.258 albertel 5597: if ($env{'browser.mathml'} && $type eq 'text/html') { $type='text/xml'; }
1.181 albertel 5598: unless ($charset) {
5599: $charset=&Apache::lonlocal::current_encoding;
5600: }
5601: if ($charset) { $type.='; charset='.$charset; }
5602: if ($r) {
5603: $r->content_type($type);
5604: } else {
5605: print("Content-type: $type\n\n");
5606: }
1.9 albertel 5607: }
1.25 albertel 5608:
1.112 bowersj2 5609: =pod
5610:
5611: =item * add_to_env($name,$value)
5612:
1.258 albertel 5613: adds $name to the %env hash with value
1.112 bowersj2 5614: $value, if $name already exists, the entry is converted to an array
5615: reference and $value is added to the array.
5616:
5617: =cut
5618:
1.25 albertel 5619: sub add_to_env {
5620: my ($name,$value)=@_;
1.258 albertel 5621: if (defined($env{$name})) {
5622: if (ref($env{$name})) {
1.25 albertel 5623: #already have multiple values
1.258 albertel 5624: push(@{ $env{$name} },$value);
1.25 albertel 5625: } else {
5626: #first time seeing multiple values, convert hash entry to an arrayref
1.258 albertel 5627: my $first=$env{$name};
5628: undef($env{$name});
5629: push(@{ $env{$name} },$first,$value);
1.25 albertel 5630: }
5631: } else {
1.258 albertel 5632: $env{$name}=$value;
1.25 albertel 5633: }
1.31 albertel 5634: }
1.149 albertel 5635:
5636: =pod
5637:
5638: =item * get_env_multiple($name)
5639:
1.258 albertel 5640: gets $name from the %env hash, it seemlessly handles the cases where multiple
1.149 albertel 5641: values may be defined and end up as an array ref.
5642:
5643: returns an array of values
5644:
5645: =cut
5646:
5647: sub get_env_multiple {
5648: my ($name) = @_;
5649: my @values;
1.258 albertel 5650: if (defined($env{$name})) {
1.149 albertel 5651: # exists is it an array
1.258 albertel 5652: if (ref($env{$name})) {
5653: @values=@{ $env{$name} };
1.149 albertel 5654: } else {
1.258 albertel 5655: $values[0]=$env{$name};
1.149 albertel 5656: }
5657: }
5658: return(@values);
5659: }
5660:
1.31 albertel 5661:
1.41 ng 5662: =pod
1.45 matthew 5663:
1.464 albertel 5664: =back
1.41 ng 5665:
1.112 bowersj2 5666: =head1 CSV Upload/Handling functions
1.38 albertel 5667:
1.41 ng 5668: =over 4
5669:
1.112 bowersj2 5670: =item * upfile_store($r)
1.41 ng 5671:
5672: Store uploaded file, $r should be the HTTP Request object,
1.258 albertel 5673: needs $env{'form.upfile'}
1.41 ng 5674: returns $datatoken to be put into hidden field
5675:
5676: =cut
1.31 albertel 5677:
5678: sub upfile_store {
5679: my $r=shift;
1.258 albertel 5680: $env{'form.upfile'}=~s/\r/\n/gs;
5681: $env{'form.upfile'}=~s/\f/\n/gs;
5682: $env{'form.upfile'}=~s/\n+/\n/gs;
5683: $env{'form.upfile'}=~s/\n+$//gs;
1.31 albertel 5684:
1.258 albertel 5685: my $datatoken=$env{'user.name'}.'_'.$env{'user.domain'}.
5686: '_enroll_'.$env{'request.course.id'}.'_'.time.'_'.$$;
1.31 albertel 5687: {
1.158 raeburn 5688: my $datafile = $r->dir_config('lonDaemons').
5689: '/tmp/'.$datatoken.'.tmp';
5690: if ( open(my $fh,">$datafile") ) {
1.258 albertel 5691: print $fh $env{'form.upfile'};
1.158 raeburn 5692: close($fh);
5693: }
1.31 albertel 5694: }
5695: return $datatoken;
5696: }
5697:
1.56 matthew 5698: =pod
5699:
1.112 bowersj2 5700: =item * load_tmp_file($r)
1.41 ng 5701:
5702: Load uploaded file from tmp, $r should be the HTTP Request object,
1.258 albertel 5703: needs $env{'form.datatoken'},
5704: sets $env{'form.upfile'} to the contents of the file
1.41 ng 5705:
5706: =cut
1.31 albertel 5707:
5708: sub load_tmp_file {
5709: my $r=shift;
5710: my @studentdata=();
5711: {
1.158 raeburn 5712: my $studentfile = $r->dir_config('lonDaemons').
1.258 albertel 5713: '/tmp/'.$env{'form.datatoken'}.'.tmp';
1.158 raeburn 5714: if ( open(my $fh,"<$studentfile") ) {
5715: @studentdata=<$fh>;
5716: close($fh);
5717: }
1.31 albertel 5718: }
1.258 albertel 5719: $env{'form.upfile'}=join('',@studentdata);
1.31 albertel 5720: }
5721:
1.56 matthew 5722: =pod
5723:
1.112 bowersj2 5724: =item * upfile_record_sep()
1.41 ng 5725:
5726: Separate uploaded file into records
5727: returns array of records,
1.258 albertel 5728: needs $env{'form.upfile'} and $env{'form.upfiletype'}
1.41 ng 5729:
5730: =cut
1.31 albertel 5731:
5732: sub upfile_record_sep {
1.258 albertel 5733: if ($env{'form.upfiletype'} eq 'xml') {
1.31 albertel 5734: } else {
1.248 albertel 5735: my @records;
1.258 albertel 5736: foreach my $line (split(/\n/,$env{'form.upfile'})) {
1.248 albertel 5737: if ($line=~/^\s*$/) { next; }
5738: push(@records,$line);
5739: }
5740: return @records;
1.31 albertel 5741: }
5742: }
5743:
1.56 matthew 5744: =pod
5745:
1.112 bowersj2 5746: =item * record_sep($record)
1.41 ng 5747:
1.258 albertel 5748: Separate a record into fields $record should be an item from the upfile_record_sep(), needs $env{'form.upfiletype'}
1.41 ng 5749:
5750: =cut
5751:
1.263 www 5752: sub takeleft {
5753: my $index=shift;
5754: return substr('0000'.$index,-4,4);
5755: }
5756:
1.31 albertel 5757: sub record_sep {
5758: my $record=shift;
5759: my %components=();
1.258 albertel 5760: if ($env{'form.upfiletype'} eq 'xml') {
5761: } elsif ($env{'form.upfiletype'} eq 'space') {
1.31 albertel 5762: my $i=0;
1.356 albertel 5763: foreach my $field (split(/\s+/,$record)) {
1.31 albertel 5764: $field=~s/^(\"|\')//;
5765: $field=~s/(\"|\')$//;
1.263 www 5766: $components{&takeleft($i)}=$field;
1.31 albertel 5767: $i++;
5768: }
1.258 albertel 5769: } elsif ($env{'form.upfiletype'} eq 'tab') {
1.31 albertel 5770: my $i=0;
1.356 albertel 5771: foreach my $field (split(/\t/,$record)) {
1.31 albertel 5772: $field=~s/^(\"|\')//;
5773: $field=~s/(\"|\')$//;
1.263 www 5774: $components{&takeleft($i)}=$field;
1.31 albertel 5775: $i++;
5776: }
5777: } else {
1.480 banghart 5778: my @allfields;
5779: if ($env{'form.upfiletype'} eq 'semisv') {
1.504 albertel 5780: @allfields=split(/;/,$record,-1);
1.480 banghart 5781: } else {
1.504 albertel 5782: @allfields=split(/\,/,$record,-1);
1.480 banghart 5783: }
1.31 albertel 5784: my $i=0;
5785: my $j;
5786: for ($j=0;$j<=$#allfields;$j++) {
5787: my $field=$allfields[$j];
5788: if ($field=~/^\s*(\"|\')/) {
5789: my $delimiter=$1;
5790: while (($field!~/$delimiter$/) && ($j<$#allfields)) {
5791: $j++;
5792: $field.=','.$allfields[$j];
5793: }
5794: $field=~s/^\s*$delimiter//;
5795: $field=~s/$delimiter\s*$//;
5796: }
1.263 www 5797: $components{&takeleft($i)}=$field;
1.31 albertel 5798: $i++;
5799: }
5800: }
5801: return %components;
5802: }
5803:
1.144 matthew 5804: ######################################################
5805: ######################################################
5806:
1.56 matthew 5807: =pod
5808:
1.112 bowersj2 5809: =item * upfile_select_html()
1.41 ng 5810:
1.144 matthew 5811: Return HTML code to select a file from the users machine and specify
5812: the file type.
1.41 ng 5813:
5814: =cut
5815:
1.144 matthew 5816: ######################################################
5817: ######################################################
1.31 albertel 5818: sub upfile_select_html {
1.144 matthew 5819: my %Types = (
5820: csv => &mt('CSV (comma separated values, spreadsheet)'),
1.480 banghart 5821: semisv => &mt('Semicolon separated values'),
1.144 matthew 5822: space => &mt('Space separated'),
5823: tab => &mt('Tabulator separated'),
5824: # xml => &mt('HTML/XML'),
5825: );
5826: my $Str = '<input type="file" name="upfile" size="50" />'.
5827: '<br />Type: <select name="upfiletype">';
5828: foreach my $type (sort(keys(%Types))) {
5829: $Str .= '<option value="'.$type.'" >'.$Types{$type}."</option>\n";
5830: }
5831: $Str .= "</select>\n";
5832: return $Str;
1.31 albertel 5833: }
5834:
1.301 albertel 5835: sub get_samples {
5836: my ($records,$toget) = @_;
5837: my @samples=({});
5838: my $got=0;
5839: foreach my $rec (@$records) {
5840: my %temp = &record_sep($rec);
5841: if (! grep(/\S/, values(%temp))) { next; }
5842: if (%temp) {
5843: $samples[$got]=\%temp;
5844: $got++;
5845: if ($got == $toget) { last; }
5846: }
5847: }
5848: return \@samples;
5849: }
5850:
1.144 matthew 5851: ######################################################
5852: ######################################################
5853:
1.56 matthew 5854: =pod
5855:
1.112 bowersj2 5856: =item * csv_print_samples($r,$records)
1.41 ng 5857:
5858: Prints a table of sample values from each column uploaded $r is an
5859: Apache Request ref, $records is an arrayref from
5860: &Apache::loncommon::upfile_record_sep
5861:
5862: =cut
5863:
1.144 matthew 5864: ######################################################
5865: ######################################################
1.31 albertel 5866: sub csv_print_samples {
5867: my ($r,$records) = @_;
1.301 albertel 5868: my $samples = &get_samples($records,3);
5869:
1.144 matthew 5870: $r->print(&mt('Samples').'<br /><table border="2"><tr>');
1.356 albertel 5871: foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
5872: $r->print('<th>'.&mt('Column [_1]',($sample+1)).'</th>'); }
1.31 albertel 5873: $r->print('</tr>');
1.301 albertel 5874: foreach my $hash (@$samples) {
1.31 albertel 5875: $r->print('<tr>');
1.356 albertel 5876: foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
1.31 albertel 5877: $r->print('<td>');
1.356 albertel 5878: if (defined($$hash{$sample})) { $r->print($$hash{$sample}); }
1.31 albertel 5879: $r->print('</td>');
5880: }
5881: $r->print('</tr>');
5882: }
5883: $r->print('</tr></table><br />'."\n");
5884: }
5885:
1.144 matthew 5886: ######################################################
5887: ######################################################
5888:
1.56 matthew 5889: =pod
5890:
1.112 bowersj2 5891: =item * csv_print_select_table($r,$records,$d)
1.41 ng 5892:
5893: Prints a table to create associations between values and table columns.
1.144 matthew 5894:
1.41 ng 5895: $r is an Apache Request ref,
5896: $records is an arrayref from &Apache::loncommon::upfile_record_sep,
1.174 matthew 5897: $d is an array of 2 element arrays (internal name, displayed name,defaultcol)
1.41 ng 5898:
5899: =cut
5900:
1.144 matthew 5901: ######################################################
5902: ######################################################
1.31 albertel 5903: sub csv_print_select_table {
5904: my ($r,$records,$d) = @_;
1.301 albertel 5905: my $i=0;
5906: my $samples = &get_samples($records,1);
1.144 matthew 5907: $r->print(&mt('Associate columns with student attributes.')."\n".
5908: '<table border="2"><tr>'.
5909: '<th>'.&mt('Attribute').'</th>'.
5910: '<th>'.&mt('Column').'</th></tr>'."\n");
1.356 albertel 5911: foreach my $array_ref (@$d) {
5912: my ($value,$display,$defaultcol)=@{ $array_ref };
1.31 albertel 5913: $r->print('<tr><td>'.$display.'</td>');
5914:
5915: $r->print('<td><select name=f'.$i.
1.32 matthew 5916: ' onchange="javascript:flip(this.form,'.$i.');">');
1.31 albertel 5917: $r->print('<option value="none"></option>');
1.356 albertel 5918: foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
5919: $r->print('<option value="'.$sample.'"'.
5920: ($sample eq $defaultcol ? ' selected="selected" ' : '').
5921: '>Column '.($sample+1).'</option>');
1.31 albertel 5922: }
5923: $r->print('</select></td></tr>'."\n");
5924: $i++;
5925: }
5926: $i--;
5927: return $i;
5928: }
1.56 matthew 5929:
1.144 matthew 5930: ######################################################
5931: ######################################################
5932:
1.56 matthew 5933: =pod
1.31 albertel 5934:
1.112 bowersj2 5935: =item * csv_samples_select_table($r,$records,$d)
1.41 ng 5936:
5937: Prints a table of sample values from the upload and can make associate samples to internal names.
5938:
5939: $r is an Apache Request ref,
5940: $records is an arrayref from &Apache::loncommon::upfile_record_sep,
5941: $d is an array of 2 element arrays (internal name, displayed name)
5942:
5943: =cut
5944:
1.144 matthew 5945: ######################################################
5946: ######################################################
1.31 albertel 5947: sub csv_samples_select_table {
5948: my ($r,$records,$d) = @_;
5949: my $i=0;
1.144 matthew 5950: #
1.301 albertel 5951: my $samples = &get_samples($records,3);
1.144 matthew 5952: $r->print('<table border=2><tr><th>'.
5953: &mt('Field').'</th><th>'.&mt('Samples').'</th></tr>');
1.301 albertel 5954:
5955: foreach my $key (sort(keys(%{ $samples->[0] }))) {
1.144 matthew 5956: $r->print('<tr><td><select name="f'.$i.'"'.
1.32 matthew 5957: ' onchange="javascript:flip(this.form,'.$i.');">');
1.301 albertel 5958: foreach my $option (@$d) {
5959: my ($value,$display,$defaultcol)=@{ $option };
1.174 matthew 5960: $r->print('<option value="'.$value.'"'.
1.253 albertel 5961: ($i eq $defaultcol ? ' selected="selected" ':'').'>'.
1.174 matthew 5962: $display.'</option>');
1.31 albertel 5963: }
5964: $r->print('</select></td><td>');
1.301 albertel 5965: foreach my $line (0..2) {
5966: if (defined($samples->[$line]{$key})) {
5967: $r->print($samples->[$line]{$key}."<br />\n");
5968: }
5969: }
1.31 albertel 5970: $r->print('</td></tr>');
5971: $i++;
5972: }
5973: $i--;
5974: return($i);
1.115 matthew 5975: }
5976:
1.144 matthew 5977: ######################################################
5978: ######################################################
5979:
1.115 matthew 5980: =pod
5981:
5982: =item clean_excel_name($name)
5983:
5984: Returns a replacement for $name which does not contain any illegal characters.
5985:
5986: =cut
5987:
1.144 matthew 5988: ######################################################
5989: ######################################################
1.115 matthew 5990: sub clean_excel_name {
5991: my ($name) = @_;
5992: $name =~ s/[:\*\?\/\\]//g;
5993: if (length($name) > 31) {
5994: $name = substr($name,0,31);
5995: }
5996: return $name;
1.25 albertel 5997: }
1.84 albertel 5998:
1.85 albertel 5999: =pod
6000:
1.112 bowersj2 6001: =item * check_if_partid_hidden($id,$symb,$udom,$uname)
1.85 albertel 6002:
6003: Returns either 1 or undef
6004:
6005: 1 if the part is to be hidden, undef if it is to be shown
6006:
6007: Arguments are:
6008:
6009: $id the id of the part to be checked
6010: $symb, optional the symb of the resource to check
6011: $udom, optional the domain of the user to check for
6012: $uname, optional the username of the user to check for
6013:
6014: =cut
1.84 albertel 6015:
6016: sub check_if_partid_hidden {
6017: my ($id,$symb,$udom,$uname) = @_;
1.133 albertel 6018: my $hiddenparts=&Apache::lonnet::EXT('resource.0.hiddenparts',
1.84 albertel 6019: $symb,$udom,$uname);
1.141 albertel 6020: my $truth=1;
6021: #if the string starts with !, then the list is the list to show not hide
6022: if ($hiddenparts=~s/^\s*!//) { $truth=undef; }
1.84 albertel 6023: my @hiddenlist=split(/,/,$hiddenparts);
6024: foreach my $checkid (@hiddenlist) {
1.141 albertel 6025: if ($checkid =~ /^\s*\Q$id\E\s*$/) { return $truth; }
1.84 albertel 6026: }
1.141 albertel 6027: return !$truth;
1.84 albertel 6028: }
1.127 matthew 6029:
1.138 matthew 6030:
6031: ############################################################
6032: ############################################################
6033:
6034: =pod
6035:
1.157 matthew 6036: =back
6037:
1.138 matthew 6038: =head1 cgi-bin script and graphing routines
6039:
1.157 matthew 6040: =over 4
6041:
1.138 matthew 6042: =item get_cgi_id
6043:
6044: Inputs: none
6045:
6046: Returns an id which can be used to pass environment variables
6047: to various cgi-bin scripts. These environment variables will
6048: be removed from the users environment after a given time by
6049: the routine &Apache::lonnet::transfer_profile_to_env.
6050:
6051: =cut
6052:
6053: ############################################################
6054: ############################################################
1.152 albertel 6055: my $uniq=0;
1.136 matthew 6056: sub get_cgi_id {
1.154 albertel 6057: $uniq=($uniq+1)%100000;
1.280 albertel 6058: return (time.'_'.$$.'_'.$uniq);
1.136 matthew 6059: }
6060:
1.127 matthew 6061: ############################################################
6062: ############################################################
6063:
6064: =pod
6065:
1.134 matthew 6066: =item DrawBarGraph
1.127 matthew 6067:
1.138 matthew 6068: Facilitates the plotting of data in a (stacked) bar graph.
6069: Puts plot definition data into the users environment in order for
6070: graph.png to plot it. Returns an <img> tag for the plot.
6071: The bars on the plot are labeled '1','2',...,'n'.
6072:
6073: Inputs:
6074:
6075: =over 4
6076:
6077: =item $Title: string, the title of the plot
6078:
6079: =item $xlabel: string, text describing the X-axis of the plot
6080:
6081: =item $ylabel: string, text describing the Y-axis of the plot
6082:
6083: =item $Max: scalar, the maximum Y value to use in the plot
6084: If $Max is < any data point, the graph will not be rendered.
6085:
1.140 matthew 6086: =item $colors: array ref holding the colors to be used for the data sets when
1.138 matthew 6087: they are plotted. If undefined, default values will be used.
6088:
1.178 matthew 6089: =item $labels: array ref holding the labels to use on the x-axis for the bars.
6090:
1.138 matthew 6091: =item @Values: An array of array references. Each array reference holds data
6092: to be plotted in a stacked bar chart.
6093:
1.239 matthew 6094: =item If the final element of @Values is a hash reference the key/value
6095: pairs will be added to the graph definition.
6096:
1.138 matthew 6097: =back
6098:
6099: Returns:
6100:
6101: An <img> tag which references graph.png and the appropriate identifying
6102: information for the plot.
6103:
1.127 matthew 6104: =cut
6105:
6106: ############################################################
6107: ############################################################
1.134 matthew 6108: sub DrawBarGraph {
1.178 matthew 6109: my ($Title,$xlabel,$ylabel,$Max,$colors,$labels,@Values)=@_;
1.134 matthew 6110: #
6111: if (! defined($colors)) {
6112: $colors = ['#33ff00',
6113: '#0033cc', '#990000', '#aaaa66', '#663399', '#ff9933',
6114: '#66ccff', '#ff9999', '#cccc33', '#660000', '#33cc66',
6115: ];
6116: }
1.228 matthew 6117: my $extra_settings = {};
6118: if (ref($Values[-1]) eq 'HASH') {
6119: $extra_settings = pop(@Values);
6120: }
1.127 matthew 6121: #
1.136 matthew 6122: my $identifier = &get_cgi_id();
6123: my $id = 'cgi.'.$identifier;
1.129 matthew 6124: if (! @Values || ref($Values[0]) ne 'ARRAY') {
1.127 matthew 6125: return '';
6126: }
1.225 matthew 6127: #
6128: my @Labels;
6129: if (defined($labels)) {
6130: @Labels = @$labels;
6131: } else {
6132: for (my $i=0;$i<@{$Values[0]};$i++) {
6133: push (@Labels,$i+1);
6134: }
6135: }
6136: #
1.129 matthew 6137: my $NumBars = scalar(@{$Values[0]});
1.225 matthew 6138: if ($NumBars < scalar(@Labels)) { $NumBars = scalar(@Labels); }
1.129 matthew 6139: my %ValuesHash;
6140: my $NumSets=1;
6141: foreach my $array (@Values) {
6142: next if (! ref($array));
1.136 matthew 6143: $ValuesHash{$id.'.data.'.$NumSets++} =
1.132 matthew 6144: join(',',@$array);
1.129 matthew 6145: }
1.127 matthew 6146: #
1.136 matthew 6147: my ($height,$width,$xskip,$bar_width) = (200,120,1,15);
1.225 matthew 6148: if ($NumBars < 3) {
6149: $width = 120+$NumBars*32;
1.220 matthew 6150: $xskip = 1;
1.225 matthew 6151: $bar_width = 30;
6152: } elsif ($NumBars < 5) {
6153: $width = 120+$NumBars*20;
6154: $xskip = 1;
6155: $bar_width = 20;
1.220 matthew 6156: } elsif ($NumBars < 10) {
1.136 matthew 6157: $width = 120+$NumBars*15;
6158: $xskip = 1;
6159: $bar_width = 15;
6160: } elsif ($NumBars <= 25) {
6161: $width = 120+$NumBars*11;
6162: $xskip = 5;
6163: $bar_width = 8;
6164: } elsif ($NumBars <= 50) {
6165: $width = 120+$NumBars*8;
6166: $xskip = 5;
6167: $bar_width = 4;
6168: } else {
6169: $width = 120+$NumBars*8;
6170: $xskip = 5;
6171: $bar_width = 4;
6172: }
6173: #
1.137 matthew 6174: $Max = 1 if ($Max < 1);
6175: if ( int($Max) < $Max ) {
6176: $Max++;
6177: $Max = int($Max);
6178: }
1.127 matthew 6179: $Title = '' if (! defined($Title));
6180: $xlabel = '' if (! defined($xlabel));
6181: $ylabel = '' if (! defined($ylabel));
1.369 www 6182: $ValuesHash{$id.'.title'} = &escape($Title);
6183: $ValuesHash{$id.'.xlabel'} = &escape($xlabel);
6184: $ValuesHash{$id.'.ylabel'} = &escape($ylabel);
1.137 matthew 6185: $ValuesHash{$id.'.y_max_value'} = $Max;
1.136 matthew 6186: $ValuesHash{$id.'.NumBars'} = $NumBars;
6187: $ValuesHash{$id.'.NumSets'} = $NumSets;
6188: $ValuesHash{$id.'.PlotType'} = 'bar';
6189: $ValuesHash{$id.'.Colors'} = join(',',@{$colors});
6190: $ValuesHash{$id.'.height'} = $height;
6191: $ValuesHash{$id.'.width'} = $width;
6192: $ValuesHash{$id.'.xskip'} = $xskip;
6193: $ValuesHash{$id.'.bar_width'} = $bar_width;
6194: $ValuesHash{$id.'.labels'} = join(',',@Labels);
1.127 matthew 6195: #
1.228 matthew 6196: # Deal with other parameters
6197: while (my ($key,$value) = each(%$extra_settings)) {
6198: $ValuesHash{$id.'.'.$key} = $value;
6199: }
6200: #
1.137 matthew 6201: &Apache::lonnet::appenv(%ValuesHash);
6202: return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
6203: }
6204:
6205: ############################################################
6206: ############################################################
6207:
6208: =pod
6209:
6210: =item DrawXYGraph
6211:
1.138 matthew 6212: Facilitates the plotting of data in an XY graph.
6213: Puts plot definition data into the users environment in order for
6214: graph.png to plot it. Returns an <img> tag for the plot.
6215:
6216: Inputs:
6217:
6218: =over 4
6219:
6220: =item $Title: string, the title of the plot
6221:
6222: =item $xlabel: string, text describing the X-axis of the plot
6223:
6224: =item $ylabel: string, text describing the Y-axis of the plot
6225:
6226: =item $Max: scalar, the maximum Y value to use in the plot
6227: If $Max is < any data point, the graph will not be rendered.
6228:
6229: =item $colors: Array ref containing the hex color codes for the data to be
6230: plotted in. If undefined, default values will be used.
6231:
6232: =item $Xlabels: Array ref containing the labels to be used for the X-axis.
6233:
6234: =item $Ydata: Array ref containing Array refs.
1.185 www 6235: Each of the contained arrays will be plotted as a separate curve.
1.138 matthew 6236:
6237: =item %Values: hash indicating or overriding any default values which are
6238: passed to graph.png.
6239: Possible values are: width, xskip, x_ticks, x_tick_offset, among others.
6240:
6241: =back
6242:
6243: Returns:
6244:
6245: An <img> tag which references graph.png and the appropriate identifying
6246: information for the plot.
6247:
1.137 matthew 6248: =cut
6249:
6250: ############################################################
6251: ############################################################
6252: sub DrawXYGraph {
6253: my ($Title,$xlabel,$ylabel,$Max,$colors,$Xlabels,$Ydata,%Values)=@_;
6254: #
6255: # Create the identifier for the graph
6256: my $identifier = &get_cgi_id();
6257: my $id = 'cgi.'.$identifier;
6258: #
6259: $Title = '' if (! defined($Title));
6260: $xlabel = '' if (! defined($xlabel));
6261: $ylabel = '' if (! defined($ylabel));
6262: my %ValuesHash =
6263: (
1.369 www 6264: $id.'.title' => &escape($Title),
6265: $id.'.xlabel' => &escape($xlabel),
6266: $id.'.ylabel' => &escape($ylabel),
1.137 matthew 6267: $id.'.y_max_value'=> $Max,
6268: $id.'.labels' => join(',',@$Xlabels),
6269: $id.'.PlotType' => 'XY',
6270: );
6271: #
6272: if (defined($colors) && ref($colors) eq 'ARRAY') {
6273: $ValuesHash{$id.'.Colors'} = join(',',@{$colors});
6274: }
6275: #
6276: if (! ref($Ydata) || ref($Ydata) ne 'ARRAY') {
6277: return '';
6278: }
6279: my $NumSets=1;
1.138 matthew 6280: foreach my $array (@{$Ydata}){
1.137 matthew 6281: next if (! ref($array));
6282: $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array);
6283: }
1.138 matthew 6284: $ValuesHash{$id.'.NumSets'} = $NumSets-1;
1.137 matthew 6285: #
6286: # Deal with other parameters
6287: while (my ($key,$value) = each(%Values)) {
6288: $ValuesHash{$id.'.'.$key} = $value;
1.127 matthew 6289: }
6290: #
1.136 matthew 6291: &Apache::lonnet::appenv(%ValuesHash);
6292: return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
6293: }
6294:
6295: ############################################################
6296: ############################################################
6297:
6298: =pod
6299:
1.138 matthew 6300: =item DrawXYYGraph
6301:
6302: Facilitates the plotting of data in an XY graph with two Y axes.
6303: Puts plot definition data into the users environment in order for
6304: graph.png to plot it. Returns an <img> tag for the plot.
6305:
6306: Inputs:
6307:
6308: =over 4
6309:
6310: =item $Title: string, the title of the plot
6311:
6312: =item $xlabel: string, text describing the X-axis of the plot
6313:
6314: =item $ylabel: string, text describing the Y-axis of the plot
6315:
6316: =item $colors: Array ref containing the hex color codes for the data to be
6317: plotted in. If undefined, default values will be used.
6318:
6319: =item $Xlabels: Array ref containing the labels to be used for the X-axis.
6320:
6321: =item $Ydata1: The first data set
6322:
6323: =item $Min1: The minimum value of the left Y-axis
6324:
6325: =item $Max1: The maximum value of the left Y-axis
6326:
6327: =item $Ydata2: The second data set
6328:
6329: =item $Min2: The minimum value of the right Y-axis
6330:
6331: =item $Max2: The maximum value of the left Y-axis
6332:
6333: =item %Values: hash indicating or overriding any default values which are
6334: passed to graph.png.
6335: Possible values are: width, xskip, x_ticks, x_tick_offset, among others.
6336:
6337: =back
6338:
6339: Returns:
6340:
6341: An <img> tag which references graph.png and the appropriate identifying
6342: information for the plot.
1.136 matthew 6343:
6344: =cut
6345:
6346: ############################################################
6347: ############################################################
1.137 matthew 6348: sub DrawXYYGraph {
6349: my ($Title,$xlabel,$ylabel,$colors,$Xlabels,$Ydata1,$Min1,$Max1,
6350: $Ydata2,$Min2,$Max2,%Values)=@_;
1.136 matthew 6351: #
6352: # Create the identifier for the graph
6353: my $identifier = &get_cgi_id();
6354: my $id = 'cgi.'.$identifier;
6355: #
6356: $Title = '' if (! defined($Title));
6357: $xlabel = '' if (! defined($xlabel));
6358: $ylabel = '' if (! defined($ylabel));
6359: my %ValuesHash =
6360: (
1.369 www 6361: $id.'.title' => &escape($Title),
6362: $id.'.xlabel' => &escape($xlabel),
6363: $id.'.ylabel' => &escape($ylabel),
1.136 matthew 6364: $id.'.labels' => join(',',@$Xlabels),
6365: $id.'.PlotType' => 'XY',
6366: $id.'.NumSets' => 2,
1.137 matthew 6367: $id.'.two_axes' => 1,
6368: $id.'.y1_max_value' => $Max1,
6369: $id.'.y1_min_value' => $Min1,
6370: $id.'.y2_max_value' => $Max2,
6371: $id.'.y2_min_value' => $Min2,
1.136 matthew 6372: );
6373: #
1.137 matthew 6374: if (defined($colors) && ref($colors) eq 'ARRAY') {
6375: $ValuesHash{$id.'.Colors'} = join(',',@{$colors});
6376: }
6377: #
6378: if (! ref($Ydata1) || ref($Ydata1) ne 'ARRAY' ||
6379: ! ref($Ydata2) || ref($Ydata2) ne 'ARRAY'){
1.136 matthew 6380: return '';
6381: }
6382: my $NumSets=1;
1.137 matthew 6383: foreach my $array ($Ydata1,$Ydata2){
1.136 matthew 6384: next if (! ref($array));
6385: $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array);
1.137 matthew 6386: }
6387: #
6388: # Deal with other parameters
6389: while (my ($key,$value) = each(%Values)) {
6390: $ValuesHash{$id.'.'.$key} = $value;
1.136 matthew 6391: }
6392: #
6393: &Apache::lonnet::appenv(%ValuesHash);
1.130 albertel 6394: return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
1.139 matthew 6395: }
6396:
6397: ############################################################
6398: ############################################################
6399:
6400: =pod
6401:
1.157 matthew 6402: =back
6403:
1.139 matthew 6404: =head1 Statistics helper routines?
6405:
6406: Bad place for them but what the hell.
6407:
1.157 matthew 6408: =over 4
6409:
1.139 matthew 6410: =item &chartlink
6411:
6412: Returns a link to the chart for a specific student.
6413:
6414: Inputs:
6415:
6416: =over 4
6417:
6418: =item $linktext: The text of the link
6419:
6420: =item $sname: The students username
6421:
6422: =item $sdomain: The students domain
6423:
6424: =back
6425:
1.157 matthew 6426: =back
6427:
1.139 matthew 6428: =cut
6429:
6430: ############################################################
6431: ############################################################
6432: sub chartlink {
6433: my ($linktext, $sname, $sdomain) = @_;
6434: my $link = '<a href="/adm/statistics?reportSelected=student_assessment'.
1.369 www 6435: '&SelectedStudent='.&escape($sname.':'.$sdomain).
1.219 albertel 6436: '&chartoutputmode='.HTML::Entities::encode('html, with all links').
1.139 matthew 6437: '">'.$linktext.'</a>';
1.153 matthew 6438: }
6439:
6440: #######################################################
6441: #######################################################
6442:
6443: =pod
6444:
6445: =head1 Course Environment Routines
1.157 matthew 6446:
6447: =over 4
1.153 matthew 6448:
6449: =item &restore_course_settings
6450:
6451: =item &store_course_settings
6452:
6453: Restores/Store indicated form parameters from the course environment.
6454: Will not overwrite existing values of the form parameters.
6455:
6456: Inputs:
6457: a scalar describing the data (e.g. 'chart', 'problem_analysis')
6458:
6459: a hash ref describing the data to be stored. For example:
6460:
6461: %Save_Parameters = ('Status' => 'scalar',
6462: 'chartoutputmode' => 'scalar',
6463: 'chartoutputdata' => 'scalar',
6464: 'Section' => 'array',
1.373 raeburn 6465: 'Group' => 'array',
1.153 matthew 6466: 'StudentData' => 'array',
6467: 'Maps' => 'array');
6468:
6469: Returns: both routines return nothing
6470:
6471: =cut
6472:
6473: #######################################################
6474: #######################################################
6475: sub store_course_settings {
1.496 albertel 6476: return &store_settings($env{'request.course.id'},@_);
6477: }
6478:
6479: sub store_settings {
1.153 matthew 6480: # save to the environment
6481: # appenv the same items, just to be safe
1.300 albertel 6482: my $udom = $env{'user.domain'};
6483: my $uname = $env{'user.name'};
1.496 albertel 6484: my ($context,$prefix,$Settings) = @_;
1.153 matthew 6485: my %SaveHash;
6486: my %AppHash;
6487: while (my ($setting,$type) = each(%$Settings)) {
1.496 albertel 6488: my $basename = join('.','internal',$context,$prefix,$setting);
1.300 albertel 6489: my $envname = 'environment.'.$basename;
1.258 albertel 6490: if (exists($env{'form.'.$setting})) {
1.153 matthew 6491: # Save this value away
6492: if ($type eq 'scalar' &&
1.258 albertel 6493: (! exists($env{$envname}) ||
6494: $env{$envname} ne $env{'form.'.$setting})) {
6495: $SaveHash{$basename} = $env{'form.'.$setting};
6496: $AppHash{$envname} = $env{'form.'.$setting};
1.153 matthew 6497: } elsif ($type eq 'array') {
6498: my $stored_form;
1.258 albertel 6499: if (ref($env{'form.'.$setting})) {
1.153 matthew 6500: $stored_form = join(',',
6501: map {
1.369 www 6502: &escape($_);
1.258 albertel 6503: } sort(@{$env{'form.'.$setting}}));
1.153 matthew 6504: } else {
6505: $stored_form =
1.369 www 6506: &escape($env{'form.'.$setting});
1.153 matthew 6507: }
6508: # Determine if the array contents are the same.
1.258 albertel 6509: if ($stored_form ne $env{$envname}) {
1.153 matthew 6510: $SaveHash{$basename} = $stored_form;
6511: $AppHash{$envname} = $stored_form;
6512: }
6513: }
6514: }
6515: }
6516: my $put_result = &Apache::lonnet::put('environment',\%SaveHash,
1.300 albertel 6517: $udom,$uname);
1.153 matthew 6518: if ($put_result !~ /^(ok|delayed)/) {
6519: &Apache::lonnet::logthis('unable to save form parameters, '.
6520: 'got error:'.$put_result);
6521: }
6522: # Make sure these settings stick around in this session, too
6523: &Apache::lonnet::appenv(%AppHash);
6524: return;
6525: }
6526:
6527: sub restore_course_settings {
1.499 albertel 6528: return &restore_settings($env{'request.course.id'},@_);
1.496 albertel 6529: }
6530:
6531: sub restore_settings {
6532: my ($context,$prefix,$Settings) = @_;
1.153 matthew 6533: while (my ($setting,$type) = each(%$Settings)) {
1.258 albertel 6534: next if (exists($env{'form.'.$setting}));
1.496 albertel 6535: my $envname = 'environment.internal.'.$context.'.'.$prefix.
1.153 matthew 6536: '.'.$setting;
1.258 albertel 6537: if (exists($env{$envname})) {
1.153 matthew 6538: if ($type eq 'scalar') {
1.258 albertel 6539: $env{'form.'.$setting} = $env{$envname};
1.153 matthew 6540: } elsif ($type eq 'array') {
1.258 albertel 6541: $env{'form.'.$setting} = [
1.153 matthew 6542: map {
1.369 www 6543: &unescape($_);
1.258 albertel 6544: } split(',',$env{$envname})
1.153 matthew 6545: ];
6546: }
6547: }
6548: }
1.127 matthew 6549: }
6550:
6551: ############################################################
6552: ############################################################
1.154 albertel 6553:
1.443 albertel 6554: sub commit_customrole {
6555: my ($udom,$uname,$url,$three,$four,$five,$start,$end) = @_;
6556: my $output = &mt('Assigning custom role').' "'.$five.'" by '.$four.'@'.$three.' in '.$url.
6557: ($start?', '.&mt('starting').' '.localtime($start):'').
6558: ($end?', ending '.localtime($end):'').': <b>'.
6559: &Apache::lonnet::assigncustomrole(
6560: $udom,$uname,$url,$three,$four,$five,$end,$start).
6561: '</b><br />';
6562: return $output;
6563: }
6564:
6565: sub commit_standardrole {
1.541 raeburn 6566: my ($udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context) = @_;
6567: my ($output,$logmsg,$linefeed);
6568: if ($context eq 'auto') {
6569: $linefeed = "\n";
6570: } else {
6571: $linefeed = "<br />\n";
6572: }
1.443 albertel 6573: if ($three eq 'st') {
1.541 raeburn 6574: my $result = &commit_studentrole(\$logmsg,$udom,$uname,$url,$three,$start,$end,
6575: $one,$two,$sec,$context);
6576: if (($result =~ /^error/) || ($result eq 'not_in_class') ||
6577: ($result eq 'unknown_course')) {
1.443 albertel 6578: $output = "Error: $result\n";
6579: } else {
1.541 raeburn 6580: $output = $logmsg.$linefeed.&mt('Assigning').' '.$three.' in '.$url.
1.443 albertel 6581: ($start?', '.&mt('starting').' '.localtime($start):'').
1.541 raeburn 6582: ($end?', '.&mt('ending').' '.localtime($end):'').': ';
6583: if ($context eq 'auto') {
6584: $output .= $result.$linefeed.&mt('Add to classlist').': ok';
6585: } else {
6586: $output .= '<b>'.$result.'</b>'.$linefeed.
6587: &mt('Add to classlist').': <b>ok</b>';
6588: }
6589: $output .= $linefeed;
1.443 albertel 6590: }
6591: } else {
6592: $output = &mt('Assigning').' '.$three.' in '.$url.
6593: ($start?', '.&mt('starting').' '.localtime($start):'').
1.541 raeburn 6594: ($end?', '.&mt('ending').' '.localtime($end):'').': ';
6595: my $result = &Apache::lonnet::assignrole($udom,$uname,$url,$three,$end,$start);
6596: if ($context eq 'auto') {
6597: $output .= $result.$linefeed;
6598: } else {
6599: $output .= '<b>'.$result.'</b>'.$linefeed;
6600: }
1.443 albertel 6601: }
6602: return $output;
6603: }
6604:
6605: sub commit_studentrole {
1.541 raeburn 6606: my ($logmsg,$udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context) = @_;
6607: my ($result,$linefeed);
6608: if ($context eq 'auto') {
6609: $linefeed = "\n";
6610: } else {
6611: $linefeed = '<br />'."\n";
6612: }
1.443 albertel 6613: if (defined($one) && defined($two)) {
6614: my $cid=$one.'_'.$two;
6615: my $oldsec=&Apache::lonnet::getsection($udom,$uname,$cid);
6616: my $secchange = 0;
6617: my $expire_role_result;
6618: my $modify_section_result;
6619: unless ($oldsec eq '-1') {
6620: unless ($sec eq $oldsec) {
6621: $secchange = 1;
6622: my $uurl='/'.$cid;
6623: $uurl=~s/\_/\//g;
6624: if ($oldsec) {
6625: $uurl.='/'.$oldsec;
6626: }
6627: $expire_role_result = &Apache::lonnet::assignrole($udom,$uname,$uurl,'st',time);
6628: $result = $expire_role_result;
6629: }
6630: }
6631: if (($expire_role_result eq 'ok') || ($secchange == 0)) {
6632: $modify_section_result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,'','',$cid);
6633: if ($modify_section_result =~ /^ok/) {
6634: if ($secchange == 1) {
6635: $$logmsg .= "Section for $uname switched from old section: $oldsec to new section: $sec".$linefeed;
6636: } elsif ($oldsec eq '-1') {
6637: $$logmsg .= "New student role for $uname in section $sec in course $cid".$linefeed;
6638: } else {
6639: $$logmsg .= "Student $uname assigned to unchanged section $sec in course $cid".$linefeed;
6640: }
6641: } else {
6642: $$logmsg .= "Error when attempting section change for $uname from old section $oldsec to new section: $sec in course $cid -error: $modify_section_result".$linefeed;
6643: }
6644: $result = $modify_section_result;
6645: } elsif ($secchange == 1) {
6646: $$logmsg .= "Error when attempting to expire role for $uname in old section $oldsec in course $cid -error: $expire_role_result".$linefeed;
6647: }
6648: } else {
6649: $$logmsg .= "Incomplete course id defined. Addition of user $uname from domain $udom to course $one\_$two, section $sec not completed.$linefeed";
6650: $result = "error: incomplete course id\n";
6651: }
6652: return $result;
6653: }
6654:
6655: ############################################################
6656: ############################################################
6657:
1.444 albertel 6658: sub construct_course {
1.541 raeburn 6659: my ($args,$logmsg,$courseid,$crsudom,$crsunum,$udom,$uname,$context) = @_;
1.444 albertel 6660: my $outcome;
1.541 raeburn 6661: my $linefeed = '<br />'."\n";
6662: if ($context eq 'auto') {
6663: $linefeed = "\n";
6664: }
1.444 albertel 6665: #
6666: # Open course
6667: #
6668: my $crstype = lc($args->{'crstype'});
6669: my %cenv=();
6670: $$courseid=&Apache::lonnet::createcourse($args->{'course_domain'},
6671: $args->{'cdescr'},
6672: $args->{'curl'},
6673: $args->{'course_home'},
6674: $args->{'nonstandard'},
6675: $args->{'crscode'},
6676: $args->{'ccuname'}.':'.
6677: $args->{'ccdomain'},
6678: $args->{'crstype'});
6679:
6680: # Note: The testing routines depend on this being output; see
6681: # Utils::Course. This needs to at least be output as a comment
6682: # if anyone ever decides to not show this, and Utils::Course::new
6683: # will need to be suitably modified.
1.541 raeburn 6684: $outcome .= &mt('New LON-CAPA [_1] ID: [_2]',$crstype,$$courseid).$linefeed;
6685:
1.444 albertel 6686: #
6687: # Check if created correctly
6688: #
1.479 albertel 6689: ($$crsudom,$$crsunum)= &LONCAPA::split_courseid($$courseid);
1.444 albertel 6690: my $crsuhome=&Apache::lonnet::homeserver($$crsunum,$$crsudom);
1.541 raeburn 6691: $outcome .= &mt('Created on').': '.$crsuhome.$linefeed;
1.444 albertel 6692: #
6693: # Are we cloning?
6694: #
6695: my $cloneid='';
6696: if (($args->{'clonecourse'}) && ($args->{'clonedomain'})) {
1.541 raeburn 6697: my $can_clone = 0;
1.444 albertel 6698: $cloneid='/'.$args->{'clonedomain'}.'/'.$args->{'clonecourse'};
1.479 albertel 6699: my ($clonecrsudom,$clonecrsunum)= &LONCAPA::split_courseid($cloneid);
1.444 albertel 6700: my $clonehome=&Apache::lonnet::homeserver($clonecrsunum,$clonecrsudom);
1.541 raeburn 6701: my $clonemsg;
1.444 albertel 6702: if ($clonehome eq 'no_host') {
1.541 raeburn 6703: $clonemsg = &mt('Attempting to clone non-existing [_1]',$crstype);
6704: if ($context eq 'auto') {
6705: $outcome .= $clonemsg;
6706: } else {
6707: $outcome .= '<font color="red">'.$clonemsg.'</font>';
6708: }
6709: $outcome .= $linefeed;
1.444 albertel 6710: } else {
1.541 raeburn 6711: my %clonedesc = &Apache::lonnet::coursedescription($cloneid,{'one_time' => 1});
6712: if ($env{'request.role.domain'} eq $args->{'form.clonedomain'}) {
6713: $can_clone = 1;
6714: } else {
6715: my %clonehash = &Apache::lonnet::get('environment',['cloners'],
6716: $args->{'clonedomain'},$args->{'clonecourse'});
6717: my @cloners = split(/,/,$clonehash{'cloners'});
6718: my %roleshash =
6719: &Apache::lonnet::get_my_roles($args->{'ccuname'},
6720: $args->{'ccdomain'},'userroles',['active'],['cc'],
6721: [$args->{'clonedomain'}]);
6722: if (($roleshash{$args->{'clonecourse'}.':'.$args->{'clonedomain'}.':cc'}) || (grep(/^\Q$args->{'ccuname'}\E:\Q$args->{'ccdomain'}\E$/,@cloners))) {
6723: $can_clone = 1;
6724: } else {
6725: $clonemsg = &mt('The new course was not cloned from an existing course because the course owner ([_1]) does not have cloning rights in the existing course ([_2]).',$args->{'ccuname'}.':'.$args->{'ccdomain'},$clonedesc{'description'});
6726: if ($context eq 'auto') {
6727: $outcome .= $clonemsg;
6728: } else {
6729: $outcome .= '<font color="red">'.$clonemsg.'</font>';
6730: }
6731: $outcome .= $linefeed;
6732: }
6733: }
6734: }
6735: if ($can_clone) {
6736: $clonemsg = &mt('Cloning [_1] from [_2]',$crstype,$clonehome);
6737: if ($context eq 'auto') {
6738: $outcome = $clonemsg;
6739: } else {
6740: $outcome .= '<font color="green">'.$clonemsg.'</font>';
6741: }
6742: $outcome .= $linefeed;
1.444 albertel 6743: my %oldcenv=&Apache::lonnet::dump('environment',$$crsudom,$$crsunum);
6744: # Copy all files
6745: &Apache::lonclonecourse::copycoursefiles($cloneid,$$courseid);
6746: # Restore URL
6747: $cenv{'url'}=$oldcenv{'url'};
6748: # Restore title
6749: $cenv{'description'}=$oldcenv{'description'};
6750: # restore grading mode
6751: if (defined($oldcenv{'grading'})) {
6752: $cenv{'grading'}=$oldcenv{'grading'};
6753: }
6754: # Mark as cloned
6755: $cenv{'clonedfrom'}=$cloneid;
6756: delete($cenv{'default_enrollment_start_date'});
6757: delete($cenv{'default_enrollment_end_date'});
6758: }
6759: }
6760: #
6761: # Set environment (will override cloned, if existing)
6762: #
6763: my @sections = ();
6764: my @xlists = ();
6765: if ($args->{'crstype'}) {
6766: $cenv{'type'}=$args->{'crstype'};
6767: }
6768: if ($args->{'crsid'}) {
6769: $cenv{'courseid'}=$args->{'crsid'};
6770: }
6771: if ($args->{'crscode'}) {
6772: $cenv{'internal.coursecode'}=$args->{'crscode'};
6773: }
6774: if ($args->{'crsquota'} ne '') {
6775: $cenv{'internal.coursequota'}=$args->{'crsquota'};
6776: } else {
6777: $cenv{'internal.coursequota'}=$args->{'crsquota'} = 20;
6778: }
6779: if ($args->{'ccuname'}) {
6780: $cenv{'internal.courseowner'} = $args->{'ccuname'}.
6781: ':'.$args->{'ccdomain'};
6782: } else {
6783: $cenv{'internal.courseowner'} = $args->{'curruser'};
6784: }
6785:
6786: my @badclasses = (); # Used to accumulate sections/crosslistings that did not pass classlist access check for course owner.
6787: if ($args->{'crssections'}) {
6788: $cenv{'internal.sectionnums'} = '';
6789: if ($args->{'crssections'} =~ m/,/) {
6790: @sections = split/,/,$args->{'crssections'};
6791: } else {
6792: $sections[0] = $args->{'crssections'};
6793: }
6794: if (@sections > 0) {
6795: foreach my $item (@sections) {
6796: my ($sec,$gp) = split/:/,$item;
6797: my $class = $args->{'crscode'}.$sec;
6798: my $addcheck = &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$class,$cenv{'internal.courseowner'});
6799: $cenv{'internal.sectionnums'} .= $item.',';
6800: unless ($addcheck eq 'ok') {
6801: push @badclasses, $class;
6802: }
6803: }
6804: $cenv{'internal.sectionnums'} =~ s/,$//;
6805: }
6806: }
6807: # do not hide course coordinator from staff listing,
6808: # even if privileged
6809: $cenv{'nothideprivileged'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
6810: # add crosslistings
6811: if ($args->{'crsxlist'}) {
6812: $cenv{'internal.crosslistings'}='';
6813: if ($args->{'crsxlist'} =~ m/,/) {
6814: @xlists = split/,/,$args->{'crsxlist'};
6815: } else {
6816: $xlists[0] = $args->{'crsxlist'};
6817: }
6818: if (@xlists > 0) {
6819: foreach my $item (@xlists) {
6820: my ($xl,$gp) = split/:/,$item;
6821: my $addcheck = &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$xl,$cenv{'internal.courseowner'});
6822: $cenv{'internal.crosslistings'} .= $item.',';
6823: unless ($addcheck eq 'ok') {
6824: push @badclasses, $xl;
6825: }
6826: }
6827: $cenv{'internal.crosslistings'} =~ s/,$//;
6828: }
6829: }
6830: if ($args->{'autoadds'}) {
6831: $cenv{'internal.autoadds'}=$args->{'autoadds'};
6832: }
6833: if ($args->{'autodrops'}) {
6834: $cenv{'internal.autodrops'}=$args->{'autodrops'};
6835: }
6836: # check for notification of enrollment changes
6837: my @notified = ();
6838: if ($args->{'notify_owner'}) {
6839: if ($args->{'ccuname'} ne '') {
6840: push(@notified,$args->{'ccuname'}.':'.$args->{'ccdomain'});
6841: }
6842: }
6843: if ($args->{'notify_dc'}) {
6844: if ($uname ne '') {
6845: push(@notified,$uname.'@'.$udom);
6846: }
6847: }
6848: if (@notified > 0) {
6849: my $notifylist;
6850: if (@notified > 1) {
6851: $notifylist = join(',',@notified);
6852: } else {
6853: $notifylist = $notified[0];
6854: }
6855: $cenv{'internal.notifylist'} = $notifylist;
6856: }
6857: if (@badclasses > 0) {
6858: my %lt=&Apache::lonlocal::texthash(
6859: 'tclb' => 'The courses listed below were included as sections or crosslistings affiliated with your new LON-CAPA course. However, if automated course roster updates are enabled for this class, these particular sections/crosslistings will not contribute towards enrollment, because the user identified as the course owner for this LON-CAPA course',
6860: 'dnhr' => 'does not have rights to access enrollment in these classes',
6861: 'adby' => 'as determined by the policies of your institution on access to official classlists'
6862: );
1.541 raeburn 6863: my $badclass_msg = $cenv{'internal.courseowner'}.') - '.$lt{'dnhr'}.
6864: ' ('.$lt{'adby'}.')';
6865: if ($context eq 'auto') {
6866: $outcome .= $badclass_msg.$linefeed;
6867: $outcome .= '<font color="red">'.$badclass_msg.$linefeed.'<ul>'."\n";
6868: foreach my $item (@badclasses) {
6869: if ($context eq 'auto') {
6870: $outcome .= " - $item\n";
6871: } else {
6872: $outcome .= "<li>$item</li>\n";
6873: }
6874: }
6875: if ($context eq 'auto') {
6876: $outcome .= $linefeed;
6877: } else {
6878: $outcome .= "</ul><br /><br /></font>\n";
6879: }
6880: }
1.444 albertel 6881: }
6882: if ($args->{'no_end_date'}) {
6883: $args->{'endaccess'} = 0;
6884: }
6885: $cenv{'internal.autostart'}=$args->{'enrollstart'};
6886: $cenv{'internal.autoend'}=$args->{'enrollend'};
6887: $cenv{'default_enrollment_start_date'}=$args->{'startaccess'};
6888: $cenv{'default_enrollment_end_date'}=$args->{'endaccess'};
6889: if ($args->{'showphotos'}) {
6890: $cenv{'internal.showphotos'}=$args->{'showphotos'};
6891: }
6892: $cenv{'internal.authtype'} = $args->{'authtype'};
6893: $cenv{'internal.autharg'} = $args->{'autharg'};
6894: if ( ($cenv{'internal.authtype'} =~ /^krb/) && ($cenv{'internal.autoadds'} == 1)) {
6895: if (! defined($cenv{'internal.autharg'}) || $cenv{'internal.autharg'} eq '') {
1.541 raeburn 6896: my $krb_msg = &mt('As you did not include the default Kerberos domain to be used for authentication in this class, the institutional data used by the automated enrollment process must include the Kerberos domain for each new student');
6897: if ($context eq 'auto') {
6898: $outcome .= $krb_msg;
6899: } else {
6900: $outcome .= '<font color="red" size="+1">'.$krb_msg.'</font>';
6901: }
6902: $outcome .= $linefeed;
1.444 albertel 6903: }
6904: }
6905: if (($args->{'ccdomain'}) && ($args->{'ccuname'})) {
6906: if ($args->{'setpolicy'}) {
6907: $cenv{'policy.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
6908: }
6909: if ($args->{'setcontent'}) {
6910: $cenv{'question.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
6911: }
6912: }
6913: if ($args->{'reshome'}) {
6914: $cenv{'reshome'}=$args->{'reshome'}.'/';
6915: $cenv{'reshome'}=~s/\/+$/\//;
6916: }
6917: #
6918: # course has keyed access
6919: #
6920: if ($args->{'setkeys'}) {
6921: $cenv{'keyaccess'}='yes';
6922: }
6923: # if specified, key authority is not course, but user
6924: # only active if keyaccess is yes
6925: if ($args->{'keyauth'}) {
1.487 albertel 6926: my ($user,$domain) = split(':',$args->{'keyauth'});
6927: $user = &LONCAPA::clean_username($user);
6928: $domain = &LONCAPA::clean_username($domain);
1.488 foxr 6929: if ($user ne '' && $domain ne '') {
1.487 albertel 6930: $cenv{'keyauth'}=$user.':'.$domain;
1.444 albertel 6931: }
6932: }
6933:
6934: if ($args->{'disresdis'}) {
6935: $cenv{'pch.roles.denied'}='st';
6936: }
6937: if ($args->{'disablechat'}) {
6938: $cenv{'plc.roles.denied'}='st';
6939: }
6940:
6941: # Record we've not yet viewed the Course Initialization Helper for this
6942: # course
6943: $cenv{'course.helper.not.run'} = 1;
6944: #
6945: # Use new Randomseed
6946: #
6947: $cenv{'rndseed'}=&Apache::lonnet::latest_rnd_algorithm_id();;
6948: $cenv{'receiptalg'}=&Apache::lonnet::latest_receipt_algorithm_id();;
6949: #
6950: # The encryption code and receipt prefix for this course
6951: #
6952: $cenv{'internal.encseed'}=$Apache::lonnet::perlvar{'lonReceipt'}.$$.time.int(rand(9999));
6953: $cenv{'internal.encpref'}=100+int(9*rand(99));
6954: #
6955: # By default, use standard grading
6956: if (!defined($cenv{'grading'})) { $cenv{'grading'} = 'standard'; }
6957:
1.541 raeburn 6958: $outcome .= $linefeed.&mt('Setting environment').': '.
6959: &Apache::lonnet::put('environment',\%cenv,$$crsudom,$$crsunum).$linefeed;
1.444 albertel 6960: #
6961: # Open all assignments
6962: #
6963: if ($args->{'openall'}) {
6964: my $storeunder=$$crsudom.'_'.$$crsunum.'.0.opendate';
6965: my %storecontent = ($storeunder => time,
6966: $storeunder.'.type' => 'date_start');
6967:
6968: $outcome .= &mt('Opening all assignments').': '.&Apache::lonnet::cput
1.541 raeburn 6969: ('resourcedata',\%storecontent,$$crsudom,$$crsunum).$linefeed;
1.444 albertel 6970: }
6971: #
6972: # Set first page
6973: #
6974: unless (($args->{'nonstandard'}) || ($args->{'firstres'} eq 'blank')
6975: || ($cloneid)) {
1.445 albertel 6976: use LONCAPA::map;
1.444 albertel 6977: $outcome .= &mt('Setting first resource').': ';
1.445 albertel 6978:
6979: my $map = '/uploaded/'.$$crsudom.'/'.$$crsunum.'/default.sequence';
6980: my ($errtext,$fatal)=&LONCAPA::map::mapread($map);
6981:
1.444 albertel 6982: $outcome .= ($fatal?$errtext:'read ok').' - ';
6983: my $title; my $url;
6984: if ($args->{'firstres'} eq 'syl') {
6985: $title='Syllabus';
6986: $url='/public/'.$$crsudom.'/'.$$crsunum.'/syllabus';
6987: } else {
6988: $title='Navigate Contents';
6989: $url='/adm/navmaps';
6990: }
1.445 albertel 6991:
6992: $LONCAPA::map::resources[1]=$title.':'.$url.':false:start:res';
6993: (my $outtext,$errtext) = &LONCAPA::map::storemap($map,1);
6994:
6995: if ($errtext) { $fatal=2; }
1.541 raeburn 6996: $outcome .= ($fatal?$errtext:'write ok').$linefeed;
1.444 albertel 6997: }
6998: return $outcome;
6999: }
7000:
7001: ############################################################
7002: ############################################################
7003:
1.378 raeburn 7004: sub course_type {
7005: my ($cid) = @_;
7006: if (!defined($cid)) {
7007: $cid = $env{'request.course.id'};
7008: }
1.404 albertel 7009: if (defined($env{'course.'.$cid.'.type'})) {
7010: return $env{'course.'.$cid.'.type'};
1.378 raeburn 7011: } else {
7012: return 'Course';
1.377 raeburn 7013: }
7014: }
1.156 albertel 7015:
1.406 raeburn 7016: sub group_term {
7017: my $crstype = &course_type();
7018: my %names = (
7019: 'Course' => 'group',
7020: 'Group' => 'team',
7021: );
7022: return $names{$crstype};
7023: }
7024:
1.156 albertel 7025: sub icon {
7026: my ($file)=@_;
1.505 albertel 7027: my $curfext = lc((split(/\./,$file))[-1]);
1.168 albertel 7028: my $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/unknown.gif';
1.156 albertel 7029: my $embstyle = &Apache::loncommon::fileembstyle($curfext);
1.168 albertel 7030: if (!(!defined($embstyle) || $embstyle eq 'unk' || $embstyle eq 'hdn')) {
7031: if (-e $Apache::lonnet::perlvar{'lonDocRoot'}.'/'.
7032: $Apache::lonnet::perlvar{'lonIconsURL'}.'/'.
7033: $curfext.".gif") {
7034: $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/'.
7035: $curfext.".gif";
7036: }
7037: }
1.249 albertel 7038: return &lonhttpdurl($iconname);
1.154 albertel 7039: }
1.84 albertel 7040:
1.215 albertel 7041: sub lonhttpdurl {
7042: my ($url)=@_;
7043: my $lonhttpd_port=$Apache::lonnet::perlvar{'lonhttpdPort'};
7044: if (!defined($lonhttpd_port)) { $lonhttpd_port='8080'; }
7045: return 'http://'.$ENV{'SERVER_NAME'}.':'.$lonhttpd_port.$url;
7046: }
7047:
1.213 albertel 7048: sub connection_aborted {
7049: my ($r)=@_;
7050: $r->print(" ");$r->rflush();
7051: my $c = $r->connection;
7052: return $c->aborted();
7053: }
7054:
1.221 foxr 7055: # Escapes strings that may have embedded 's that will be put into
1.222 foxr 7056: # strings as 'strings'.
7057: sub escape_single {
1.221 foxr 7058: my ($input) = @_;
1.223 albertel 7059: $input =~ s/\\/\\\\/g; # Escape the \'s..(must be first)>
1.221 foxr 7060: $input =~ s/\'/\\\'/g; # Esacpe the 's....
7061: return $input;
7062: }
1.223 albertel 7063:
1.222 foxr 7064: # Same as escape_single, but escape's "'s This
7065: # can be used for "strings"
7066: sub escape_double {
7067: my ($input) = @_;
7068: $input =~ s/\\/\\\\/g; # Escape the /'s..(must be first)>
7069: $input =~ s/\"/\\\"/g; # Esacpe the "s....
7070: return $input;
7071: }
1.223 albertel 7072:
1.222 foxr 7073: # Escapes the last element of a full URL.
7074: sub escape_url {
7075: my ($url) = @_;
1.238 raeburn 7076: my @urlslices = split(/\//, $url,-1);
1.369 www 7077: my $lastitem = &escape(pop(@urlslices));
1.223 albertel 7078: return join('/',@urlslices).'/'.$lastitem;
1.222 foxr 7079: }
1.462 albertel 7080:
7081: # -------------------------------------------------------- Initliaze user login
7082: sub init_user_environment {
1.463 albertel 7083: my ($r, $username, $domain, $authhost, $form, $args) = @_;
1.462 albertel 7084: my $lonids=$Apache::lonnet::perlvar{'lonIDsDir'};
7085:
7086: my $public=($username eq 'public' && $domain eq 'public');
7087:
7088: # See if old ID present, if so, remove
7089:
7090: my ($filename,$cookie,$userroles);
7091: my $now=time;
7092:
7093: if ($public) {
7094: my $max_public=100;
7095: my $oldest;
7096: my $oldest_time=0;
7097: for(my $next=1;$next<=$max_public;$next++) {
7098: if (-e $lonids."/publicuser_$next.id") {
7099: my $mtime=(stat($lonids."/publicuser_$next.id"))[9];
7100: if ($mtime<$oldest_time || !$oldest_time) {
7101: $oldest_time=$mtime;
7102: $oldest=$next;
7103: }
7104: } else {
7105: $cookie="publicuser_$next";
7106: last;
7107: }
7108: }
7109: if (!$cookie) { $cookie="publicuser_$oldest"; }
7110: } else {
1.463 albertel 7111: # if this isn't a robot, kill any existing non-robot sessions
7112: if (!$args->{'robot'}) {
7113: opendir(DIR,$lonids);
7114: while ($filename=readdir(DIR)) {
7115: if ($filename=~/^$username\_\d+\_$domain\_$authhost\.id$/) {
7116: unlink($lonids.'/'.$filename);
7117: }
1.462 albertel 7118: }
1.463 albertel 7119: closedir(DIR);
1.462 albertel 7120: }
7121: # Give them a new cookie
1.463 albertel 7122: my $id = ($args->{'robot'} ? 'robot'.$args->{'robot'}
7123: : $now);
7124: $cookie="$username\_$id\_$domain\_$authhost";
1.462 albertel 7125:
7126: # Initialize roles
7127:
7128: $userroles=&Apache::lonnet::rolesinit($domain,$username,$authhost);
7129: }
7130: # ------------------------------------ Check browser type and MathML capability
7131:
7132: my ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,
7133: $clientunicode,$clientos) = &decode_user_agent($r);
7134:
7135: # -------------------------------------- Any accessibility options to remember?
7136: if (($form->{'interface'}) && ($form->{'remember'} eq 'true')) {
7137: foreach my $option ('imagesuppress','appletsuppress',
7138: 'embedsuppress','fontenhance','blackwhite') {
7139: if ($form->{$option} eq 'true') {
7140: &Apache::lonnet::put('environment',{$option => 'on'},
7141: $domain,$username);
7142: } else {
7143: &Apache::lonnet::del('environment',[$option],
7144: $domain,$username);
7145: }
7146: }
7147: }
7148: # ------------------------------------------------------------- Get environment
7149:
7150: my %userenv = &Apache::lonnet::dump('environment',$domain,$username);
7151: my ($tmp) = keys(%userenv);
7152: if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
7153: # default remote control to off
7154: if ($userenv{'remote'} ne 'on') { $userenv{'remote'} = 'off'; }
7155: } else {
7156: undef(%userenv);
7157: }
7158: if (($userenv{'interface'}) && (!$form->{'interface'})) {
7159: $form->{'interface'}=$userenv{'interface'};
7160: }
7161: $env{'environment.remote'}=$userenv{'remote'};
7162: if ($userenv{'texengine'} eq 'ttm') { $clientmathml=1; }
7163:
7164: # --------------- Do not trust query string to be put directly into environment
7165: foreach my $option ('imagesuppress','appletsuppress',
7166: 'embedsuppress','fontenhance','blackwhite',
7167: 'interface','localpath','localres') {
7168: $form->{$option}=~s/[\n\r\=]//gs;
7169: }
7170: # --------------------------------------------------------- Write first profile
7171:
7172: {
7173: my %initial_env =
7174: ("user.name" => $username,
7175: "user.domain" => $domain,
7176: "user.home" => $authhost,
7177: "browser.type" => $clientbrowser,
7178: "browser.version" => $clientversion,
7179: "browser.mathml" => $clientmathml,
7180: "browser.unicode" => $clientunicode,
7181: "browser.os" => $clientos,
7182: "server.domain" => $Apache::lonnet::perlvar{'lonDefDomain'},
7183: "request.course.fn" => '',
7184: "request.course.uri" => '',
7185: "request.course.sec" => '',
7186: "request.role" => 'cm',
7187: "request.role.adv" => $env{'user.adv'},
7188: "request.host" => $ENV{'REMOTE_ADDR'},);
7189:
7190: if ($form->{'localpath'}) {
7191: $initial_env{"browser.localpath"} = $form->{'localpath'};
7192: $initial_env{"browser.localres"} = $form->{'localres'};
7193: }
7194:
7195: if ($public) {
7196: $initial_env{"environment.remote"} = "off";
7197: }
7198: if ($form->{'interface'}) {
7199: $form->{'interface'}=~s/\W//gs;
7200: $initial_env{"browser.interface"} = $form->{'interface'};
7201: $env{'browser.interface'}=$form->{'interface'};
7202: foreach my $option ('imagesuppress','appletsuppress',
7203: 'embedsuppress','fontenhance','blackwhite') {
7204: if (($form->{$option} eq 'true') ||
7205: ($userenv{$option} eq 'on')) {
7206: $initial_env{"browser.$option"} = "on";
7207: }
7208: }
7209: }
7210:
7211: $env{'user.environment'} = "$lonids/$cookie.id";
7212:
7213: if (tie(my %disk_env,'GDBM_File',"$lonids/$cookie.id",
7214: &GDBM_WRCREAT(),0640)) {
7215: &_add_to_env(\%disk_env,\%initial_env);
7216: &_add_to_env(\%disk_env,\%userenv,'environment.');
7217: &_add_to_env(\%disk_env,$userroles);
1.463 albertel 7218: if (ref($args->{'extra_env'})) {
7219: &_add_to_env(\%disk_env,$args->{'extra_env'});
7220: }
1.462 albertel 7221: untie(%disk_env);
7222: } else {
7223: &Apache::lonnet::logthis("<font color=\"blue\">WARNING: ".
7224: 'Could not create environment storage in lonauth: '.$!.'</font>');
7225: return 'error: '.$!;
7226: }
7227: }
7228: $env{'request.role'}='cm';
7229: $env{'request.role.adv'}=$env{'user.adv'};
7230: $env{'browser.type'}=$clientbrowser;
7231:
7232: return $cookie;
7233:
7234: }
7235:
7236: sub _add_to_env {
7237: my ($idf,$env_data,$prefix) = @_;
7238: while (my ($key,$value) = each(%$env_data)) {
7239: $idf->{$prefix.$key} = $value;
7240: $env{$prefix.$key} = $value;
7241: }
7242: }
7243:
7244:
1.41 ng 7245: =pod
7246:
7247: =back
7248:
1.112 bowersj2 7249: =cut
1.41 ng 7250:
1.112 bowersj2 7251: 1;
7252: __END__;
1.41 ng 7253:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>