Annotation of loncom/interface/loncommon.pm, revision 1.1109
1.10 albertel 1: # The LearningOnline Network with CAPA
1.1 albertel 2: # a pile of common routines
1.10 albertel 3: #
1.1109 ! raeburn 4: # $Id: loncommon.pm,v 1.1108 2013/01/01 19:53:26 raeburn 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.685 tempelho 64: use Apache::lonnet();
1.139 matthew 65: use HTML::Entities;
1.334 albertel 66: use Apache::lonhtmlcommon();
67: use Apache::loncoursedata();
1.344 albertel 68: use Apache::lontexconvert();
1.444 albertel 69: use Apache::lonclonecourse();
1.1108 raeburn 70: use Apache::lonuserutils();
1.479 albertel 71: use LONCAPA qw(:DEFAULT :match);
1.657 raeburn 72: use DateTime::TimeZone;
1.687 raeburn 73: use DateTime::Locale::Catalog;
1.1091 foxr 74: use Text::Aspell;
1.1094 raeburn 75: use Authen::Captcha;
76: use Captcha::reCAPTCHA;
1.117 www 77:
1.517 raeburn 78: # ---------------------------------------------- Designs
79: use vars qw(%defaultdesign);
80:
1.22 www 81: my $readit;
82:
1.517 raeburn 83:
1.157 matthew 84: ##
85: ## Global Variables
86: ##
1.46 matthew 87:
1.643 foxr 88:
89: # ----------------------------------------------- SSI with retries:
90: #
91:
92: =pod
93:
1.648 raeburn 94: =head1 Server Side include with retries:
1.643 foxr 95:
96: =over 4
97:
1.648 raeburn 98: =item * &ssi_with_retries(resource,retries form)
1.643 foxr 99:
100: Performs an ssi with some number of retries. Retries continue either
101: until the result is ok or until the retry count supplied by the
102: caller is exhausted.
103:
104: Inputs:
1.648 raeburn 105:
106: =over 4
107:
1.643 foxr 108: resource - Identifies the resource to insert.
1.648 raeburn 109:
1.643 foxr 110: retries - Count of the number of retries allowed.
1.648 raeburn 111:
1.643 foxr 112: form - Hash that identifies the rendering options.
113:
1.648 raeburn 114: =back
115:
116: Returns:
117:
118: =over 4
119:
1.643 foxr 120: content - The content of the response. If retries were exhausted this is empty.
1.648 raeburn 121:
1.643 foxr 122: response - The response from the last attempt (which may or may not have been successful.
123:
1.648 raeburn 124: =back
125:
126: =back
127:
1.643 foxr 128: =cut
129:
130: sub ssi_with_retries {
131: my ($resource, $retries, %form) = @_;
132:
133:
134: my $ok = 0; # True if we got a good response.
135: my $content;
136: my $response;
137:
138: # Try to get the ssi done. within the retries count:
139:
140: do {
141: ($content, $response) = &Apache::lonnet::ssi($resource, %form);
142: $ok = $response->is_success;
1.650 www 143: if (!$ok) {
144: &Apache::lonnet::logthis("Failed ssi_with_retries on $resource: ".$response->is_success.', '.$response->code.', '.$response->message);
145: }
1.643 foxr 146: $retries--;
147: } while (!$ok && ($retries > 0));
148:
149: if (!$ok) {
150: $content = ''; # On error return an empty content.
151: }
152: return ($content, $response);
153:
154: }
155:
156:
157:
1.20 www 158: # ----------------------------------------------- Filetypes/Languages/Copyright
1.12 harris41 159: my %language;
1.124 www 160: my %supported_language;
1.1088 foxr 161: my %supported_codes;
1.1048 foxr 162: my %latex_language; # For choosing hyphenation in <transl..>
163: my %latex_language_bykey; # for choosing hyphenation from metadata
1.12 harris41 164: my %cprtag;
1.192 taceyjo1 165: my %scprtag;
1.351 www 166: my %fe; my %fd; my %fm;
1.41 ng 167: my %category_extensions;
1.12 harris41 168:
1.46 matthew 169: # ---------------------------------------------- Thesaurus variables
1.144 matthew 170: #
171: # %Keywords:
172: # A hash used by &keyword to determine if a word is considered a keyword.
173: # $thesaurus_db_file
174: # Scalar containing the full path to the thesaurus database.
1.46 matthew 175:
176: my %Keywords;
177: my $thesaurus_db_file;
178:
1.144 matthew 179: #
180: # Initialize values from language.tab, copyright.tab, filetypes.tab,
181: # thesaurus.tab, and filecategories.tab.
182: #
1.18 www 183: BEGIN {
1.46 matthew 184: # Variable initialization
185: $thesaurus_db_file = $Apache::lonnet::perlvar{'lonTabDir'}."/thesaurus.db";
186: #
1.22 www 187: unless ($readit) {
1.12 harris41 188: # ------------------------------------------------------------------- languages
189: {
1.158 raeburn 190: my $langtabfile = $Apache::lonnet::perlvar{'lonTabDir'}.
191: '/language.tab';
192: if ( open(my $fh,"<$langtabfile") ) {
1.356 albertel 193: while (my $line = <$fh>) {
194: next if ($line=~/^\#/);
195: chomp($line);
1.1088 foxr 196: my ($key,$code,$country,$three,$enc,$val,$sup,$latex)=(split(/\t/,$line));
1.158 raeburn 197: $language{$key}=$val.' - '.$enc;
198: if ($sup) {
199: $supported_language{$key}=$sup;
1.1088 foxr 200: $supported_codes{$key} = $code;
1.158 raeburn 201: }
1.1048 foxr 202: if ($latex) {
203: $latex_language_bykey{$key} = $latex;
1.1088 foxr 204: $latex_language{$code} = $latex;
1.1048 foxr 205: }
1.158 raeburn 206: }
207: close($fh);
208: }
1.12 harris41 209: }
210: # ------------------------------------------------------------------ copyrights
211: {
1.158 raeburn 212: my $copyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}.
213: '/copyright.tab';
214: if ( open (my $fh,"<$copyrightfile") ) {
1.356 albertel 215: while (my $line = <$fh>) {
216: next if ($line=~/^\#/);
217: chomp($line);
218: my ($key,$val)=(split(/\s+/,$line,2));
1.158 raeburn 219: $cprtag{$key}=$val;
220: }
221: close($fh);
222: }
1.12 harris41 223: }
1.351 www 224: # ----------------------------------------------------------- source copyrights
1.192 taceyjo1 225: {
226: my $sourcecopyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}.
227: '/source_copyright.tab';
228: if ( open (my $fh,"<$sourcecopyrightfile") ) {
1.356 albertel 229: while (my $line = <$fh>) {
230: next if ($line =~ /^\#/);
231: chomp($line);
232: my ($key,$val)=(split(/\s+/,$line,2));
1.192 taceyjo1 233: $scprtag{$key}=$val;
234: }
235: close($fh);
236: }
237: }
1.63 www 238:
1.517 raeburn 239: # -------------------------------------------------------------- default domain designs
1.63 www 240: my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors';
1.517 raeburn 241: my $designfile = $designdir.'/default.tab';
242: if ( open (my $fh,"<$designfile") ) {
243: while (my $line = <$fh>) {
244: next if ($line =~ /^\#/);
245: chomp($line);
246: my ($key,$val)=(split(/\=/,$line));
247: if ($val) { $defaultdesign{$key}=$val; }
248: }
249: close($fh);
1.63 www 250: }
251:
1.15 harris41 252: # ------------------------------------------------------------- file categories
253: {
1.158 raeburn 254: my $categoryfile = $Apache::lonnet::perlvar{'lonTabDir'}.
255: '/filecategories.tab';
256: if ( open (my $fh,"<$categoryfile") ) {
1.356 albertel 257: while (my $line = <$fh>) {
258: next if ($line =~ /^\#/);
259: chomp($line);
260: my ($extension,$category)=(split(/\s+/,$line,2));
1.158 raeburn 261: push @{$category_extensions{lc($category)}},$extension;
262: }
263: close($fh);
264: }
265:
1.15 harris41 266: }
1.12 harris41 267: # ------------------------------------------------------------------ file types
268: {
1.158 raeburn 269: my $typesfile = $Apache::lonnet::perlvar{'lonTabDir'}.
270: '/filetypes.tab';
271: if ( open (my $fh,"<$typesfile") ) {
1.356 albertel 272: while (my $line = <$fh>) {
273: next if ($line =~ /^\#/);
274: chomp($line);
275: my ($ending,$emb,$mime,$descr)=split(/\s+/,$line,4);
1.158 raeburn 276: if ($descr ne '') {
277: $fe{$ending}=lc($emb);
278: $fd{$ending}=$descr;
1.351 www 279: if ($mime ne 'unk') { $fm{$ending}=$mime; }
1.158 raeburn 280: }
281: }
282: close($fh);
283: }
1.12 harris41 284: }
1.22 www 285: &Apache::lonnet::logthis(
1.705 tempelho 286: "<span style='color:yellow;'>INFO: Read file types</span>");
1.22 www 287: $readit=1;
1.46 matthew 288: } # end of unless($readit)
1.32 matthew 289:
290: }
1.112 bowersj2 291:
1.42 matthew 292: ###############################################################
293: ## HTML and Javascript Helper Functions ##
294: ###############################################################
295:
296: =pod
297:
1.112 bowersj2 298: =head1 HTML and Javascript Functions
1.42 matthew 299:
1.112 bowersj2 300: =over 4
301:
1.648 raeburn 302: =item * &browser_and_searcher_javascript()
1.112 bowersj2 303:
304: X<browsing, javascript>X<searching, javascript>Returns a string
305: containing javascript with two functions, C<openbrowser> and
306: C<opensearcher>. Returned string does not contain E<lt>scriptE<gt>
307: tags.
1.42 matthew 308:
1.648 raeburn 309: =item * &openbrowser(formname,elementname,only,omit) [javascript]
1.42 matthew 310:
311: inputs: formname, elementname, only, omit
312:
313: formname and elementname indicate the name of the html form and name of
314: the element that the results of the browsing selection are to be placed in.
315:
316: Specifying 'only' will restrict the browser to displaying only files
1.185 www 317: with the given extension. Can be a comma separated list.
1.42 matthew 318:
319: Specifying 'omit' will restrict the browser to NOT displaying files
1.185 www 320: with the given extension. Can be a comma separated list.
1.42 matthew 321:
1.648 raeburn 322: =item * &opensearcher(formname,elementname) [javascript]
1.42 matthew 323:
324: Inputs: formname, elementname
325:
326: formname and elementname specify the name of the html form and the name
327: of the element the selection from the search results will be placed in.
1.542 raeburn 328:
1.42 matthew 329: =cut
330:
331: sub browser_and_searcher_javascript {
1.199 albertel 332: my ($mode)=@_;
333: if (!defined($mode)) { $mode='edit'; }
1.453 albertel 334: my $resurl=&escape_single(&lastresurl());
1.42 matthew 335: return <<END;
1.219 albertel 336: // <!-- BEGIN LON-CAPA Internal
1.50 matthew 337: var editbrowser = null;
1.135 albertel 338: function openbrowser(formname,elementname,only,omit,titleelement) {
1.170 www 339: var url = '$resurl/?';
1.42 matthew 340: if (editbrowser == null) {
341: url += 'launch=1&';
342: }
343: url += 'catalogmode=interactive&';
1.199 albertel 344: url += 'mode=$mode&';
1.611 albertel 345: url += 'inhibitmenu=yes&';
1.42 matthew 346: url += 'form=' + formname + '&';
347: if (only != null) {
348: url += 'only=' + only + '&';
1.217 albertel 349: } else {
350: url += 'only=&';
351: }
1.42 matthew 352: if (omit != null) {
353: url += 'omit=' + omit + '&';
1.217 albertel 354: } else {
355: url += 'omit=&';
356: }
1.135 albertel 357: if (titleelement != null) {
358: url += 'titleelement=' + titleelement + '&';
1.217 albertel 359: } else {
360: url += 'titleelement=&';
361: }
1.42 matthew 362: url += 'element=' + elementname + '';
363: var title = 'Browser';
1.435 albertel 364: var options = 'scrollbars=1,resizable=1,menubar=0,toolbar=1,location=1';
1.42 matthew 365: options += ',width=700,height=600';
366: editbrowser = open(url,title,options,'1');
367: editbrowser.focus();
368: }
369: var editsearcher;
1.135 albertel 370: function opensearcher(formname,elementname,titleelement) {
1.42 matthew 371: var url = '/adm/searchcat?';
372: if (editsearcher == null) {
373: url += 'launch=1&';
374: }
375: url += 'catalogmode=interactive&';
1.199 albertel 376: url += 'mode=$mode&';
1.42 matthew 377: url += 'form=' + formname + '&';
1.135 albertel 378: if (titleelement != null) {
379: url += 'titleelement=' + titleelement + '&';
1.217 albertel 380: } else {
381: url += 'titleelement=&';
382: }
1.42 matthew 383: url += 'element=' + elementname + '';
384: var title = 'Search';
1.435 albertel 385: var options = 'scrollbars=1,resizable=1,menubar=0,toolbar=1,location=1';
1.42 matthew 386: options += ',width=700,height=600';
387: editsearcher = open(url,title,options,'1');
388: editsearcher.focus();
389: }
1.219 albertel 390: // END LON-CAPA Internal -->
1.42 matthew 391: END
1.170 www 392: }
393:
394: sub lastresurl {
1.258 albertel 395: if ($env{'environment.lastresurl'}) {
396: return $env{'environment.lastresurl'}
1.170 www 397: } else {
398: return '/res';
399: }
400: }
401:
402: sub storeresurl {
403: my $resurl=&Apache::lonnet::clutter(shift);
404: unless ($resurl=~/^\/res/) { return 0; }
405: $resurl=~s/\/$//;
406: &Apache::lonnet::put('environment',{'lastresurl' => $resurl});
1.646 raeburn 407: &Apache::lonnet::appenv({'environment.lastresurl' => $resurl});
1.170 www 408: return 1;
1.42 matthew 409: }
410:
1.74 www 411: sub studentbrowser_javascript {
1.111 www 412: unless (
1.258 albertel 413: (($env{'request.course.id'}) &&
1.302 albertel 414: (&Apache::lonnet::allowed('srm',$env{'request.course.id'})
415: || &Apache::lonnet::allowed('srm',$env{'request.course.id'}.
416: '/'.$env{'request.course.sec'})
417: ))
1.258 albertel 418: || ($env{'request.role'}=~/^(au|dc|su)/)
1.111 www 419: ) { return ''; }
1.74 www 420: return (<<'ENDSTDBRW');
1.776 bisitz 421: <script type="text/javascript" language="Javascript">
1.824 bisitz 422: // <![CDATA[
1.74 www 423: var stdeditbrowser;
1.999 www 424: function openstdbrowser(formname,uname,udom,clicker,roleflag,ignorefilter,courseadvonly) {
1.74 www 425: var url = '/adm/pickstudent?';
426: var filter;
1.558 albertel 427: if (!ignorefilter) {
428: eval('filter=document.'+formname+'.'+uname+'.value;');
429: }
1.74 www 430: if (filter != null) {
431: if (filter != '') {
432: url += 'filter='+filter+'&';
433: }
434: }
435: url += 'form=' + formname + '&unameelement='+uname+
1.999 www 436: '&udomelement='+udom+
437: '&clicker='+clicker;
1.111 www 438: if (roleflag) { url+="&roles=1"; }
1.793 raeburn 439: if (courseadvonly) { url+="&courseadvonly=1"; }
1.102 www 440: var title = 'Student_Browser';
1.74 www 441: var options = 'scrollbars=1,resizable=1,menubar=0';
442: options += ',width=700,height=600';
443: stdeditbrowser = open(url,title,options,'1');
444: stdeditbrowser.focus();
445: }
1.824 bisitz 446: // ]]>
1.74 www 447: </script>
448: ENDSTDBRW
449: }
1.42 matthew 450:
1.1003 www 451: sub resourcebrowser_javascript {
452: unless ($env{'request.course.id'}) { return ''; }
1.1004 www 453: return (<<'ENDRESBRW');
1.1003 www 454: <script type="text/javascript" language="Javascript">
455: // <![CDATA[
456: var reseditbrowser;
1.1004 www 457: function openresbrowser(formname,reslink) {
1.1005 www 458: var url = '/adm/pickresource?form='+formname+'&reslink='+reslink;
1.1003 www 459: var title = 'Resource_Browser';
460: var options = 'scrollbars=1,resizable=1,menubar=0';
1.1005 www 461: options += ',width=700,height=500';
1.1004 www 462: reseditbrowser = open(url,title,options,'1');
463: reseditbrowser.focus();
1.1003 www 464: }
465: // ]]>
466: </script>
1.1004 www 467: ENDRESBRW
1.1003 www 468: }
469:
1.74 www 470: sub selectstudent_link {
1.999 www 471: my ($form,$unameele,$udomele,$courseadvonly,$clickerid)=@_;
472: my $callargs = "'".&Apache::lonhtmlcommon::entity_encode($form)."','".
473: &Apache::lonhtmlcommon::entity_encode($unameele)."','".
474: &Apache::lonhtmlcommon::entity_encode($udomele)."'";
1.258 albertel 475: if ($env{'request.course.id'}) {
1.302 albertel 476: if (!&Apache::lonnet::allowed('srm',$env{'request.course.id'})
477: && !&Apache::lonnet::allowed('srm',$env{'request.course.id'}.
478: '/'.$env{'request.course.sec'})) {
1.111 www 479: return '';
480: }
1.999 www 481: $callargs.=",'".&Apache::lonhtmlcommon::entity_encode($clickerid)."'";
1.793 raeburn 482: if ($courseadvonly) {
483: $callargs .= ",'',1,1";
484: }
485: return '<span class="LC_nobreak">'.
486: '<a href="javascript:openstdbrowser('.$callargs.');">'.
487: &mt('Select User').'</a></span>';
1.74 www 488: }
1.258 albertel 489: if ($env{'request.role'}=~/^(au|dc|su)/) {
1.1012 www 490: $callargs .= ",'',1";
1.793 raeburn 491: return '<span class="LC_nobreak">'.
492: '<a href="javascript:openstdbrowser('.$callargs.');">'.
493: &mt('Select User').'</a></span>';
1.111 www 494: }
495: return '';
1.91 www 496: }
497:
1.1004 www 498: sub selectresource_link {
499: my ($form,$reslink,$arg)=@_;
500:
501: my $callargs = "'".&Apache::lonhtmlcommon::entity_encode($form)."','".
502: &Apache::lonhtmlcommon::entity_encode($reslink)."'";
503: unless ($env{'request.course.id'}) { return $arg; }
504: return '<span class="LC_nobreak">'.
505: '<a href="javascript:openresbrowser('.$callargs.');">'.
506: $arg.'</a></span>';
507: }
508:
509:
510:
1.653 raeburn 511: sub authorbrowser_javascript {
512: return <<"ENDAUTHORBRW";
1.776 bisitz 513: <script type="text/javascript" language="JavaScript">
1.824 bisitz 514: // <![CDATA[
1.653 raeburn 515: var stdeditbrowser;
516:
517: function openauthorbrowser(formname,udom) {
518: var url = '/adm/pickauthor?';
519: url += 'form='+formname+'&roledom='+udom;
520: var title = 'Author_Browser';
521: var options = 'scrollbars=1,resizable=1,menubar=0';
522: options += ',width=700,height=600';
523: stdeditbrowser = open(url,title,options,'1');
524: stdeditbrowser.focus();
525: }
526:
1.824 bisitz 527: // ]]>
1.653 raeburn 528: </script>
529: ENDAUTHORBRW
530: }
531:
1.91 www 532: sub coursebrowser_javascript {
1.909 raeburn 533: my ($domainfilter,$sec_element,$formname,$role_element,$crstype) = @_;
1.932 raeburn 534: my $wintitle = 'Course_Browser';
1.931 raeburn 535: if ($crstype eq 'Community') {
1.932 raeburn 536: $wintitle = 'Community_Browser';
1.909 raeburn 537: }
1.876 raeburn 538: my $id_functions = &javascript_index_functions();
539: my $output = '
1.776 bisitz 540: <script type="text/javascript" language="JavaScript">
1.824 bisitz 541: // <![CDATA[
1.468 raeburn 542: var stdeditbrowser;'."\n";
1.876 raeburn 543:
544: $output .= <<"ENDSTDBRW";
1.909 raeburn 545: function opencrsbrowser(formname,uname,udom,desc,extra_element,multflag,type,type_elem) {
1.91 www 546: var url = '/adm/pickcourse?';
1.895 raeburn 547: var formid = getFormIdByName(formname);
1.876 raeburn 548: var domainfilter = getDomainFromSelectbox(formname,udom);
1.128 albertel 549: if (domainfilter != null) {
550: if (domainfilter != '') {
551: url += 'domainfilter='+domainfilter+'&';
552: }
553: }
1.91 www 554: url += 'form=' + formname + '&cnumelement='+uname+
1.187 albertel 555: '&cdomelement='+udom+
556: '&cnameelement='+desc;
1.468 raeburn 557: if (extra_element !=null && extra_element != '') {
1.594 raeburn 558: if (formname == 'rolechoice' || formname == 'studentform') {
1.468 raeburn 559: url += '&roleelement='+extra_element;
560: if (domainfilter == null || domainfilter == '') {
561: url += '&domainfilter='+extra_element;
562: }
1.234 raeburn 563: }
1.468 raeburn 564: else {
565: if (formname == 'portform') {
566: url += '&setroles='+extra_element;
1.800 raeburn 567: } else {
568: if (formname == 'rules') {
569: url += '&fixeddom='+extra_element;
570: }
1.468 raeburn 571: }
572: }
1.230 raeburn 573: }
1.909 raeburn 574: if (type != null && type != '') {
575: url += '&type='+type;
576: }
577: if (type_elem != null && type_elem != '') {
578: url += '&typeelement='+type_elem;
579: }
1.872 raeburn 580: if (formname == 'ccrs') {
581: var ownername = document.forms[formid].ccuname.value;
582: var ownerdom = document.forms[formid].ccdomain.options[document.forms[formid].ccdomain.selectedIndex].value;
583: url += '&cloner='+ownername+':'+ownerdom;
584: }
1.293 raeburn 585: if (multflag !=null && multflag != '') {
586: url += '&multiple='+multflag;
587: }
1.909 raeburn 588: var title = '$wintitle';
1.91 www 589: var options = 'scrollbars=1,resizable=1,menubar=0';
590: options += ',width=700,height=600';
591: stdeditbrowser = open(url,title,options,'1');
592: stdeditbrowser.focus();
593: }
1.876 raeburn 594: $id_functions
595: ENDSTDBRW
1.905 raeburn 596: if (($sec_element ne '') || ($role_element ne '')) {
597: $output .= &setsec_javascript($sec_element,$formname,$role_element);
1.876 raeburn 598: }
599: $output .= '
600: // ]]>
601: </script>';
602: return $output;
603: }
604:
605: sub javascript_index_functions {
606: return <<"ENDJS";
607:
608: function getFormIdByName(formname) {
609: for (var i=0;i<document.forms.length;i++) {
610: if (document.forms[i].name == formname) {
611: return i;
612: }
613: }
614: return -1;
615: }
616:
617: function getIndexByName(formid,item) {
618: for (var i=0;i<document.forms[formid].elements.length;i++) {
619: if (document.forms[formid].elements[i].name == item) {
620: return i;
621: }
622: }
623: return -1;
624: }
1.468 raeburn 625:
1.876 raeburn 626: function getDomainFromSelectbox(formname,udom) {
627: var userdom;
628: var formid = getFormIdByName(formname);
629: if (formid > -1) {
630: var domid = getIndexByName(formid,udom);
631: if (domid > -1) {
632: if (document.forms[formid].elements[domid].type == 'select-one') {
633: userdom=document.forms[formid].elements[domid].options[document.forms[formid].elements[domid].selectedIndex].value;
634: }
635: if (document.forms[formid].elements[domid].type == 'hidden') {
636: userdom=document.forms[formid].elements[domid].value;
1.468 raeburn 637: }
638: }
639: }
1.876 raeburn 640: return userdom;
641: }
642:
643: ENDJS
1.468 raeburn 644:
1.876 raeburn 645: }
646:
1.1017 raeburn 647: sub javascript_array_indexof {
1.1018 raeburn 648: return <<ENDJS;
1.1017 raeburn 649: <script type="text/javascript" language="JavaScript">
650: // <![CDATA[
651:
652: if (!Array.prototype.indexOf) {
653: Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
654: "use strict";
655: if (this === void 0 || this === null) {
656: throw new TypeError();
657: }
658: var t = Object(this);
659: var len = t.length >>> 0;
660: if (len === 0) {
661: return -1;
662: }
663: var n = 0;
664: if (arguments.length > 0) {
665: n = Number(arguments[1]);
1.1088 foxr 666: if (n !== n) { // shortcut for verifying if it is NaN
1.1017 raeburn 667: n = 0;
668: } else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
669: n = (n > 0 || -1) * Math.floor(Math.abs(n));
670: }
671: }
672: if (n >= len) {
673: return -1;
674: }
675: var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
676: for (; k < len; k++) {
677: if (k in t && t[k] === searchElement) {
678: return k;
679: }
680: }
681: return -1;
682: }
683: }
684:
685: // ]]>
686: </script>
687:
688: ENDJS
689:
690: }
691:
1.876 raeburn 692: sub userbrowser_javascript {
693: my $id_functions = &javascript_index_functions();
694: return <<"ENDUSERBRW";
695:
1.888 raeburn 696: function openuserbrowser(formname,uname,udom,ulast,ufirst,uemail,hideudom,crsdom,caller) {
1.876 raeburn 697: var url = '/adm/pickuser?';
698: var userdom = getDomainFromSelectbox(formname,udom);
699: if (userdom != null) {
700: if (userdom != '') {
701: url += 'srchdom='+userdom+'&';
702: }
703: }
704: url += 'form=' + formname + '&unameelement='+uname+
705: '&udomelement='+udom+
706: '&ulastelement='+ulast+
707: '&ufirstelement='+ufirst+
708: '&uemailelement='+uemail+
1.881 raeburn 709: '&hideudomelement='+hideudom+
710: '&coursedom='+crsdom;
1.888 raeburn 711: if ((caller != null) && (caller != undefined)) {
712: url += '&caller='+caller;
713: }
1.876 raeburn 714: var title = 'User_Browser';
715: var options = 'scrollbars=1,resizable=1,menubar=0';
716: options += ',width=700,height=600';
717: var stdeditbrowser = open(url,title,options,'1');
718: stdeditbrowser.focus();
719: }
720:
1.888 raeburn 721: function fix_domain (formname,udom,origdom,uname) {
1.876 raeburn 722: var formid = getFormIdByName(formname);
723: if (formid > -1) {
1.888 raeburn 724: var unameid = getIndexByName(formid,uname);
1.876 raeburn 725: var domid = getIndexByName(formid,udom);
726: var hidedomid = getIndexByName(formid,origdom);
727: if (hidedomid > -1) {
728: var fixeddom = document.forms[formid].elements[hidedomid].value;
1.888 raeburn 729: var unameval = document.forms[formid].elements[unameid].value;
730: if ((fixeddom != '') && (fixeddom != undefined) && (fixeddom != null) && (unameval != '') && (unameval != undefined) && (unameval != null)) {
731: if (domid > -1) {
732: var slct = document.forms[formid].elements[domid];
733: if (slct.type == 'select-one') {
734: var i;
735: for (i=0;i<slct.length;i++) {
736: if (slct.options[i].value==fixeddom) { slct.selectedIndex=i; }
737: }
738: }
739: if (slct.type == 'hidden') {
740: slct.value = fixeddom;
1.876 raeburn 741: }
742: }
1.468 raeburn 743: }
744: }
745: }
1.876 raeburn 746: return;
747: }
748:
749: $id_functions
750: ENDUSERBRW
1.468 raeburn 751: }
752:
753: sub setsec_javascript {
1.905 raeburn 754: my ($sec_element,$formname,$role_element) = @_;
755: my (@courserolenames,@communityrolenames,$rolestr,$courserolestr,
756: $communityrolestr);
757: if ($role_element ne '') {
758: my @allroles = ('st','ta','ep','in','ad');
759: foreach my $crstype ('Course','Community') {
760: if ($crstype eq 'Community') {
761: foreach my $role (@allroles) {
762: push(@communityrolenames,&Apache::lonnet::plaintext($role,$crstype));
763: }
764: push(@communityrolenames,&Apache::lonnet::plaintext('co'));
765: } else {
766: foreach my $role (@allroles) {
767: push(@courserolenames,&Apache::lonnet::plaintext($role,$crstype));
768: }
769: push(@courserolenames,&Apache::lonnet::plaintext('cc'));
770: }
771: }
772: $rolestr = '"'.join('","',@allroles).'"';
773: $courserolestr = '"'.join('","',@courserolenames).'"';
774: $communityrolestr = '"'.join('","',@communityrolenames).'"';
775: }
1.468 raeburn 776: my $setsections = qq|
777: function setSect(sectionlist) {
1.629 raeburn 778: var sectionsArray = new Array();
779: if ((sectionlist != '') && (typeof sectionlist != "undefined")) {
780: sectionsArray = sectionlist.split(",");
781: }
1.468 raeburn 782: var numSections = sectionsArray.length;
783: document.$formname.$sec_element.length = 0;
784: if (numSections == 0) {
785: document.$formname.$sec_element.multiple=false;
786: document.$formname.$sec_element.size=1;
787: document.$formname.$sec_element.options[0] = new Option('No existing sections','',false,false)
788: } else {
789: if (numSections == 1) {
790: document.$formname.$sec_element.multiple=false;
791: document.$formname.$sec_element.size=1;
792: document.$formname.$sec_element.options[0] = new Option('Select','',true,true);
793: document.$formname.$sec_element.options[1] = new Option('No section','',false,false)
794: document.$formname.$sec_element.options[2] = new Option(sectionsArray[0],sectionsArray[0],false,false);
795: } else {
796: for (var i=0; i<numSections; i++) {
797: document.$formname.$sec_element.options[i] = new Option(sectionsArray[i],sectionsArray[i],false,false)
798: }
799: document.$formname.$sec_element.multiple=true
800: if (numSections < 3) {
801: document.$formname.$sec_element.size=numSections;
802: } else {
803: document.$formname.$sec_element.size=3;
804: }
805: document.$formname.$sec_element.options[0].selected = false
806: }
807: }
1.91 www 808: }
1.905 raeburn 809:
810: function setRole(crstype) {
1.468 raeburn 811: |;
1.905 raeburn 812: if ($role_element eq '') {
813: $setsections .= ' return;
814: }
815: ';
816: } else {
817: $setsections .= qq|
818: var elementLength = document.$formname.$role_element.length;
819: var allroles = Array($rolestr);
820: var courserolenames = Array($courserolestr);
821: var communityrolenames = Array($communityrolestr);
822: if (elementLength != undefined) {
823: if (document.$formname.$role_element.options[5].value == 'cc') {
824: if (crstype == 'Course') {
825: return;
826: } else {
827: allroles[5] = 'co';
828: for (var i=0; i<6; i++) {
829: document.$formname.$role_element.options[i].value = allroles[i];
830: document.$formname.$role_element.options[i].text = communityrolenames[i];
831: }
832: }
833: } else {
834: if (crstype == 'Community') {
835: return;
836: } else {
837: allroles[5] = 'cc';
838: for (var i=0; i<6; i++) {
839: document.$formname.$role_element.options[i].value = allroles[i];
840: document.$formname.$role_element.options[i].text = courserolenames[i];
841: }
842: }
843: }
844: }
845: return;
846: }
847: |;
848: }
1.468 raeburn 849: return $setsections;
850: }
851:
1.91 www 852: sub selectcourse_link {
1.909 raeburn 853: my ($form,$unameele,$udomele,$desc,$extra_element,$multflag,$selecttype,
854: $typeelement) = @_;
855: my $type = $selecttype;
1.871 raeburn 856: my $linktext = &mt('Select Course');
857: if ($selecttype eq 'Community') {
1.909 raeburn 858: $linktext = &mt('Select Community');
1.906 raeburn 859: } elsif ($selecttype eq 'Course/Community') {
860: $linktext = &mt('Select Course/Community');
1.909 raeburn 861: $type = '';
1.1019 raeburn 862: } elsif ($selecttype eq 'Select') {
863: $linktext = &mt('Select');
864: $type = '';
1.871 raeburn 865: }
1.787 bisitz 866: return '<span class="LC_nobreak">'
867: ."<a href='"
868: .'javascript:opencrsbrowser("'.$form.'","'.$unameele
869: .'","'.$udomele.'","'.$desc.'","'.$extra_element
1.909 raeburn 870: .'","'.$multflag.'","'.$type.'","'.$typeelement.'");'
1.871 raeburn 871: ."'>".$linktext.'</a>'
1.787 bisitz 872: .'</span>';
1.74 www 873: }
1.42 matthew 874:
1.653 raeburn 875: sub selectauthor_link {
876: my ($form,$udom)=@_;
877: return '<a href="javascript:openauthorbrowser('."'$form','$udom'".');">'.
878: &mt('Select Author').'</a>';
879: }
880:
1.876 raeburn 881: sub selectuser_link {
1.881 raeburn 882: my ($form,$unameelem,$domelem,$lastelem,$firstelem,$emailelem,$hdomelem,
1.888 raeburn 883: $coursedom,$linktext,$caller) = @_;
1.876 raeburn 884: return '<a href="javascript:openuserbrowser('."'$form','$unameelem','$domelem',".
1.888 raeburn 885: "'$lastelem','$firstelem','$emailelem','$hdomelem','$coursedom','$caller'".
1.881 raeburn 886: ');">'.$linktext.'</a>';
1.876 raeburn 887: }
888:
1.273 raeburn 889: sub check_uncheck_jscript {
890: my $jscript = <<"ENDSCRT";
891: function checkAll(field) {
892: if (field.length > 0) {
893: for (i = 0; i < field.length; i++) {
1.1093 raeburn 894: if (!field[i].disabled) {
895: field[i].checked = true;
896: }
1.273 raeburn 897: }
898: } else {
1.1093 raeburn 899: if (!field.disabled) {
900: field.checked = true;
901: }
1.273 raeburn 902: }
903: }
904:
905: function uncheckAll(field) {
906: if (field.length > 0) {
907: for (i = 0; i < field.length; i++) {
908: field[i].checked = false ;
1.543 albertel 909: }
910: } else {
1.273 raeburn 911: field.checked = false ;
912: }
913: }
914: ENDSCRT
915: return $jscript;
916: }
917:
1.656 www 918: sub select_timezone {
1.659 raeburn 919: my ($name,$selected,$onchange,$includeempty)=@_;
920: my $output='<select name="'.$name.'" '.$onchange.'>'."\n";
921: if ($includeempty) {
922: $output .= '<option value=""';
923: if (($selected eq '') || ($selected eq 'local')) {
924: $output .= ' selected="selected" ';
925: }
926: $output .= '> </option>';
927: }
1.657 raeburn 928: my @timezones = DateTime::TimeZone->all_names;
929: foreach my $tzone (@timezones) {
930: $output.= '<option value="'.$tzone.'"';
931: if ($tzone eq $selected) {
932: $output.=' selected="selected"';
933: }
934: $output.=">$tzone</option>\n";
1.656 www 935: }
936: $output.="</select>";
937: return $output;
938: }
1.273 raeburn 939:
1.687 raeburn 940: sub select_datelocale {
941: my ($name,$selected,$onchange,$includeempty)=@_;
942: my $output='<select name="'.$name.'" '.$onchange.'>'."\n";
943: if ($includeempty) {
944: $output .= '<option value=""';
945: if ($selected eq '') {
946: $output .= ' selected="selected" ';
947: }
948: $output .= '> </option>';
949: }
950: my (@possibles,%locale_names);
951: my @locales = DateTime::Locale::Catalog::Locales;
952: foreach my $locale (@locales) {
953: if (ref($locale) eq 'HASH') {
954: my $id = $locale->{'id'};
955: if ($id ne '') {
956: my $en_terr = $locale->{'en_territory'};
957: my $native_terr = $locale->{'native_territory'};
1.695 raeburn 958: my @languages = &Apache::lonlocal::preferred_languages();
1.687 raeburn 959: if (grep(/^en$/,@languages) || !@languages) {
960: if ($en_terr ne '') {
961: $locale_names{$id} = '('.$en_terr.')';
962: } elsif ($native_terr ne '') {
963: $locale_names{$id} = $native_terr;
964: }
965: } else {
966: if ($native_terr ne '') {
967: $locale_names{$id} = $native_terr.' ';
968: } elsif ($en_terr ne '') {
969: $locale_names{$id} = '('.$en_terr.')';
970: }
971: }
972: push (@possibles,$id);
973: }
974: }
975: }
976: foreach my $item (sort(@possibles)) {
977: $output.= '<option value="'.$item.'"';
978: if ($item eq $selected) {
979: $output.=' selected="selected"';
980: }
981: $output.=">$item";
982: if ($locale_names{$item} ne '') {
983: $output.=" $locale_names{$item}</option>\n";
984: }
985: $output.="</option>\n";
986: }
987: $output.="</select>";
988: return $output;
989: }
990:
1.792 raeburn 991: sub select_language {
992: my ($name,$selected,$includeempty) = @_;
993: my %langchoices;
994: if ($includeempty) {
995: %langchoices = ('' => 'No language preference');
996: }
997: foreach my $id (&languageids()) {
998: my $code = &supportedlanguagecode($id);
999: if ($code) {
1000: $langchoices{$code} = &plainlanguagedescription($id);
1001: }
1002: }
1.970 raeburn 1003: return &select_form($selected,$name,\%langchoices);
1.792 raeburn 1004: }
1005:
1.42 matthew 1006: =pod
1.36 matthew 1007:
1.1088 foxr 1008:
1009: =item * &list_languages()
1010:
1011: Returns an array reference that is suitable for use in language prompters.
1012: Each array element is itself a two element array. The first element
1013: is the language code. The second element a descsriptiuon of the
1014: language itself. This is suitable for use in e.g.
1015: &Apache::edit::select_arg (once dereferenced that is).
1016:
1017: =cut
1018:
1019: sub list_languages {
1020: my @lang_choices;
1021:
1022: foreach my $id (&languageids()) {
1023: my $code = &supportedlanguagecode($id);
1024: if ($code) {
1025: my $selector = $supported_codes{$id};
1026: my $description = &plainlanguagedescription($id);
1027: push (@lang_choices, [$selector, $description]);
1028: }
1029: }
1030: return \@lang_choices;
1031: }
1032:
1033: =pod
1034:
1.648 raeburn 1035: =item * &linked_select_forms(...)
1.36 matthew 1036:
1037: linked_select_forms returns a string containing a <script></script> block
1038: and html for two <select> menus. The select menus will be linked in that
1039: changing the value of the first menu will result in new values being placed
1040: in the second menu. The values in the select menu will appear in alphabetical
1.609 raeburn 1041: order unless a defined order is provided.
1.36 matthew 1042:
1043: linked_select_forms takes the following ordered inputs:
1044:
1045: =over 4
1046:
1.112 bowersj2 1047: =item * $formname, the name of the <form> tag
1.36 matthew 1048:
1.112 bowersj2 1049: =item * $middletext, the text which appears between the <select> tags
1.36 matthew 1050:
1.112 bowersj2 1051: =item * $firstdefault, the default value for the first menu
1.36 matthew 1052:
1.112 bowersj2 1053: =item * $firstselectname, the name of the first <select> tag
1.36 matthew 1054:
1.112 bowersj2 1055: =item * $secondselectname, the name of the second <select> tag
1.36 matthew 1056:
1.112 bowersj2 1057: =item * $hashref, a reference to a hash containing the data for the menus.
1.36 matthew 1058:
1.609 raeburn 1059: =item * $menuorder, the order of values in the first menu
1060:
1.41 ng 1061: =back
1062:
1.36 matthew 1063: Below is an example of such a hash. Only the 'text', 'default', and
1064: 'select2' keys must appear as stated. keys(%menu) are the possible
1065: values for the first select menu. The text that coincides with the
1.41 ng 1066: first menu value is given in $menu{$choice1}->{'text'}. The values
1.36 matthew 1067: and text for the second menu are given in the hash pointed to by
1068: $menu{$choice1}->{'select2'}.
1069:
1.112 bowersj2 1070: my %menu = ( A1 => { text =>"Choice A1" ,
1071: default => "B3",
1072: select2 => {
1073: B1 => "Choice B1",
1074: B2 => "Choice B2",
1075: B3 => "Choice B3",
1076: B4 => "Choice B4"
1.609 raeburn 1077: },
1078: order => ['B4','B3','B1','B2'],
1.112 bowersj2 1079: },
1080: A2 => { text =>"Choice A2" ,
1081: default => "C2",
1082: select2 => {
1083: C1 => "Choice C1",
1084: C2 => "Choice C2",
1085: C3 => "Choice C3"
1.609 raeburn 1086: },
1087: order => ['C2','C1','C3'],
1.112 bowersj2 1088: },
1089: A3 => { text =>"Choice A3" ,
1090: default => "D6",
1091: select2 => {
1092: D1 => "Choice D1",
1093: D2 => "Choice D2",
1094: D3 => "Choice D3",
1095: D4 => "Choice D4",
1096: D5 => "Choice D5",
1097: D6 => "Choice D6",
1098: D7 => "Choice D7"
1.609 raeburn 1099: },
1100: order => ['D4','D3','D2','D1','D7','D6','D5'],
1.112 bowersj2 1101: }
1102: );
1.36 matthew 1103:
1104: =cut
1105:
1106: sub linked_select_forms {
1107: my ($formname,
1108: $middletext,
1109: $firstdefault,
1110: $firstselectname,
1111: $secondselectname,
1.609 raeburn 1112: $hashref,
1113: $menuorder,
1.36 matthew 1114: ) = @_;
1115: my $second = "document.$formname.$secondselectname";
1116: my $first = "document.$formname.$firstselectname";
1117: # output the javascript to do the changing
1118: my $result = '';
1.776 bisitz 1119: $result.='<script type="text/javascript" language="JavaScript">'."\n";
1.824 bisitz 1120: $result.="// <![CDATA[\n";
1.36 matthew 1121: $result.="var select2data = new Object();\n";
1122: $" = '","';
1123: my $debug = '';
1124: foreach my $s1 (sort(keys(%$hashref))) {
1125: $result.="select2data.d_$s1 = new Object();\n";
1126: $result.="select2data.d_$s1.def = new String('".
1127: $hashref->{$s1}->{'default'}."');\n";
1.609 raeburn 1128: $result.="select2data.d_$s1.values = new Array(";
1.36 matthew 1129: my @s2values = sort(keys( %{ $hashref->{$s1}->{'select2'} } ));
1.609 raeburn 1130: if (ref($hashref->{$s1}->{'order'}) eq 'ARRAY') {
1131: @s2values = @{$hashref->{$s1}->{'order'}};
1132: }
1.36 matthew 1133: $result.="\"@s2values\");\n";
1134: $result.="select2data.d_$s1.texts = new Array(";
1135: my @s2texts;
1136: foreach my $value (@s2values) {
1137: push @s2texts, $hashref->{$s1}->{'select2'}->{$value};
1138: }
1139: $result.="\"@s2texts\");\n";
1140: }
1141: $"=' ';
1142: $result.= <<"END";
1143:
1144: function select1_changed() {
1145: // Determine new choice
1146: var newvalue = "d_" + $first.value;
1147: // update select2
1148: var values = select2data[newvalue].values;
1149: var texts = select2data[newvalue].texts;
1150: var select2def = select2data[newvalue].def;
1151: var i;
1152: // out with the old
1153: for (i = 0; i < $second.options.length; i++) {
1154: $second.options[i] = null;
1155: }
1156: // in with the nuclear
1157: for (i=0;i<values.length; i++) {
1158: $second.options[i] = new Option(values[i]);
1.143 matthew 1159: $second.options[i].value = values[i];
1.36 matthew 1160: $second.options[i].text = texts[i];
1161: if (values[i] == select2def) {
1162: $second.options[i].selected = true;
1163: }
1164: }
1165: }
1.824 bisitz 1166: // ]]>
1.36 matthew 1167: </script>
1168: END
1169: # output the initial values for the selection lists
1170: $result .= "<select size=\"1\" name=\"$firstselectname\" onchange=\"select1_changed()\">\n";
1.609 raeburn 1171: my @order = sort(keys(%{$hashref}));
1172: if (ref($menuorder) eq 'ARRAY') {
1173: @order = @{$menuorder};
1174: }
1175: foreach my $value (@order) {
1.36 matthew 1176: $result.=" <option value=\"$value\" ";
1.253 albertel 1177: $result.=" selected=\"selected\" " if ($value eq $firstdefault);
1.119 www 1178: $result.=">".&mt($hashref->{$value}->{'text'})."</option>\n";
1.36 matthew 1179: }
1180: $result .= "</select>\n";
1181: my %select2 = %{$hashref->{$firstdefault}->{'select2'}};
1182: $result .= $middletext;
1183: $result .= "<select size=\"1\" name=\"$secondselectname\">\n";
1184: my $seconddefault = $hashref->{$firstdefault}->{'default'};
1.609 raeburn 1185:
1186: my @secondorder = sort(keys(%select2));
1187: if (ref($hashref->{$firstdefault}->{'order'}) eq 'ARRAY') {
1188: @secondorder = @{$hashref->{$firstdefault}->{'order'}};
1189: }
1190: foreach my $value (@secondorder) {
1.36 matthew 1191: $result.=" <option value=\"$value\" ";
1.253 albertel 1192: $result.=" selected=\"selected\" " if ($value eq $seconddefault);
1.119 www 1193: $result.=">".&mt($select2{$value})."</option>\n";
1.36 matthew 1194: }
1195: $result .= "</select>\n";
1196: # return $debug;
1197: return $result;
1198: } # end of sub linked_select_forms {
1199:
1.45 matthew 1200: =pod
1.44 bowersj2 1201:
1.973 raeburn 1202: =item * &help_open_topic($topic,$text,$stayOnPage,$width,$height,$imgid)
1.44 bowersj2 1203:
1.112 bowersj2 1204: Returns a string corresponding to an HTML link to the given help
1205: $topic, where $topic corresponds to the name of a .tex file in
1206: /home/httpd/html/adm/help/tex, with underscores replaced by
1207: spaces.
1208:
1209: $text will optionally be linked to the same topic, allowing you to
1210: link text in addition to the graphic. If you do not want to link
1211: text, but wish to specify one of the later parameters, pass an
1212: empty string.
1213:
1214: $stayOnPage is a value that will be interpreted as a boolean. If true,
1215: the link will not open a new window. If false, the link will open
1216: a new window using Javascript. (Default is false.)
1217:
1218: $width and $height are optional numerical parameters that will
1219: override the width and height of the popped up window, which may
1.973 raeburn 1220: be useful for certain help topics with big pictures included.
1221:
1222: $imgid is the id of the img tag used for the help icon. This may be
1223: used in a javascript call to switch the image src. See
1224: lonhtmlcommon::htmlareaselectactive() for an example.
1.44 bowersj2 1225:
1226: =cut
1227:
1228: sub help_open_topic {
1.973 raeburn 1229: my ($topic, $text, $stayOnPage, $width, $height, $imgid) = @_;
1.48 bowersj2 1230: $text = "" if (not defined $text);
1.44 bowersj2 1231: $stayOnPage = 0 if (not defined $stayOnPage);
1.1033 www 1232: $width = 500 if (not defined $width);
1.44 bowersj2 1233: $height = 400 if (not defined $height);
1234: my $filename = $topic;
1235: $filename =~ s/ /_/g;
1236:
1.48 bowersj2 1237: my $template = "";
1238: my $link;
1.572 banghart 1239:
1.159 www 1240: $topic=~s/\W/\_/g;
1.44 bowersj2 1241:
1.572 banghart 1242: if (!$stayOnPage) {
1.1033 www 1243: $link = "javascript:openMyModal('/adm/help/${filename}.hlp',$width,$height,'yes');";
1.1037 www 1244: } elsif ($stayOnPage eq 'popup') {
1245: $link = "javascript:void(open('/adm/help/${filename}.hlp', 'Help_for_$topic', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
1.572 banghart 1246: } else {
1.48 bowersj2 1247: $link = "/adm/help/${filename}.hlp";
1248: }
1249:
1250: # Add the text
1.755 neumanie 1251: if ($text ne "") {
1.763 bisitz 1252: $template.='<span class="LC_help_open_topic">'
1253: .'<a target="_top" href="'.$link.'">'
1254: .$text.'</a>';
1.48 bowersj2 1255: }
1256:
1.763 bisitz 1257: # (Always) Add the graphic
1.179 matthew 1258: my $title = &mt('Online Help');
1.667 raeburn 1259: my $helpicon=&lonhttpdurl("/adm/help/help.png");
1.973 raeburn 1260: if ($imgid ne '') {
1261: $imgid = ' id="'.$imgid.'"';
1262: }
1.763 bisitz 1263: $template.=' <a target="_top" href="'.$link.'" title="'.$title.'">'
1264: .'<img src="'.$helpicon.'" border="0"'
1265: .' alt="'.&mt('Help: [_1]',$topic).'"'
1.973 raeburn 1266: .' title="'.$title.'" style="vertical-align:middle;"'.$imgid
1.763 bisitz 1267: .' /></a>';
1268: if ($text ne "") {
1269: $template.='</span>';
1270: }
1.44 bowersj2 1271: return $template;
1272:
1.106 bowersj2 1273: }
1274:
1275: # This is a quicky function for Latex cheatsheet editing, since it
1276: # appears in at least four places
1277: sub helpLatexCheatsheet {
1.1037 www 1278: my ($topic,$text,$not_author,$stayOnPage) = @_;
1.732 raeburn 1279: my $out;
1.106 bowersj2 1280: my $addOther = '';
1.732 raeburn 1281: if ($topic) {
1.1037 www 1282: $addOther = '<span>'.&help_open_topic($topic,&mt($text),$stayOnPage, undef, 600).'</span> ';
1.763 bisitz 1283: }
1284: $out = '<span>' # Start cheatsheet
1285: .$addOther
1286: .'<span>'
1.1037 www 1287: .&help_open_topic('Greek_Symbols',&mt('Greek Symbols'),$stayOnPage,undef,600)
1.763 bisitz 1288: .'</span> <span>'
1.1037 www 1289: .&help_open_topic('Other_Symbols',&mt('Other Symbols'),$stayOnPage,undef,600)
1.763 bisitz 1290: .'</span>';
1.732 raeburn 1291: unless ($not_author) {
1.763 bisitz 1292: $out .= ' <span>'
1.1037 www 1293: .&help_open_topic('Authoring_Output_Tags',&mt('Output Tags'),$stayOnPage,undef,600)
1.763 bisitz 1294: .'</span>';
1.732 raeburn 1295: }
1.763 bisitz 1296: $out .= '</span>'; # End cheatsheet
1.732 raeburn 1297: return $out;
1.172 www 1298: }
1299:
1.430 albertel 1300: sub general_help {
1301: my $helptopic='Student_Intro';
1302: if ($env{'request.role'}=~/^(ca|au)/) {
1303: $helptopic='Authoring_Intro';
1.907 raeburn 1304: } elsif ($env{'request.role'}=~/^(cc|co)/) {
1.430 albertel 1305: $helptopic='Course_Coordination_Intro';
1.672 raeburn 1306: } elsif ($env{'request.role'}=~/^dc/) {
1307: $helptopic='Domain_Coordination_Intro';
1.430 albertel 1308: }
1309: return $helptopic;
1310: }
1311:
1312: sub update_help_link {
1313: my ($topic,$component_help,$faq,$bug,$stayOnPage) = @_;
1314: my $origurl = $ENV{'REQUEST_URI'};
1315: $origurl=~s|^/~|/priv/|;
1316: my $timestamp = time;
1317: foreach my $datum (\$topic,\$component_help,\$faq,\$bug,\$origurl) {
1318: $$datum = &escape($$datum);
1319: }
1320:
1321: my $banner_link = "/adm/helpmenu?page=banner&topic=$topic&component_help=$component_help&faq=$faq&bug=$bug&origurl=$origurl&stamp=$timestamp&stayonpage=$stayOnPage";
1322: my $output .= <<"ENDOUTPUT";
1323: <script type="text/javascript">
1.824 bisitz 1324: // <![CDATA[
1.430 albertel 1325: banner_link = '$banner_link';
1.824 bisitz 1326: // ]]>
1.430 albertel 1327: </script>
1328: ENDOUTPUT
1329: return $output;
1330: }
1331:
1332: # now just updates the help link and generates a blue icon
1.193 raeburn 1333: sub help_open_menu {
1.430 albertel 1334: my ($topic,$component_help,$faq,$bug,$stayOnPage,$width,$height,$text)
1.552 banghart 1335: = @_;
1.949 droeschl 1336: $stayOnPage = 1;
1.430 albertel 1337: my $output;
1338: if ($component_help) {
1339: if (!$text) {
1340: $output=&help_open_topic($component_help,undef,$stayOnPage,
1341: $width,$height);
1342: } else {
1343: my $help_text;
1344: $help_text=&unescape($topic);
1345: $output='<table><tr><td>'.
1346: &help_open_topic($component_help,$help_text,$stayOnPage,
1347: $width,$height).'</td></tr></table>';
1348: }
1349: }
1350: my $banner_link = &update_help_link($topic,$component_help,$faq,$bug,$stayOnPage);
1351: return $output.$banner_link;
1352: }
1353:
1354: sub top_nav_help {
1355: my ($text) = @_;
1.436 albertel 1356: $text = &mt($text);
1.949 droeschl 1357: my $stay_on_page = 1;
1358:
1.572 banghart 1359: my $link = ($stay_on_page) ? "javascript:helpMenu('display')"
1.436 albertel 1360: : "javascript:helpMenu('open')";
1.572 banghart 1361: my $banner_link = &update_help_link(undef,undef,undef,undef,$stay_on_page);
1.436 albertel 1362:
1.201 raeburn 1363: my $title = &mt('Get help');
1.436 albertel 1364:
1365: return <<"END";
1366: $banner_link
1367: <a href="$link" title="$title">$text</a>
1368: END
1369: }
1370:
1371: sub help_menu_js {
1372: my ($text) = @_;
1.949 droeschl 1373: my $stayOnPage = 1;
1.436 albertel 1374: my $width = 620;
1375: my $height = 600;
1.430 albertel 1376: my $helptopic=&general_help();
1377: my $details_link = '/adm/help/'.$helptopic.'.hlp';
1.261 albertel 1378: my $nothing=&Apache::lonhtmlcommon::javascript_nothing();
1.331 albertel 1379: my $start_page =
1380: &Apache::loncommon::start_page('Help Menu', undef,
1381: {'frameset' => 1,
1382: 'js_ready' => 1,
1383: 'add_entries' => {
1384: 'border' => '0',
1.579 raeburn 1385: 'rows' => "110,*",},});
1.331 albertel 1386: my $end_page =
1387: &Apache::loncommon::end_page({'frameset' => 1,
1388: 'js_ready' => 1,});
1389:
1.436 albertel 1390: my $template .= <<"ENDTEMPLATE";
1391: <script type="text/javascript">
1.877 bisitz 1392: // <![CDATA[
1.253 albertel 1393: // <!-- BEGIN LON-CAPA Internal
1.430 albertel 1394: var banner_link = '';
1.243 raeburn 1395: function helpMenu(target) {
1396: var caller = this;
1397: if (target == 'open') {
1398: var newWindow = null;
1399: try {
1.262 albertel 1400: newWindow = window.open($nothing,"helpmenu","HEIGHT=$height,WIDTH=$width,resizable=yes,scrollbars=yes" )
1.243 raeburn 1401: }
1402: catch(error) {
1403: writeHelp(caller);
1404: return;
1405: }
1406: if (newWindow) {
1407: caller = newWindow;
1408: }
1.193 raeburn 1409: }
1.243 raeburn 1410: writeHelp(caller);
1411: return;
1412: }
1413: function writeHelp(caller) {
1.1072 raeburn 1414: caller.document.writeln('$start_page\\n<frame name="bannerframe" src="'+banner_link+'" />\\n<frame name="bodyframe" src="$details_link" />\\n$end_page')
1.243 raeburn 1415: caller.document.close()
1416: caller.focus()
1.193 raeburn 1417: }
1.877 bisitz 1418: // END LON-CAPA Internal -->
1.253 albertel 1419: // ]]>
1.436 albertel 1420: </script>
1.193 raeburn 1421: ENDTEMPLATE
1422: return $template;
1423: }
1424:
1.172 www 1425: sub help_open_bug {
1426: my ($topic, $text, $stayOnPage, $width, $height) = @_;
1.258 albertel 1427: unless ($env{'user.adv'}) { return ''; }
1.172 www 1428: unless ($Apache::lonnet::perlvar{'BugzillaHost'}) { return ''; }
1429: $text = "" if (not defined $text);
1430: $stayOnPage=1;
1.184 albertel 1431: $width = 600 if (not defined $width);
1432: $height = 600 if (not defined $height);
1.172 www 1433:
1434: $topic=~s/\W+/\+/g;
1435: my $link='';
1436: my $template='';
1.379 albertel 1437: my $url=$Apache::lonnet::perlvar{'BugzillaHost'}.'enter_bug.cgi?product=LON-CAPA&bug_file_loc='.
1438: &escape($ENV{'REQUEST_URI'}).'&component='.$topic;
1.172 www 1439: if (!$stayOnPage)
1440: {
1441: $link = "javascript:void(open('$url', 'Bugzilla', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
1442: }
1443: else
1444: {
1445: $link = $url;
1446: }
1447: # Add the text
1448: if ($text ne "")
1449: {
1450: $template .=
1451: "<table bgcolor='#AA3333' cellspacing='1' cellpadding='1' border='0'><tr>".
1.705 tempelho 1452: "<td bgcolor='#FF5555'><a target=\"_top\" href=\"$link\"><span style=\"color:#FFFFFF;font-size:10pt;\">$text</span></a>";
1.172 www 1453: }
1454:
1455: # Add the graphic
1.179 matthew 1456: my $title = &mt('Report a Bug');
1.215 albertel 1457: my $bugicon=&lonhttpdurl("/adm/lonMisc/smallBug.gif");
1.172 www 1458: $template .= <<"ENDTEMPLATE";
1.436 albertel 1459: <a target="_top" href="$link" title="$title"><img src="$bugicon" border="0" alt="(Bug: $topic)" /></a>
1.172 www 1460: ENDTEMPLATE
1461: if ($text ne '') { $template.='</td></tr></table>' };
1462: return $template;
1463:
1464: }
1465:
1466: sub help_open_faq {
1467: my ($topic, $text, $stayOnPage, $width, $height) = @_;
1.258 albertel 1468: unless ($env{'user.adv'}) { return ''; }
1.172 www 1469: unless ($Apache::lonnet::perlvar{'FAQHost'}) { return ''; }
1470: $text = "" if (not defined $text);
1471: $stayOnPage=1;
1472: $width = 350 if (not defined $width);
1473: $height = 400 if (not defined $height);
1474:
1475: $topic=~s/\W+/\+/g;
1476: my $link='';
1477: my $template='';
1478: my $url=$Apache::lonnet::perlvar{'FAQHost'}.'/fom/cache/'.$topic.'.html';
1479: if (!$stayOnPage)
1480: {
1481: $link = "javascript:void(open('$url', 'FAQ-O-Matic', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
1482: }
1483: else
1484: {
1485: $link = $url;
1486: }
1487:
1488: # Add the text
1489: if ($text ne "")
1490: {
1491: $template .=
1.173 www 1492: "<table bgcolor='#337733' cellspacing='1' cellpadding='1' border='0'><tr>".
1.705 tempelho 1493: "<td bgcolor='#448844'><a target=\"_top\" href=\"$link\"><span style=\"color:#FFFFFF; font-size:10pt;\">$text</span></a>";
1.172 www 1494: }
1495:
1496: # Add the graphic
1.179 matthew 1497: my $title = &mt('View the FAQ');
1.215 albertel 1498: my $faqicon=&lonhttpdurl("/adm/lonMisc/smallFAQ.gif");
1.172 www 1499: $template .= <<"ENDTEMPLATE";
1.436 albertel 1500: <a target="_top" href="$link" title="$title"><img src="$faqicon" border="0" alt="(FAQ: $topic)" /></a>
1.172 www 1501: ENDTEMPLATE
1502: if ($text ne '') { $template.='</td></tr></table>' };
1503: return $template;
1504:
1.44 bowersj2 1505: }
1.37 matthew 1506:
1.180 matthew 1507: ###############################################################
1508: ###############################################################
1509:
1.45 matthew 1510: =pod
1511:
1.648 raeburn 1512: =item * &change_content_javascript():
1.256 matthew 1513:
1514: This and the next function allow you to create small sections of an
1515: otherwise static HTML page that you can update on the fly with
1516: Javascript, even in Netscape 4.
1517:
1518: The Javascript fragment returned by this function (no E<lt>scriptE<gt> tag)
1519: must be written to the HTML page once. It will prove the Javascript
1520: function "change(name, content)". Calling the change function with the
1521: name of the section
1522: you want to update, matching the name passed to C<changable_area>, and
1523: the new content you want to put in there, will put the content into
1524: that area.
1525:
1526: B<Note>: Netscape 4 only reserves enough space for the changable area
1527: to contain room for the original contents. You need to "make space"
1528: for whatever changes you wish to make, and be B<sure> to check your
1529: code in Netscape 4. This feature in Netscape 4 is B<not> powerful;
1530: it's adequate for updating a one-line status display, but little more.
1531: This script will set the space to 100% width, so you only need to
1532: worry about height in Netscape 4.
1533:
1534: Modern browsers are much less limiting, and if you can commit to the
1535: user not using Netscape 4, this feature may be used freely with
1536: pretty much any HTML.
1537:
1538: =cut
1539:
1540: sub change_content_javascript {
1541: # If we're on Netscape 4, we need to use Layer-based code
1.258 albertel 1542: if ($env{'browser.type'} eq 'netscape' &&
1543: $env{'browser.version'} =~ /^4\./) {
1.256 matthew 1544: return (<<NETSCAPE4);
1545: function change(name, content) {
1546: doc = document.layers[name+"___escape"].layers[0].document;
1547: doc.open();
1548: doc.write(content);
1549: doc.close();
1550: }
1551: NETSCAPE4
1552: } else {
1553: # Otherwise, we need to use semi-standards-compliant code
1554: # (technically, "innerHTML" isn't standard but the equivalent
1555: # is really scary, and every useful browser supports it
1556: return (<<DOMBASED);
1557: function change(name, content) {
1558: element = document.getElementById(name);
1559: element.innerHTML = content;
1560: }
1561: DOMBASED
1562: }
1563: }
1564:
1565: =pod
1566:
1.648 raeburn 1567: =item * &changable_area($name,$origContent):
1.256 matthew 1568:
1569: This provides a "changable area" that can be modified on the fly via
1570: the Javascript code provided in C<change_content_javascript>. $name is
1571: the name you will use to reference the area later; do not repeat the
1572: same name on a given HTML page more then once. $origContent is what
1573: the area will originally contain, which can be left blank.
1574:
1575: =cut
1576:
1577: sub changable_area {
1578: my ($name, $origContent) = @_;
1579:
1.258 albertel 1580: if ($env{'browser.type'} eq 'netscape' &&
1581: $env{'browser.version'} =~ /^4\./) {
1.256 matthew 1582: # If this is netscape 4, we need to use the Layer tag
1583: return "<ilayer width='100%' id='${name}___escape' overflow='none'><layer width='100%' id='$name' overflow='none'>$origContent</layer></ilayer>";
1584: } else {
1585: return "<span id='$name'>$origContent</span>";
1586: }
1587: }
1588:
1589: =pod
1590:
1.648 raeburn 1591: =item * &viewport_geometry_js
1.590 raeburn 1592:
1593: Provides javascript object (Geometry) which can provide information about the viewport geometry for the client browser.
1594:
1595: =cut
1596:
1597:
1598: sub viewport_geometry_js {
1599: return <<"GEOMETRY";
1600: var Geometry = {};
1601: function init_geometry() {
1602: if (Geometry.init) { return };
1603: Geometry.init=1;
1604: if (window.innerHeight) {
1605: Geometry.getViewportHeight = function() { return window.innerHeight; };
1606: Geometry.getViewportWidth = function() { return window.innerWidth; };
1607: Geometry.getHorizontalScroll = function() { return window.pageXOffset; };
1608: Geometry.getVerticalScroll = function() { return window.pageYOffset; };
1609: }
1610: else if (document.documentElement && document.documentElement.clientHeight) {
1611: Geometry.getViewportHeight =
1612: function() { return document.documentElement.clientHeight; };
1613: Geometry.getViewportWidth =
1614: function() { return document.documentElement.clientWidth; };
1615:
1616: Geometry.getHorizontalScroll =
1617: function() { return document.documentElement.scrollLeft; };
1618: Geometry.getVerticalScroll =
1619: function() { return document.documentElement.scrollTop; };
1620: }
1621: else if (document.body.clientHeight) {
1622: Geometry.getViewportHeight =
1623: function() { return document.body.clientHeight; };
1624: Geometry.getViewportWidth =
1625: function() { return document.body.clientWidth; };
1626: Geometry.getHorizontalScroll =
1627: function() { return document.body.scrollLeft; };
1628: Geometry.getVerticalScroll =
1629: function() { return document.body.scrollTop; };
1630: }
1631: }
1632:
1633: GEOMETRY
1634: }
1635:
1636: =pod
1637:
1.648 raeburn 1638: =item * &viewport_size_js()
1.590 raeburn 1639:
1640: Provides a javascript function to set values of two form elements - width and height (elements are passed in as arguments to the javascript function) to the dimensions of the user's browser window.
1641:
1642: =cut
1643:
1644: sub viewport_size_js {
1645: my $geometry = &viewport_geometry_js();
1646: return <<"DIMS";
1647:
1648: $geometry
1649:
1650: function getViewportDims(width,height) {
1651: init_geometry();
1652: width.value = Geometry.getViewportWidth();
1653: height.value = Geometry.getViewportHeight();
1654: return;
1655: }
1656:
1657: DIMS
1658: }
1659:
1660: =pod
1661:
1.648 raeburn 1662: =item * &resize_textarea_js()
1.565 albertel 1663:
1664: emits the needed javascript to resize a textarea to be as big as possible
1665:
1666: creates a function resize_textrea that takes two IDs first should be
1667: the id of the element to resize, second should be the id of a div that
1668: surrounds everything that comes after the textarea, this routine needs
1669: to be attached to the <body> for the onload and onresize events.
1670:
1.648 raeburn 1671: =back
1.565 albertel 1672:
1673: =cut
1674:
1675: sub resize_textarea_js {
1.590 raeburn 1676: my $geometry = &viewport_geometry_js();
1.565 albertel 1677: return <<"RESIZE";
1678: <script type="text/javascript">
1.824 bisitz 1679: // <![CDATA[
1.590 raeburn 1680: $geometry
1.565 albertel 1681:
1.588 albertel 1682: function getX(element) {
1683: var x = 0;
1684: while (element) {
1685: x += element.offsetLeft;
1686: element = element.offsetParent;
1687: }
1688: return x;
1689: }
1690: function getY(element) {
1691: var y = 0;
1692: while (element) {
1693: y += element.offsetTop;
1694: element = element.offsetParent;
1695: }
1696: return y;
1697: }
1698:
1699:
1.565 albertel 1700: function resize_textarea(textarea_id,bottom_id) {
1701: init_geometry();
1702: var textarea = document.getElementById(textarea_id);
1703: //alert(textarea);
1704:
1.588 albertel 1705: var textarea_top = getY(textarea);
1.565 albertel 1706: var textarea_height = textarea.offsetHeight;
1707: var bottom = document.getElementById(bottom_id);
1.588 albertel 1708: var bottom_top = getY(bottom);
1.565 albertel 1709: var bottom_height = bottom.offsetHeight;
1710: var window_height = Geometry.getViewportHeight();
1.588 albertel 1711: var fudge = 23;
1.565 albertel 1712: var new_height = window_height-fudge-textarea_top-bottom_height;
1713: if (new_height < 300) {
1714: new_height = 300;
1715: }
1716: textarea.style.height=new_height+'px';
1717: }
1.824 bisitz 1718: // ]]>
1.565 albertel 1719: </script>
1720: RESIZE
1721:
1722: }
1723:
1724: =pod
1725:
1.256 matthew 1726: =head1 Excel and CSV file utility routines
1727:
1728: =over 4
1729:
1730: =cut
1731:
1732: ###############################################################
1733: ###############################################################
1734:
1735: =pod
1736:
1.648 raeburn 1737: =item * &csv_translate($text)
1.37 matthew 1738:
1.185 www 1739: Translate $text to allow it to be output as a 'comma separated values'
1.37 matthew 1740: format.
1741:
1742: =cut
1743:
1.180 matthew 1744: ###############################################################
1745: ###############################################################
1.37 matthew 1746: sub csv_translate {
1747: my $text = shift;
1748: $text =~ s/\"/\"\"/g;
1.209 albertel 1749: $text =~ s/\n/ /g;
1.37 matthew 1750: return $text;
1751: }
1.180 matthew 1752:
1753: ###############################################################
1754: ###############################################################
1755:
1756: =pod
1757:
1.648 raeburn 1758: =item * &define_excel_formats()
1.180 matthew 1759:
1760: Define some commonly used Excel cell formats.
1761:
1762: Currently supported formats:
1763:
1764: =over 4
1765:
1766: =item header
1767:
1768: =item bold
1769:
1770: =item h1
1771:
1772: =item h2
1773:
1774: =item h3
1775:
1.256 matthew 1776: =item h4
1777:
1778: =item i
1779:
1.180 matthew 1780: =item date
1781:
1782: =back
1783:
1784: Inputs: $workbook
1785:
1786: Returns: $format, a hash reference.
1787:
1.1057 foxr 1788:
1.180 matthew 1789: =cut
1790:
1791: ###############################################################
1792: ###############################################################
1793: sub define_excel_formats {
1794: my ($workbook) = @_;
1795: my $format;
1796: $format->{'header'} = $workbook->add_format(bold => 1,
1797: bottom => 1,
1798: align => 'center');
1799: $format->{'bold'} = $workbook->add_format(bold=>1);
1800: $format->{'h1'} = $workbook->add_format(bold=>1, size=>18);
1801: $format->{'h2'} = $workbook->add_format(bold=>1, size=>16);
1802: $format->{'h3'} = $workbook->add_format(bold=>1, size=>14);
1.255 matthew 1803: $format->{'h4'} = $workbook->add_format(bold=>1, size=>12);
1.246 matthew 1804: $format->{'i'} = $workbook->add_format(italic=>1);
1.180 matthew 1805: $format->{'date'} = $workbook->add_format(num_format=>
1.207 matthew 1806: 'mm/dd/yyyy hh:mm:ss');
1.180 matthew 1807: return $format;
1808: }
1809:
1810: ###############################################################
1811: ###############################################################
1.113 bowersj2 1812:
1813: =pod
1814:
1.648 raeburn 1815: =item * &create_workbook()
1.255 matthew 1816:
1817: Create an Excel worksheet. If it fails, output message on the
1818: request object and return undefs.
1819:
1820: Inputs: Apache request object
1821:
1822: Returns (undef) on failure,
1823: Excel worksheet object, scalar with filename, and formats
1824: from &Apache::loncommon::define_excel_formats on success
1825:
1826: =cut
1827:
1828: ###############################################################
1829: ###############################################################
1830: sub create_workbook {
1831: my ($r) = @_;
1832: #
1833: # Create the excel spreadsheet
1834: my $filename = '/prtspool/'.
1.258 albertel 1835: $env{'user.name'}.'_'.$env{'user.domain'}.'_'.
1.255 matthew 1836: time.'_'.rand(1000000000).'.xls';
1837: my $workbook = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
1838: if (! defined($workbook)) {
1839: $r->log_error("Error creating excel spreadsheet $filename: $!");
1.928 bisitz 1840: $r->print(
1841: '<p class="LC_error">'
1842: .&mt('Problems occurred in creating the new Excel file.')
1843: .' '.&mt('This error has been logged.')
1844: .' '.&mt('Please alert your LON-CAPA administrator.')
1845: .'</p>'
1846: );
1.255 matthew 1847: return (undef);
1848: }
1849: #
1.1014 foxr 1850: $workbook->set_tempdir(LONCAPA::tempdir());
1.255 matthew 1851: #
1852: my $format = &Apache::loncommon::define_excel_formats($workbook);
1853: return ($workbook,$filename,$format);
1854: }
1855:
1856: ###############################################################
1857: ###############################################################
1858:
1859: =pod
1860:
1.648 raeburn 1861: =item * &create_text_file()
1.113 bowersj2 1862:
1.542 raeburn 1863: Create a file to write to and eventually make available to the user.
1.256 matthew 1864: If file creation fails, outputs an error message on the request object and
1865: return undefs.
1.113 bowersj2 1866:
1.256 matthew 1867: Inputs: Apache request object, and file suffix
1.113 bowersj2 1868:
1.256 matthew 1869: Returns (undef) on failure,
1870: Filehandle and filename on success.
1.113 bowersj2 1871:
1872: =cut
1873:
1.256 matthew 1874: ###############################################################
1875: ###############################################################
1876: sub create_text_file {
1877: my ($r,$suffix) = @_;
1878: if (! defined($suffix)) { $suffix = 'txt'; };
1879: my $fh;
1880: my $filename = '/prtspool/'.
1.258 albertel 1881: $env{'user.name'}.'_'.$env{'user.domain'}.'_'.
1.256 matthew 1882: time.'_'.rand(1000000000).'.'.$suffix;
1883: $fh = Apache::File->new('>/home/httpd'.$filename);
1884: if (! defined($fh)) {
1885: $r->log_error("Couldn't open $filename for output $!");
1.928 bisitz 1886: $r->print(
1887: '<p class="LC_error">'
1888: .&mt('Problems occurred in creating the output file.')
1889: .' '.&mt('This error has been logged.')
1890: .' '.&mt('Please alert your LON-CAPA administrator.')
1891: .'</p>'
1892: );
1.113 bowersj2 1893: }
1.256 matthew 1894: return ($fh,$filename)
1.113 bowersj2 1895: }
1896:
1897:
1.256 matthew 1898: =pod
1.113 bowersj2 1899:
1900: =back
1901:
1902: =cut
1.37 matthew 1903:
1904: ###############################################################
1.33 matthew 1905: ## Home server <option> list generating code ##
1906: ###############################################################
1.35 matthew 1907:
1.169 www 1908: # ------------------------------------------
1909:
1910: sub domain_select {
1911: my ($name,$value,$multiple)=@_;
1912: my %domains=map {
1.514 albertel 1913: $_ => $_.' '. &Apache::lonnet::domain($_,'description')
1.512 albertel 1914: } &Apache::lonnet::all_domains();
1.169 www 1915: if ($multiple) {
1916: $domains{''}=&mt('Any domain');
1.550 albertel 1917: $domains{'select_form_order'} = [sort {lc($a) cmp lc($b) } (keys(%domains))];
1.287 albertel 1918: return &multiple_select_form($name,$value,4,\%domains);
1.169 www 1919: } else {
1.550 albertel 1920: $domains{'select_form_order'} = [sort {lc($a) cmp lc($b) } (keys(%domains))];
1.970 raeburn 1921: return &select_form($name,$value,\%domains);
1.169 www 1922: }
1923: }
1924:
1.282 albertel 1925: #-------------------------------------------
1926:
1927: =pod
1928:
1.519 raeburn 1929: =head1 Routines for form select boxes
1930:
1931: =over 4
1932:
1.648 raeburn 1933: =item * &multiple_select_form($name,$value,$size,$hash,$order)
1.282 albertel 1934:
1935: Returns a string containing a <select> element int multiple mode
1936:
1937:
1938: Args:
1939: $name - name of the <select> element
1.506 raeburn 1940: $value - scalar or array ref of values that should already be selected
1.282 albertel 1941: $size - number of rows long the select element is
1.283 albertel 1942: $hash - the elements should be 'option' => 'shown text'
1.282 albertel 1943: (shown text should already have been &mt())
1.506 raeburn 1944: $order - (optional) array ref of the order to show the elements in
1.283 albertel 1945:
1.282 albertel 1946: =cut
1947:
1948: #-------------------------------------------
1.169 www 1949: sub multiple_select_form {
1.284 albertel 1950: my ($name,$value,$size,$hash,$order)=@_;
1.169 www 1951: my %selected = map { $_ => 1 } ref($value)?@{$value}:($value);
1952: my $output='';
1.191 matthew 1953: if (! defined($size)) {
1954: $size = 4;
1.283 albertel 1955: if (scalar(keys(%$hash))<4) {
1956: $size = scalar(keys(%$hash));
1.191 matthew 1957: }
1958: }
1.734 bisitz 1959: $output.="\n".'<select name="'.$name.'" size="'.$size.'" multiple="multiple">';
1.501 banghart 1960: my @order;
1.506 raeburn 1961: if (ref($order) eq 'ARRAY') {
1962: @order = @{$order};
1963: } else {
1964: @order = sort(keys(%$hash));
1.501 banghart 1965: }
1966: if (exists($$hash{'select_form_order'})) {
1967: @order = @{$$hash{'select_form_order'}};
1968: }
1969:
1.284 albertel 1970: foreach my $key (@order) {
1.356 albertel 1971: $output.='<option value="'.&HTML::Entities::encode($key,'"<>&').'" ';
1.284 albertel 1972: $output.='selected="selected" ' if ($selected{$key});
1973: $output.='>'.$hash->{$key}."</option>\n";
1.169 www 1974: }
1975: $output.="</select>\n";
1976: return $output;
1977: }
1978:
1.88 www 1979: #-------------------------------------------
1980:
1981: =pod
1982:
1.970 raeburn 1983: =item * &select_form($defdom,$name,$hashref,$onchange)
1.88 www 1984:
1985: Returns a string containing a <select name='$name' size='1'> form to
1.970 raeburn 1986: allow a user to select options from a ref to a hash containing:
1987: option_name => displayed text. An optional $onchange can include
1988: a javascript onchange item, e.g., onchange="this.form.submit();"
1989:
1.88 www 1990: See lonrights.pm for an example invocation and use.
1991:
1992: =cut
1993:
1994: #-------------------------------------------
1995: sub select_form {
1.970 raeburn 1996: my ($def,$name,$hashref,$onchange) = @_;
1997: return unless (ref($hashref) eq 'HASH');
1998: if ($onchange) {
1999: $onchange = ' onchange="'.$onchange.'"';
2000: }
2001: my $selectform = "<select name=\"$name\" size=\"1\"$onchange>\n";
1.128 albertel 2002: my @keys;
1.970 raeburn 2003: if (exists($hashref->{'select_form_order'})) {
2004: @keys=@{$hashref->{'select_form_order'}};
1.128 albertel 2005: } else {
1.970 raeburn 2006: @keys=sort(keys(%{$hashref}));
1.128 albertel 2007: }
1.356 albertel 2008: foreach my $key (@keys) {
2009: $selectform.=
2010: '<option value="'.&HTML::Entities::encode($key,'"<>&').'" '.
2011: ($key eq $def ? 'selected="selected" ' : '').
1.970 raeburn 2012: ">".$hashref->{$key}."</option>\n";
1.88 www 2013: }
2014: $selectform.="</select>";
2015: return $selectform;
2016: }
2017:
1.475 www 2018: # For display filters
2019:
2020: sub display_filter {
1.1074 raeburn 2021: my ($context) = @_;
1.475 www 2022: if (!$env{'form.show'}) { $env{'form.show'}=10; }
1.477 www 2023: if (!$env{'form.displayfilter'}) { $env{'form.displayfilter'}='currentfolder'; }
1.1074 raeburn 2024: my $phraseinput = 'hidden';
2025: my $includeinput = 'hidden';
2026: my ($checked,$includetypestext);
2027: if ($env{'form.displayfilter'} eq 'containing') {
2028: $phraseinput = 'text';
2029: if ($context eq 'parmslog') {
2030: $includeinput = 'checkbox';
2031: if ($env{'form.includetypes'}) {
2032: $checked = ' checked="checked"';
2033: }
2034: $includetypestext = &mt('Include parameter types');
2035: }
2036: } else {
2037: $includetypestext = ' ';
2038: }
2039: my ($additional,$secondid,$thirdid);
2040: if ($context eq 'parmslog') {
2041: $additional =
2042: '<label><input type="'.$includeinput.'" name="includetypes"'.
2043: $checked.' name="includetypes" value="1" id="includetypes" />'.
2044: ' <span id="includetypestext">'.$includetypestext.'</span>'.
2045: '</label>';
2046: $secondid = 'includetypes';
2047: $thirdid = 'includetypestext';
2048: }
2049: my $onchange = "javascript:toggleHistoryOptions(this,'containingphrase','$context',
2050: '$secondid','$thirdid')";
2051: return '<span class="LC_nobreak"><label>'.&mt('Records: [_1]',
1.475 www 2052: &Apache::lonmeta::selectbox('show',$env{'form.show'},undef,
2053: (&mt('all'),10,20,50,100,1000,10000))).
1.714 bisitz 2054: '</label></span> <span class="LC_nobreak">'.
1.1074 raeburn 2055: &mt('Filter: [_1]',
1.477 www 2056: &select_form($env{'form.displayfilter'},
2057: 'displayfilter',
1.970 raeburn 2058: {'currentfolder' => 'Current folder/page',
1.477 www 2059: 'containing' => 'Containing phrase',
1.1074 raeburn 2060: 'none' => 'None'},$onchange)).' '.
2061: '<input type="'.$phraseinput.'" name="containingphrase" id="containingphrase" size="30" value="'.
2062: &HTML::Entities::encode($env{'form.containingphrase'}).
2063: '" />'.$additional;
2064: }
2065:
2066: sub display_filter_js {
2067: my $includetext = &mt('Include parameter types');
2068: return <<"ENDJS";
2069:
2070: function toggleHistoryOptions(setter,firstid,context,secondid,thirdid) {
2071: var firstType = 'hidden';
2072: if (setter.options[setter.selectedIndex].value == 'containing') {
2073: firstType = 'text';
2074: }
2075: firstObject = document.getElementById(firstid);
2076: if (typeof(firstObject) == 'object') {
2077: if (firstObject.type != firstType) {
2078: changeInputType(firstObject,firstType);
2079: }
2080: }
2081: if (context == 'parmslog') {
2082: var secondType = 'hidden';
2083: if (firstType == 'text') {
2084: secondType = 'checkbox';
2085: }
2086: secondObject = document.getElementById(secondid);
2087: if (typeof(secondObject) == 'object') {
2088: if (secondObject.type != secondType) {
2089: changeInputType(secondObject,secondType);
2090: }
2091: }
2092: var textItem = document.getElementById(thirdid);
2093: var currtext = textItem.innerHTML;
2094: var newtext;
2095: if (firstType == 'text') {
2096: newtext = '$includetext';
2097: } else {
2098: newtext = ' ';
2099: }
2100: if (currtext != newtext) {
2101: textItem.innerHTML = newtext;
2102: }
2103: }
2104: return;
2105: }
2106:
2107: function changeInputType(oldObject,newType) {
2108: var newObject = document.createElement('input');
2109: newObject.type = newType;
2110: if (oldObject.size) {
2111: newObject.size = oldObject.size;
2112: }
2113: if (oldObject.value) {
2114: newObject.value = oldObject.value;
2115: }
2116: if (oldObject.name) {
2117: newObject.name = oldObject.name;
2118: }
2119: if (oldObject.id) {
2120: newObject.id = oldObject.id;
2121: }
2122: oldObject.parentNode.replaceChild(newObject,oldObject);
2123: return;
2124: }
2125:
2126: ENDJS
1.475 www 2127: }
2128:
1.167 www 2129: sub gradeleveldescription {
2130: my $gradelevel=shift;
2131: my %gradelevels=(0 => 'Not specified',
2132: 1 => 'Grade 1',
2133: 2 => 'Grade 2',
2134: 3 => 'Grade 3',
2135: 4 => 'Grade 4',
2136: 5 => 'Grade 5',
2137: 6 => 'Grade 6',
2138: 7 => 'Grade 7',
2139: 8 => 'Grade 8',
2140: 9 => 'Grade 9',
2141: 10 => 'Grade 10',
2142: 11 => 'Grade 11',
2143: 12 => 'Grade 12',
2144: 13 => 'Grade 13',
2145: 14 => '100 Level',
2146: 15 => '200 Level',
2147: 16 => '300 Level',
2148: 17 => '400 Level',
2149: 18 => 'Graduate Level');
2150: return &mt($gradelevels{$gradelevel});
2151: }
2152:
1.163 www 2153: sub select_level_form {
2154: my ($deflevel,$name)=@_;
2155: unless ($deflevel) { $deflevel=0; }
1.167 www 2156: my $selectform = "<select name=\"$name\" size=\"1\">\n";
2157: for (my $i=0; $i<=18; $i++) {
2158: $selectform.="<option value=\"$i\" ".
1.253 albertel 2159: ($i==$deflevel ? 'selected="selected" ' : '').
1.167 www 2160: ">".&gradeleveldescription($i)."</option>\n";
2161: }
2162: $selectform.="</select>";
2163: return $selectform;
1.163 www 2164: }
1.167 www 2165:
1.35 matthew 2166: #-------------------------------------------
2167:
1.45 matthew 2168: =pod
2169:
1.910 raeburn 2170: =item * &select_dom_form($defdom,$name,$includeempty,$showdomdesc,$onchange,$incdoms)
1.35 matthew 2171:
2172: Returns a string containing a <select name='$name' size='1'> form to
2173: allow a user to select the domain to preform an operation in.
2174: See loncreateuser.pm for an example invocation and use.
2175:
1.90 www 2176: If the $includeempty flag is set, it also includes an empty choice ("no domain
2177: selected");
2178:
1.743 raeburn 2179: If the $showdomdesc flag is set, the domain name is followed by the domain description.
2180:
1.910 raeburn 2181: The optional $onchange argument specifies what should occur if the domain selector is changed, e.g., 'this.form.submit()' if the form is to be automatically submitted.
2182:
2183: The optional $incdoms is a reference to an array of domains which will be the only available options.
1.563 raeburn 2184:
1.35 matthew 2185: =cut
2186:
2187: #-------------------------------------------
1.34 matthew 2188: sub select_dom_form {
1.910 raeburn 2189: my ($defdom,$name,$includeempty,$showdomdesc,$onchange,$incdoms) = @_;
1.872 raeburn 2190: if ($onchange) {
1.874 raeburn 2191: $onchange = ' onchange="'.$onchange.'"';
1.743 raeburn 2192: }
1.910 raeburn 2193: my @domains;
2194: if (ref($incdoms) eq 'ARRAY') {
2195: @domains = sort {lc($a) cmp lc($b)} (@{$incdoms});
2196: } else {
2197: @domains = sort {lc($a) cmp lc($b)} (&Apache::lonnet::all_domains());
2198: }
1.90 www 2199: if ($includeempty) { @domains=('',@domains); }
1.743 raeburn 2200: my $selectdomain = "<select name=\"$name\" size=\"1\"$onchange>\n";
1.356 albertel 2201: foreach my $dom (@domains) {
2202: $selectdomain.="<option value=\"$dom\" ".
1.563 raeburn 2203: ($dom eq $defdom ? 'selected="selected" ' : '').'>'.$dom;
2204: if ($showdomdesc) {
2205: if ($dom ne '') {
2206: my $domdesc = &Apache::lonnet::domain($dom,'description');
2207: if ($domdesc ne '') {
2208: $selectdomain .= ' ('.$domdesc.')';
2209: }
2210: }
2211: }
2212: $selectdomain .= "</option>\n";
1.34 matthew 2213: }
2214: $selectdomain.="</select>";
2215: return $selectdomain;
2216: }
2217:
1.35 matthew 2218: #-------------------------------------------
2219:
1.45 matthew 2220: =pod
2221:
1.648 raeburn 2222: =item * &home_server_form_item($domain,$name,$defaultflag)
1.35 matthew 2223:
1.586 raeburn 2224: input: 4 arguments (two required, two optional) -
2225: $domain - domain of new user
2226: $name - name of form element
2227: $default - Value of 'default' causes a default item to be first
2228: option, and selected by default.
2229: $hide - Value of 'hide' causes hiding of the name of the server,
2230: if 1 server found, or default, if 0 found.
1.594 raeburn 2231: output: returns 2 items:
1.586 raeburn 2232: (a) form element which contains either:
2233: (i) <select name="$name">
2234: <option value="$hostid1">$hostid $servers{$hostid}</option>
2235: <option value="$hostid2">$hostid $servers{$hostid}</option>
2236: </select>
2237: form item if there are multiple library servers in $domain, or
2238: (ii) an <input type="hidden" name="$name" value="$hostid" /> form item
2239: if there is only one library server in $domain.
2240:
2241: (b) number of library servers found.
2242:
2243: See loncreateuser.pm for example of use.
1.35 matthew 2244:
2245: =cut
2246:
2247: #-------------------------------------------
1.586 raeburn 2248: sub home_server_form_item {
2249: my ($domain,$name,$default,$hide) = @_;
1.513 albertel 2250: my %servers = &Apache::lonnet::get_servers($domain,'library');
1.586 raeburn 2251: my $result;
2252: my $numlib = keys(%servers);
2253: if ($numlib > 1) {
2254: $result .= '<select name="'.$name.'" />'."\n";
2255: if ($default) {
1.804 bisitz 2256: $result .= '<option value="default" selected="selected">'.&mt('default').
1.586 raeburn 2257: '</option>'."\n";
2258: }
2259: foreach my $hostid (sort(keys(%servers))) {
2260: $result.= '<option value="'.$hostid.'">'.
2261: $hostid.' '.$servers{$hostid}."</option>\n";
2262: }
2263: $result .= '</select>'."\n";
2264: } elsif ($numlib == 1) {
2265: my $hostid;
2266: foreach my $item (keys(%servers)) {
2267: $hostid = $item;
2268: }
2269: $result .= '<input type="hidden" name="'.$name.'" value="'.
2270: $hostid.'" />';
2271: if (!$hide) {
2272: $result .= $hostid.' '.$servers{$hostid};
2273: }
2274: $result .= "\n";
2275: } elsif ($default) {
2276: $result .= '<input type="hidden" name="'.$name.
2277: '" value="default" />';
2278: if (!$hide) {
2279: $result .= &mt('default');
2280: }
2281: $result .= "\n";
1.33 matthew 2282: }
1.586 raeburn 2283: return ($result,$numlib);
1.33 matthew 2284: }
1.112 bowersj2 2285:
2286: =pod
2287:
1.534 albertel 2288: =back
2289:
1.112 bowersj2 2290: =cut
1.87 matthew 2291:
2292: ###############################################################
1.112 bowersj2 2293: ## Decoding User Agent ##
1.87 matthew 2294: ###############################################################
2295:
2296: =pod
2297:
1.112 bowersj2 2298: =head1 Decoding the User Agent
2299:
2300: =over 4
2301:
2302: =item * &decode_user_agent()
1.87 matthew 2303:
2304: Inputs: $r
2305:
2306: Outputs:
2307:
2308: =over 4
2309:
1.112 bowersj2 2310: =item * $httpbrowser
1.87 matthew 2311:
1.112 bowersj2 2312: =item * $clientbrowser
1.87 matthew 2313:
1.112 bowersj2 2314: =item * $clientversion
1.87 matthew 2315:
1.112 bowersj2 2316: =item * $clientmathml
1.87 matthew 2317:
1.112 bowersj2 2318: =item * $clientunicode
1.87 matthew 2319:
1.112 bowersj2 2320: =item * $clientos
1.87 matthew 2321:
2322: =back
2323:
1.157 matthew 2324: =back
2325:
1.87 matthew 2326: =cut
2327:
2328: ###############################################################
2329: ###############################################################
2330: sub decode_user_agent {
1.247 albertel 2331: my ($r)=@_;
1.87 matthew 2332: my @browsertype=split(/\&/,$Apache::lonnet::perlvar{"lonBrowsDet"});
2333: my %mathcap=split(/\&/,$$Apache::lonnet::perlvar{"lonMathML"});
2334: my $httpbrowser=$ENV{"HTTP_USER_AGENT"};
1.247 albertel 2335: if (!$httpbrowser && $r) { $httpbrowser=$r->header_in('User-Agent'); }
1.87 matthew 2336: my $clientbrowser='unknown';
2337: my $clientversion='0';
2338: my $clientmathml='';
2339: my $clientunicode='0';
2340: for (my $i=0;$i<=$#browsertype;$i++) {
2341: my ($bname,$match,$notmatch,$vreg,$minv,$univ)=split(/\:/,$browsertype[$i]);
2342: if (($httpbrowser=~/$match/i) && ($httpbrowser!~/$notmatch/i)) {
2343: $clientbrowser=$bname;
2344: $httpbrowser=~/$vreg/i;
2345: $clientversion=$1;
2346: $clientmathml=($clientversion>=$minv);
2347: $clientunicode=($clientversion>=$univ);
2348: }
2349: }
2350: my $clientos='unknown';
2351: if (($httpbrowser=~/linux/i) ||
2352: ($httpbrowser=~/unix/i) ||
2353: ($httpbrowser=~/ux/i) ||
2354: ($httpbrowser=~/solaris/i)) { $clientos='unix'; }
2355: if (($httpbrowser=~/vax/i) ||
2356: ($httpbrowser=~/vms/i)) { $clientos='vms'; }
2357: if ($httpbrowser=~/next/i) { $clientos='next'; }
2358: if (($httpbrowser=~/mac/i) ||
2359: ($httpbrowser=~/powerpc/i)) { $clientos='mac'; }
2360: if ($httpbrowser=~/win/i) { $clientos='win'; }
2361: if ($httpbrowser=~/embed/i) { $clientos='pda'; }
2362: return ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,
2363: $clientunicode,$clientos,);
2364: }
2365:
1.32 matthew 2366: ###############################################################
2367: ## Authentication changing form generation subroutines ##
2368: ###############################################################
2369: ##
2370: ## All of the authform_xxxxxxx subroutines take their inputs in a
2371: ## hash, and have reasonable default values.
2372: ##
2373: ## formname = the name given in the <form> tag.
1.35 matthew 2374: #-------------------------------------------
2375:
1.45 matthew 2376: =pod
2377:
1.112 bowersj2 2378: =head1 Authentication Routines
2379:
2380: =over 4
2381:
1.648 raeburn 2382: =item * &authform_xxxxxx()
1.35 matthew 2383:
2384: The authform_xxxxxx subroutines provide javascript and html forms which
2385: handle some of the conveniences required for authentication forms.
2386: This is not an optimal method, but it works.
2387:
2388: =over 4
2389:
1.112 bowersj2 2390: =item * authform_header
1.35 matthew 2391:
1.112 bowersj2 2392: =item * authform_authorwarning
1.35 matthew 2393:
1.112 bowersj2 2394: =item * authform_nochange
1.35 matthew 2395:
1.112 bowersj2 2396: =item * authform_kerberos
1.35 matthew 2397:
1.112 bowersj2 2398: =item * authform_internal
1.35 matthew 2399:
1.112 bowersj2 2400: =item * authform_filesystem
1.35 matthew 2401:
2402: =back
2403:
1.648 raeburn 2404: See loncreateuser.pm for invocation and use examples.
1.157 matthew 2405:
1.35 matthew 2406: =cut
2407:
2408: #-------------------------------------------
1.32 matthew 2409: sub authform_header{
2410: my %in = (
2411: formname => 'cu',
1.80 albertel 2412: kerb_def_dom => '',
1.32 matthew 2413: @_,
2414: );
2415: $in{'formname'} = 'document.' . $in{'formname'};
2416: my $result='';
1.80 albertel 2417:
2418: #---------------------------------------------- Code for upper case translation
2419: my $Javascript_toUpperCase;
2420: unless ($in{kerb_def_dom}) {
2421: $Javascript_toUpperCase =<<"END";
2422: switch (choice) {
2423: case 'krb': currentform.elements[choicearg].value =
2424: currentform.elements[choicearg].value.toUpperCase();
2425: break;
2426: default:
2427: }
2428: END
2429: } else {
2430: $Javascript_toUpperCase = "";
2431: }
2432:
1.165 raeburn 2433: my $radioval = "'nochange'";
1.591 raeburn 2434: if (defined($in{'curr_authtype'})) {
2435: if ($in{'curr_authtype'} ne '') {
2436: $radioval = "'".$in{'curr_authtype'}."arg'";
2437: }
1.174 matthew 2438: }
1.165 raeburn 2439: my $argfield = 'null';
1.591 raeburn 2440: if (defined($in{'mode'})) {
1.165 raeburn 2441: if ($in{'mode'} eq 'modifycourse') {
1.591 raeburn 2442: if (defined($in{'curr_autharg'})) {
2443: if ($in{'curr_autharg'} ne '') {
1.165 raeburn 2444: $argfield = "'$in{'curr_autharg'}'";
2445: }
2446: }
2447: }
2448: }
2449:
1.32 matthew 2450: $result.=<<"END";
2451: var current = new Object();
1.165 raeburn 2452: current.radiovalue = $radioval;
2453: current.argfield = $argfield;
1.32 matthew 2454:
2455: function changed_radio(choice,currentform) {
2456: var choicearg = choice + 'arg';
2457: // If a radio button in changed, we need to change the argfield
2458: if (current.radiovalue != choice) {
2459: current.radiovalue = choice;
2460: if (current.argfield != null) {
2461: currentform.elements[current.argfield].value = '';
2462: }
2463: if (choice == 'nochange') {
2464: current.argfield = null;
2465: } else {
2466: current.argfield = choicearg;
2467: switch(choice) {
2468: case 'krb':
2469: currentform.elements[current.argfield].value =
2470: "$in{'kerb_def_dom'}";
2471: break;
2472: default:
2473: break;
2474: }
2475: }
2476: }
2477: return;
2478: }
1.22 www 2479:
1.32 matthew 2480: function changed_text(choice,currentform) {
2481: var choicearg = choice + 'arg';
2482: if (currentform.elements[choicearg].value !='') {
1.80 albertel 2483: $Javascript_toUpperCase
1.32 matthew 2484: // clear old field
2485: if ((current.argfield != choicearg) && (current.argfield != null)) {
2486: currentform.elements[current.argfield].value = '';
2487: }
2488: current.argfield = choicearg;
2489: }
2490: set_auth_radio_buttons(choice,currentform);
2491: return;
1.20 www 2492: }
1.32 matthew 2493:
2494: function set_auth_radio_buttons(newvalue,currentform) {
1.986 raeburn 2495: var numauthchoices = currentform.login.length;
2496: if (typeof numauthchoices == "undefined") {
2497: return;
2498: }
1.32 matthew 2499: var i=0;
1.986 raeburn 2500: while (i < numauthchoices) {
1.32 matthew 2501: if (currentform.login[i].value == newvalue) { break; }
2502: i++;
2503: }
1.986 raeburn 2504: if (i == numauthchoices) {
1.32 matthew 2505: return;
2506: }
2507: current.radiovalue = newvalue;
2508: currentform.login[i].checked = true;
2509: return;
2510: }
2511: END
2512: return $result;
2513: }
2514:
1.1106 raeburn 2515: sub authform_authorwarning {
1.32 matthew 2516: my $result='';
1.144 matthew 2517: $result='<i>'.
2518: &mt('As a general rule, only authors or co-authors should be '.
2519: 'filesystem authenticated '.
2520: '(which allows access to the server filesystem).')."</i>\n";
1.32 matthew 2521: return $result;
2522: }
2523:
1.1106 raeburn 2524: sub authform_nochange {
1.32 matthew 2525: my %in = (
2526: formname => 'document.cu',
2527: kerb_def_dom => 'MSU.EDU',
2528: @_,
2529: );
1.1106 raeburn 2530: my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.586 raeburn 2531: my $result;
1.1104 raeburn 2532: if (!$authnum) {
1.1105 raeburn 2533: $result = &mt('Under your current role you are not permitted to change login settings for this user');
1.586 raeburn 2534: } else {
2535: $result = '<label>'.&mt('[_1] Do not change login data',
2536: '<input type="radio" name="login" value="nochange" '.
2537: 'checked="checked" onclick="'.
1.281 albertel 2538: "javascript:changed_radio('nochange',$in{'formname'});".'" />').
2539: '</label>';
1.586 raeburn 2540: }
1.32 matthew 2541: return $result;
2542: }
2543:
1.591 raeburn 2544: sub authform_kerberos {
1.32 matthew 2545: my %in = (
2546: formname => 'document.cu',
2547: kerb_def_dom => 'MSU.EDU',
1.80 albertel 2548: kerb_def_auth => 'krb4',
1.32 matthew 2549: @_,
2550: );
1.586 raeburn 2551: my ($check4,$check5,$krbcheck,$krbarg,$krbver,$result,$authtype,
2552: $autharg,$jscall);
1.1106 raeburn 2553: my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.80 albertel 2554: if ($in{'kerb_def_auth'} eq 'krb5') {
1.772 bisitz 2555: $check5 = ' checked="checked"';
1.80 albertel 2556: } else {
1.772 bisitz 2557: $check4 = ' checked="checked"';
1.80 albertel 2558: }
1.165 raeburn 2559: $krbarg = $in{'kerb_def_dom'};
1.591 raeburn 2560: if (defined($in{'curr_authtype'})) {
2561: if ($in{'curr_authtype'} eq 'krb') {
1.772 bisitz 2562: $krbcheck = ' checked="checked"';
1.623 raeburn 2563: if (defined($in{'mode'})) {
2564: if ($in{'mode'} eq 'modifyuser') {
2565: $krbcheck = '';
2566: }
2567: }
1.591 raeburn 2568: if (defined($in{'curr_kerb_ver'})) {
2569: if ($in{'curr_krb_ver'} eq '5') {
1.772 bisitz 2570: $check5 = ' checked="checked"';
1.591 raeburn 2571: $check4 = '';
2572: } else {
1.772 bisitz 2573: $check4 = ' checked="checked"';
1.591 raeburn 2574: $check5 = '';
2575: }
1.586 raeburn 2576: }
1.591 raeburn 2577: if (defined($in{'curr_autharg'})) {
1.165 raeburn 2578: $krbarg = $in{'curr_autharg'};
2579: }
1.586 raeburn 2580: if (!$can_assign{'krb4'} && !$can_assign{'krb5'}) {
1.591 raeburn 2581: if (defined($in{'curr_autharg'})) {
1.586 raeburn 2582: $result =
2583: &mt('Currently Kerberos authenticated with domain [_1] Version [_2].',
2584: $in{'curr_autharg'},$krbver);
2585: } else {
2586: $result =
2587: &mt('Currently Kerberos authenticated, Version [_1].',$krbver);
2588: }
2589: return $result;
2590: }
2591: }
2592: } else {
2593: if ($authnum == 1) {
1.784 bisitz 2594: $authtype = '<input type="hidden" name="login" value="krb" />';
1.165 raeburn 2595: }
2596: }
1.586 raeburn 2597: if (!$can_assign{'krb4'} && !$can_assign{'krb5'}) {
2598: return;
1.587 raeburn 2599: } elsif ($authtype eq '') {
1.591 raeburn 2600: if (defined($in{'mode'})) {
1.587 raeburn 2601: if ($in{'mode'} eq 'modifycourse') {
2602: if ($authnum == 1) {
1.1104 raeburn 2603: $authtype = '<input type="radio" name="login" value="krb" />';
1.587 raeburn 2604: }
2605: }
2606: }
1.586 raeburn 2607: }
2608: $jscall = "javascript:changed_radio('krb',$in{'formname'});";
2609: if ($authtype eq '') {
2610: $authtype = '<input type="radio" name="login" value="krb" '.
2611: 'onclick="'.$jscall.'" onchange="'.$jscall.'"'.
2612: $krbcheck.' />';
2613: }
2614: if (($can_assign{'krb4'} && $can_assign{'krb5'}) ||
1.1106 raeburn 2615: ($can_assign{'krb4'} && !$can_assign{'krb5'} &&
1.586 raeburn 2616: $in{'curr_authtype'} eq 'krb5') ||
1.1106 raeburn 2617: (!$can_assign{'krb4'} && $can_assign{'krb5'} &&
1.586 raeburn 2618: $in{'curr_authtype'} eq 'krb4')) {
2619: $result .= &mt
1.144 matthew 2620: ('[_1] Kerberos authenticated with domain [_2] '.
1.281 albertel 2621: '[_3] Version 4 [_4] Version 5 [_5]',
1.586 raeburn 2622: '<label>'.$authtype,
1.281 albertel 2623: '</label><input type="text" size="10" name="krbarg" '.
1.165 raeburn 2624: 'value="'.$krbarg.'" '.
1.144 matthew 2625: 'onchange="'.$jscall.'" />',
1.281 albertel 2626: '<label><input type="radio" name="krbver" value="4" '.$check4.' />',
2627: '</label><label><input type="radio" name="krbver" value="5" '.$check5.' />',
2628: '</label>');
1.586 raeburn 2629: } elsif ($can_assign{'krb4'}) {
2630: $result .= &mt
2631: ('[_1] Kerberos authenticated with domain [_2] '.
2632: '[_3] Version 4 [_4]',
2633: '<label>'.$authtype,
2634: '</label><input type="text" size="10" name="krbarg" '.
2635: 'value="'.$krbarg.'" '.
2636: 'onchange="'.$jscall.'" />',
2637: '<label><input type="hidden" name="krbver" value="4" />',
2638: '</label>');
2639: } elsif ($can_assign{'krb5'}) {
2640: $result .= &mt
2641: ('[_1] Kerberos authenticated with domain [_2] '.
2642: '[_3] Version 5 [_4]',
2643: '<label>'.$authtype,
2644: '</label><input type="text" size="10" name="krbarg" '.
2645: 'value="'.$krbarg.'" '.
2646: 'onchange="'.$jscall.'" />',
2647: '<label><input type="hidden" name="krbver" value="5" />',
2648: '</label>');
2649: }
1.32 matthew 2650: return $result;
2651: }
2652:
1.1106 raeburn 2653: sub authform_internal {
1.586 raeburn 2654: my %in = (
1.32 matthew 2655: formname => 'document.cu',
2656: kerb_def_dom => 'MSU.EDU',
2657: @_,
2658: );
1.586 raeburn 2659: my ($intcheck,$intarg,$result,$authtype,$autharg,$jscall);
1.1106 raeburn 2660: my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.591 raeburn 2661: if (defined($in{'curr_authtype'})) {
2662: if ($in{'curr_authtype'} eq 'int') {
1.586 raeburn 2663: if ($can_assign{'int'}) {
1.772 bisitz 2664: $intcheck = 'checked="checked" ';
1.623 raeburn 2665: if (defined($in{'mode'})) {
2666: if ($in{'mode'} eq 'modifyuser') {
2667: $intcheck = '';
2668: }
2669: }
1.591 raeburn 2670: if (defined($in{'curr_autharg'})) {
1.586 raeburn 2671: $intarg = $in{'curr_autharg'};
2672: }
2673: } else {
2674: $result = &mt('Currently internally authenticated.');
2675: return $result;
1.165 raeburn 2676: }
2677: }
1.586 raeburn 2678: } else {
2679: if ($authnum == 1) {
1.784 bisitz 2680: $authtype = '<input type="hidden" name="login" value="int" />';
1.586 raeburn 2681: }
2682: }
2683: if (!$can_assign{'int'}) {
2684: return;
1.587 raeburn 2685: } elsif ($authtype eq '') {
1.591 raeburn 2686: if (defined($in{'mode'})) {
1.587 raeburn 2687: if ($in{'mode'} eq 'modifycourse') {
2688: if ($authnum == 1) {
1.1104 raeburn 2689: $authtype = '<input type="radio" name="login" value="int" />';
1.587 raeburn 2690: }
2691: }
2692: }
1.165 raeburn 2693: }
1.586 raeburn 2694: $jscall = "javascript:changed_radio('int',$in{'formname'});";
2695: if ($authtype eq '') {
2696: $authtype = '<input type="radio" name="login" value="int" '.$intcheck.
2697: ' onchange="'.$jscall.'" onclick="'.$jscall.'" />';
2698: }
1.605 bisitz 2699: $autharg = '<input type="password" size="10" name="intarg" value="'.
1.586 raeburn 2700: $intarg.'" onchange="'.$jscall.'" />';
2701: $result = &mt
1.144 matthew 2702: ('[_1] Internally authenticated (with initial password [_2])',
1.586 raeburn 2703: '<label>'.$authtype,'</label>'.$autharg);
1.824 bisitz 2704: $result.="<label><input type=\"checkbox\" name=\"visible\" onclick='if (this.checked) { this.form.intarg.type=\"text\" } else { this.form.intarg.type=\"password\" }' />".&mt('Visible input').'</label>';
1.32 matthew 2705: return $result;
2706: }
2707:
1.1104 raeburn 2708: sub authform_local {
1.32 matthew 2709: my %in = (
2710: formname => 'document.cu',
2711: kerb_def_dom => 'MSU.EDU',
2712: @_,
2713: );
1.586 raeburn 2714: my ($loccheck,$locarg,$result,$authtype,$autharg,$jscall);
1.1106 raeburn 2715: my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.591 raeburn 2716: if (defined($in{'curr_authtype'})) {
2717: if ($in{'curr_authtype'} eq 'loc') {
1.586 raeburn 2718: if ($can_assign{'loc'}) {
1.772 bisitz 2719: $loccheck = 'checked="checked" ';
1.623 raeburn 2720: if (defined($in{'mode'})) {
2721: if ($in{'mode'} eq 'modifyuser') {
2722: $loccheck = '';
2723: }
2724: }
1.591 raeburn 2725: if (defined($in{'curr_autharg'})) {
1.586 raeburn 2726: $locarg = $in{'curr_autharg'};
2727: }
2728: } else {
2729: $result = &mt('Currently using local (institutional) authentication.');
2730: return $result;
1.165 raeburn 2731: }
2732: }
1.586 raeburn 2733: } else {
2734: if ($authnum == 1) {
1.784 bisitz 2735: $authtype = '<input type="hidden" name="login" value="loc" />';
1.586 raeburn 2736: }
2737: }
2738: if (!$can_assign{'loc'}) {
2739: return;
1.587 raeburn 2740: } elsif ($authtype eq '') {
1.591 raeburn 2741: if (defined($in{'mode'})) {
1.587 raeburn 2742: if ($in{'mode'} eq 'modifycourse') {
2743: if ($authnum == 1) {
1.1104 raeburn 2744: $authtype = '<input type="radio" name="login" value="loc" />';
1.587 raeburn 2745: }
2746: }
2747: }
1.165 raeburn 2748: }
1.586 raeburn 2749: $jscall = "javascript:changed_radio('loc',$in{'formname'});";
2750: if ($authtype eq '') {
2751: $authtype = '<input type="radio" name="login" value="loc" '.
2752: $loccheck.' onchange="'.$jscall.'" onclick="'.
2753: $jscall.'" />';
2754: }
2755: $autharg = '<input type="text" size="10" name="locarg" value="'.
2756: $locarg.'" onchange="'.$jscall.'" />';
2757: $result = &mt('[_1] Local Authentication with argument [_2]',
2758: '<label>'.$authtype,'</label>'.$autharg);
1.32 matthew 2759: return $result;
2760: }
2761:
1.1106 raeburn 2762: sub authform_filesystem {
1.32 matthew 2763: my %in = (
2764: formname => 'document.cu',
2765: kerb_def_dom => 'MSU.EDU',
2766: @_,
2767: );
1.586 raeburn 2768: my ($fsyscheck,$result,$authtype,$autharg,$jscall);
1.1106 raeburn 2769: my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.591 raeburn 2770: if (defined($in{'curr_authtype'})) {
2771: if ($in{'curr_authtype'} eq 'fsys') {
1.586 raeburn 2772: if ($can_assign{'fsys'}) {
1.772 bisitz 2773: $fsyscheck = 'checked="checked" ';
1.623 raeburn 2774: if (defined($in{'mode'})) {
2775: if ($in{'mode'} eq 'modifyuser') {
2776: $fsyscheck = '';
2777: }
2778: }
1.586 raeburn 2779: } else {
2780: $result = &mt('Currently Filesystem Authenticated.');
2781: return $result;
2782: }
2783: }
2784: } else {
2785: if ($authnum == 1) {
1.784 bisitz 2786: $authtype = '<input type="hidden" name="login" value="fsys" />';
1.586 raeburn 2787: }
2788: }
2789: if (!$can_assign{'fsys'}) {
2790: return;
1.587 raeburn 2791: } elsif ($authtype eq '') {
1.591 raeburn 2792: if (defined($in{'mode'})) {
1.587 raeburn 2793: if ($in{'mode'} eq 'modifycourse') {
2794: if ($authnum == 1) {
1.1104 raeburn 2795: $authtype = '<input type="radio" name="login" value="fsys" />';
1.587 raeburn 2796: }
2797: }
2798: }
1.586 raeburn 2799: }
2800: $jscall = "javascript:changed_radio('fsys',$in{'formname'});";
2801: if ($authtype eq '') {
2802: $authtype = '<input type="radio" name="login" value="fsys" '.
2803: $fsyscheck.' onchange="'.$jscall.'" onclick="'.
2804: $jscall.'" />';
2805: }
2806: $autharg = '<input type="text" size="10" name="fsysarg" value=""'.
2807: ' onchange="'.$jscall.'" />';
2808: $result = &mt
1.144 matthew 2809: ('[_1] Filesystem Authenticated (with initial password [_2])',
1.281 albertel 2810: '<label><input type="radio" name="login" value="fsys" '.
1.586 raeburn 2811: $fsyscheck.'onchange="'.$jscall.'" onclick="'.$jscall.'" />',
1.605 bisitz 2812: '</label><input type="password" size="10" name="fsysarg" value="" '.
1.144 matthew 2813: 'onchange="'.$jscall.'" />');
1.32 matthew 2814: return $result;
2815: }
2816:
1.586 raeburn 2817: sub get_assignable_auth {
2818: my ($dom) = @_;
2819: if ($dom eq '') {
2820: $dom = $env{'request.role.domain'};
2821: }
2822: my %can_assign = (
2823: krb4 => 1,
2824: krb5 => 1,
2825: int => 1,
2826: loc => 1,
2827: );
2828: my %domconfig = &Apache::lonnet::get_dom('configuration',['usercreation'],$dom);
2829: if (ref($domconfig{'usercreation'}) eq 'HASH') {
2830: if (ref($domconfig{'usercreation'}{'authtypes'}) eq 'HASH') {
2831: my $authhash = $domconfig{'usercreation'}{'authtypes'};
2832: my $context;
2833: if ($env{'request.role'} =~ /^au/) {
2834: $context = 'author';
2835: } elsif ($env{'request.role'} =~ /^dc/) {
2836: $context = 'domain';
2837: } elsif ($env{'request.course.id'}) {
2838: $context = 'course';
2839: }
2840: if ($context) {
2841: if (ref($authhash->{$context}) eq 'HASH') {
2842: %can_assign = %{$authhash->{$context}};
2843: }
2844: }
2845: }
2846: }
2847: my $authnum = 0;
2848: foreach my $key (keys(%can_assign)) {
2849: if ($can_assign{$key}) {
2850: $authnum ++;
2851: }
2852: }
2853: if ($can_assign{'krb4'} && $can_assign{'krb5'}) {
2854: $authnum --;
2855: }
2856: return ($authnum,%can_assign);
2857: }
2858:
1.80 albertel 2859: ###############################################################
2860: ## Get Kerberos Defaults for Domain ##
2861: ###############################################################
2862: ##
2863: ## Returns default kerberos version and an associated argument
2864: ## as listed in file domain.tab. If not listed, provides
2865: ## appropriate default domain and kerberos version.
2866: ##
2867: #-------------------------------------------
2868:
2869: =pod
2870:
1.648 raeburn 2871: =item * &get_kerberos_defaults()
1.80 albertel 2872:
2873: get_kerberos_defaults($target_domain) returns the default kerberos
1.641 raeburn 2874: version and domain. If not found, it defaults to version 4 and the
2875: domain of the server.
1.80 albertel 2876:
1.648 raeburn 2877: =over 4
2878:
1.80 albertel 2879: ($def_version, $def_krb_domain) = &get_kerberos_defaults($target_domain);
2880:
1.648 raeburn 2881: =back
2882:
2883: =back
2884:
1.80 albertel 2885: =cut
2886:
2887: #-------------------------------------------
2888: sub get_kerberos_defaults {
2889: my $domain=shift;
1.641 raeburn 2890: my ($krbdef,$krbdefdom);
2891: my %domdefaults = &Apache::lonnet::get_domain_defaults($domain);
2892: if (($domdefaults{'auth_def'} =~/^krb(4|5)$/) && ($domdefaults{'auth_arg_def'} ne '')) {
2893: $krbdef = $domdefaults{'auth_def'};
2894: $krbdefdom = $domdefaults{'auth_arg_def'};
2895: } else {
1.80 albertel 2896: $ENV{'SERVER_NAME'}=~/(\w+\.\w+)$/;
2897: my $krbdefdom=$1;
2898: $krbdefdom=~tr/a-z/A-Z/;
2899: $krbdef = "krb4";
2900: }
2901: return ($krbdef,$krbdefdom);
2902: }
1.112 bowersj2 2903:
1.32 matthew 2904:
1.46 matthew 2905: ###############################################################
2906: ## Thesaurus Functions ##
2907: ###############################################################
1.20 www 2908:
1.46 matthew 2909: =pod
1.20 www 2910:
1.112 bowersj2 2911: =head1 Thesaurus Functions
2912:
2913: =over 4
2914:
1.648 raeburn 2915: =item * &initialize_keywords()
1.46 matthew 2916:
2917: Initializes the package variable %Keywords if it is empty. Uses the
2918: package variable $thesaurus_db_file.
2919:
2920: =cut
2921:
2922: ###################################################
2923:
2924: sub initialize_keywords {
2925: return 1 if (scalar keys(%Keywords));
2926: # If we are here, %Keywords is empty, so fill it up
2927: # Make sure the file we need exists...
2928: if (! -e $thesaurus_db_file) {
2929: &Apache::lonnet::logthis("Attempt to access $thesaurus_db_file".
2930: " failed because it does not exist");
2931: return 0;
2932: }
2933: # Set up the hash as a database
2934: my %thesaurus_db;
2935: if (! tie(%thesaurus_db,'GDBM_File',
1.53 albertel 2936: $thesaurus_db_file,&GDBM_READER(),0640)){
1.46 matthew 2937: &Apache::lonnet::logthis("Could not tie \%thesaurus_db to ".
2938: $thesaurus_db_file);
2939: return 0;
2940: }
2941: # Get the average number of appearances of a word.
2942: my $avecount = $thesaurus_db{'average.count'};
2943: # Put keywords (those that appear > average) into %Keywords
2944: while (my ($word,$data)=each (%thesaurus_db)) {
2945: my ($count,undef) = split /:/,$data;
2946: $Keywords{$word}++ if ($count > $avecount);
2947: }
2948: untie %thesaurus_db;
2949: # Remove special values from %Keywords.
1.356 albertel 2950: foreach my $value ('total.count','average.count') {
2951: delete($Keywords{$value}) if (exists($Keywords{$value}));
1.586 raeburn 2952: }
1.46 matthew 2953: return 1;
2954: }
2955:
2956: ###################################################
2957:
2958: =pod
2959:
1.648 raeburn 2960: =item * &keyword($word)
1.46 matthew 2961:
2962: Returns true if $word is a keyword. A keyword is a word that appears more
2963: than the average number of times in the thesaurus database. Calls
2964: &initialize_keywords
2965:
2966: =cut
2967:
2968: ###################################################
1.20 www 2969:
2970: sub keyword {
1.46 matthew 2971: return if (!&initialize_keywords());
2972: my $word=lc(shift());
2973: $word=~s/\W//g;
2974: return exists($Keywords{$word});
1.20 www 2975: }
1.46 matthew 2976:
2977: ###############################################################
2978:
2979: =pod
1.20 www 2980:
1.648 raeburn 2981: =item * &get_related_words()
1.46 matthew 2982:
1.160 matthew 2983: Look up a word in the thesaurus. Takes a scalar argument and returns
1.46 matthew 2984: an array of words. If the keyword is not in the thesaurus, an empty array
2985: will be returned. The order of the words returned is determined by the
2986: database which holds them.
2987:
2988: Uses global $thesaurus_db_file.
2989:
1.1057 foxr 2990:
1.46 matthew 2991: =cut
2992:
2993: ###############################################################
2994: sub get_related_words {
2995: my $keyword = shift;
2996: my %thesaurus_db;
2997: if (! -e $thesaurus_db_file) {
2998: &Apache::lonnet::logthis("Attempt to access $thesaurus_db_file ".
2999: "failed because the file does not exist");
3000: return ();
3001: }
3002: if (! tie(%thesaurus_db,'GDBM_File',
1.53 albertel 3003: $thesaurus_db_file,&GDBM_READER(),0640)){
1.46 matthew 3004: return ();
3005: }
3006: my @Words=();
1.429 www 3007: my $count=0;
1.46 matthew 3008: if (exists($thesaurus_db{$keyword})) {
1.356 albertel 3009: # The first element is the number of times
3010: # the word appears. We do not need it now.
1.429 www 3011: my (undef,@RelatedWords) = (split(/:/,$thesaurus_db{$keyword}));
3012: my (undef,$mostfrequentcount)=split(/\,/,$RelatedWords[0]);
3013: my $threshold=$mostfrequentcount/10;
3014: foreach my $possibleword (@RelatedWords) {
3015: my ($word,$wordcount)=split(/\,/,$possibleword);
3016: if ($wordcount>$threshold) {
3017: push(@Words,$word);
3018: $count++;
3019: if ($count>10) { last; }
3020: }
1.20 www 3021: }
3022: }
1.46 matthew 3023: untie %thesaurus_db;
3024: return @Words;
1.14 harris41 3025: }
1.1090 foxr 3026: ###############################################################
3027: #
3028: # Spell checking
3029: #
3030:
3031: =pod
3032:
3033: =head1 Spell checking
3034:
3035: =over 4
3036:
3037: =item * &check_spelling($wordlist $language)
3038:
3039: Takes a string containing words and feeds it to an external
3040: spellcheck program via a pipeline. Returns a string containing
3041: them mis-spelled words.
3042:
3043: Parameters:
3044:
3045: =over 4
3046:
3047: =item - $wordlist
3048:
3049: String that will be fed into the spellcheck program.
3050:
3051: =item - $language
3052:
3053: Language string that specifies the language for which the spell
3054: check will be performed.
3055:
3056: =back
3057:
3058: =back
3059:
3060: Note: This sub assumes that aspell is installed.
3061:
3062:
3063: =cut
3064:
1.46 matthew 3065:
1.112 bowersj2 3066: =pod
3067:
3068: =back
3069:
3070: =cut
1.61 www 3071:
1.1090 foxr 3072: sub check_spelling {
3073: my ($wordlist, $language) = @_;
1.1091 foxr 3074: my @misspellings;
3075:
3076: # Generate the speller and set the langauge.
3077: # if explicitly selected:
1.1090 foxr 3078:
1.1091 foxr 3079: my $speller = Text::Aspell->new;
1.1090 foxr 3080: if ($language) {
1.1091 foxr 3081: $speller->set_option('lang', $language);
1.1090 foxr 3082: }
3083:
1.1091 foxr 3084: # Turn the word list into an array of words by splittingon whitespace
1.1090 foxr 3085:
1.1091 foxr 3086: my @words = split(/\s+/, $wordlist);
1.1090 foxr 3087:
1.1091 foxr 3088: foreach my $word (@words) {
3089: if(! $speller->check($word)) {
3090: push(@misspellings, $word);
1.1090 foxr 3091: }
3092: }
1.1091 foxr 3093: return join(' ', @misspellings);
3094:
1.1090 foxr 3095: }
3096:
1.61 www 3097: # -------------------------------------------------------------- Plaintext name
1.81 albertel 3098: =pod
3099:
1.112 bowersj2 3100: =head1 User Name Functions
3101:
3102: =over 4
3103:
1.648 raeburn 3104: =item * &plainname($uname,$udom,$first)
1.81 albertel 3105:
1.112 bowersj2 3106: Takes a users logon name and returns it as a string in
1.226 albertel 3107: "first middle last generation" form
3108: if $first is set to 'lastname' then it returns it as
3109: 'lastname generation, firstname middlename' if their is a lastname
1.81 albertel 3110:
3111: =cut
1.61 www 3112:
1.295 www 3113:
1.81 albertel 3114: ###############################################################
1.61 www 3115: sub plainname {
1.226 albertel 3116: my ($uname,$udom,$first)=@_;
1.537 albertel 3117: return if (!defined($uname) || !defined($udom));
1.295 www 3118: my %names=&getnames($uname,$udom);
1.226 albertel 3119: my $name=&Apache::lonnet::format_name($names{'firstname'},
3120: $names{'middlename'},
3121: $names{'lastname'},
3122: $names{'generation'},$first);
3123: $name=~s/^\s+//;
1.62 www 3124: $name=~s/\s+$//;
3125: $name=~s/\s+/ /g;
1.353 albertel 3126: if ($name !~ /\S/) { $name=$uname.':'.$udom; }
1.62 www 3127: return $name;
1.61 www 3128: }
1.66 www 3129:
3130: # -------------------------------------------------------------------- Nickname
1.81 albertel 3131: =pod
3132:
1.648 raeburn 3133: =item * &nickname($uname,$udom)
1.81 albertel 3134:
3135: Gets a users name and returns it as a string as
3136:
3137: ""nickname""
1.66 www 3138:
1.81 albertel 3139: if the user has a nickname or
3140:
3141: "first middle last generation"
3142:
3143: if the user does not
3144:
3145: =cut
1.66 www 3146:
3147: sub nickname {
3148: my ($uname,$udom)=@_;
1.537 albertel 3149: return if (!defined($uname) || !defined($udom));
1.295 www 3150: my %names=&getnames($uname,$udom);
1.68 albertel 3151: my $name=$names{'nickname'};
1.66 www 3152: if ($name) {
3153: $name='"'.$name.'"';
3154: } else {
3155: $name=$names{'firstname'}.' '.$names{'middlename'}.' '.
3156: $names{'lastname'}.' '.$names{'generation'};
3157: $name=~s/\s+$//;
3158: $name=~s/\s+/ /g;
3159: }
3160: return $name;
3161: }
3162:
1.295 www 3163: sub getnames {
3164: my ($uname,$udom)=@_;
1.537 albertel 3165: return if (!defined($uname) || !defined($udom));
1.433 albertel 3166: if ($udom eq 'public' && $uname eq 'public') {
3167: return ('lastname' => &mt('Public'));
3168: }
1.295 www 3169: my $id=$uname.':'.$udom;
3170: my ($names,$cached)=&Apache::lonnet::is_cached_new('namescache',$id);
3171: if ($cached) {
3172: return %{$names};
3173: } else {
3174: my %loadnames=&Apache::lonnet::get('environment',
3175: ['firstname','middlename','lastname','generation','nickname'],
3176: $udom,$uname);
3177: &Apache::lonnet::do_cache_new('namescache',$id,\%loadnames);
3178: return %loadnames;
3179: }
3180: }
1.61 www 3181:
1.542 raeburn 3182: # -------------------------------------------------------------------- getemails
1.648 raeburn 3183:
1.542 raeburn 3184: =pod
3185:
1.648 raeburn 3186: =item * &getemails($uname,$udom)
1.542 raeburn 3187:
3188: Gets a user's email information and returns it as a hash with keys:
3189: notification, critnotification, permanentemail
3190:
3191: For notification and critnotification, values are comma-separated lists
1.648 raeburn 3192: of e-mail addresses; for permanentemail, value is a single e-mail address.
1.542 raeburn 3193:
1.648 raeburn 3194:
1.542 raeburn 3195: =cut
3196:
1.648 raeburn 3197:
1.466 albertel 3198: sub getemails {
3199: my ($uname,$udom)=@_;
3200: if ($udom eq 'public' && $uname eq 'public') {
3201: return;
3202: }
1.467 www 3203: if (!$udom) { $udom=$env{'user.domain'}; }
3204: if (!$uname) { $uname=$env{'user.name'}; }
1.466 albertel 3205: my $id=$uname.':'.$udom;
3206: my ($names,$cached)=&Apache::lonnet::is_cached_new('emailscache',$id);
3207: if ($cached) {
3208: return %{$names};
3209: } else {
3210: my %loadnames=&Apache::lonnet::get('environment',
3211: ['notification','critnotification',
3212: 'permanentemail'],
3213: $udom,$uname);
3214: &Apache::lonnet::do_cache_new('emailscache',$id,\%loadnames);
3215: return %loadnames;
3216: }
3217: }
3218:
1.551 albertel 3219: sub flush_email_cache {
3220: my ($uname,$udom)=@_;
3221: if (!$udom) { $udom =$env{'user.domain'}; }
3222: if (!$uname) { $uname=$env{'user.name'}; }
3223: return if ($udom eq 'public' && $uname eq 'public');
3224: my $id=$uname.':'.$udom;
3225: &Apache::lonnet::devalidate_cache_new('emailscache',$id);
3226: }
3227:
1.728 raeburn 3228: # -------------------------------------------------------------------- getlangs
3229:
3230: =pod
3231:
3232: =item * &getlangs($uname,$udom)
3233:
3234: Gets a user's language preference and returns it as a hash with key:
3235: language.
3236:
3237: =cut
3238:
3239:
3240: sub getlangs {
3241: my ($uname,$udom) = @_;
3242: if (!$udom) { $udom =$env{'user.domain'}; }
3243: if (!$uname) { $uname=$env{'user.name'}; }
3244: my $id=$uname.':'.$udom;
3245: my ($langs,$cached)=&Apache::lonnet::is_cached_new('userlangs',$id);
3246: if ($cached) {
3247: return %{$langs};
3248: } else {
3249: my %loadlangs=&Apache::lonnet::get('environment',['languages'],
3250: $udom,$uname);
3251: &Apache::lonnet::do_cache_new('userlangs',$id,\%loadlangs);
3252: return %loadlangs;
3253: }
3254: }
3255:
3256: sub flush_langs_cache {
3257: my ($uname,$udom)=@_;
3258: if (!$udom) { $udom =$env{'user.domain'}; }
3259: if (!$uname) { $uname=$env{'user.name'}; }
3260: return if ($udom eq 'public' && $uname eq 'public');
3261: my $id=$uname.':'.$udom;
3262: &Apache::lonnet::devalidate_cache_new('userlangs',$id);
3263: }
3264:
1.61 www 3265: # ------------------------------------------------------------------ Screenname
1.81 albertel 3266:
3267: =pod
3268:
1.648 raeburn 3269: =item * &screenname($uname,$udom)
1.81 albertel 3270:
3271: Gets a users screenname and returns it as a string
3272:
3273: =cut
1.61 www 3274:
3275: sub screenname {
3276: my ($uname,$udom)=@_;
1.258 albertel 3277: if ($uname eq $env{'user.name'} &&
3278: $udom eq $env{'user.domain'}) {return $env{'environment.screenname'};}
1.212 albertel 3279: my %names=&Apache::lonnet::get('environment',['screenname'],$udom,$uname);
1.68 albertel 3280: return $names{'screenname'};
1.62 www 3281: }
3282:
1.212 albertel 3283:
1.802 bisitz 3284: # ------------------------------------------------------------- Confirm Wrapper
3285: =pod
3286:
3287: =item confirmwrapper
3288:
3289: Wrap messages about completion of operation in box
3290:
3291: =cut
3292:
3293: sub confirmwrapper {
3294: my ($message)=@_;
3295: if ($message) {
3296: return "\n".'<div class="LC_confirm_box">'."\n"
3297: .$message."\n"
3298: .'</div>'."\n";
3299: } else {
3300: return $message;
3301: }
3302: }
3303:
1.62 www 3304: # ------------------------------------------------------------- Message Wrapper
3305:
3306: sub messagewrapper {
1.369 www 3307: my ($link,$username,$domain,$subject,$text)=@_;
1.62 www 3308: return
1.441 albertel 3309: '<a href="/adm/email?compose=individual&'.
3310: 'recname='.$username.'&recdom='.$domain.
3311: '&subject='.&escape($subject).'&text='.&escape($text).'" '.
1.200 matthew 3312: 'title="'.&mt('Send message').'">'.$link.'</a>';
1.74 www 3313: }
1.802 bisitz 3314:
1.74 www 3315: # --------------------------------------------------------------- Notes Wrapper
3316:
3317: sub noteswrapper {
3318: my ($link,$un,$do)=@_;
3319: return
1.896 amueller 3320: "<a href='/adm/email?recordftf=retrieve&recname=$un&recdom=$do'>$link</a>";
1.62 www 3321: }
1.802 bisitz 3322:
1.62 www 3323: # ------------------------------------------------------------- Aboutme Wrapper
3324:
3325: sub aboutmewrapper {
1.1070 raeburn 3326: my ($link,$username,$domain,$target,$class)=@_;
1.447 raeburn 3327: if (!defined($username) && !defined($domain)) {
3328: return;
3329: }
1.1096 raeburn 3330: return '<a href="/adm/'.$domain.'/'.$username.'/aboutme"'.
1.1070 raeburn 3331: ($target?' target="'.$target.'"':'').($class?' class="'.$class.'"':'').' title="'.&mt("View this user's personal information page").'">'.$link.'</a>';
1.62 www 3332: }
3333:
3334: # ------------------------------------------------------------ Syllabus Wrapper
3335:
3336: sub syllabuswrapper {
1.707 bisitz 3337: my ($linktext,$coursedir,$domain)=@_;
1.208 matthew 3338: return qq{<a href="/public/$domain/$coursedir/syllabus">$linktext</a>};
1.61 www 3339: }
1.14 harris41 3340:
1.802 bisitz 3341: # -----------------------------------------------------------------------------
3342:
1.208 matthew 3343: sub track_student_link {
1.887 raeburn 3344: my ($linktext,$sname,$sdom,$target,$start,$only_body) = @_;
1.268 albertel 3345: my $link ="/adm/trackstudent?";
1.208 matthew 3346: my $title = 'View recent activity';
3347: if (defined($sname) && $sname !~ /^\s*$/ &&
3348: defined($sdom) && $sdom !~ /^\s*$/) {
1.268 albertel 3349: $link .= "selected_student=$sname:$sdom";
1.208 matthew 3350: $title .= ' of this student';
1.268 albertel 3351: }
1.208 matthew 3352: if (defined($target) && $target !~ /^\s*$/) {
3353: $target = qq{target="$target"};
3354: } else {
3355: $target = '';
3356: }
1.268 albertel 3357: if ($start) { $link.='&start='.$start; }
1.887 raeburn 3358: if ($only_body) { $link .= '&only_body=1'; }
1.554 albertel 3359: $title = &mt($title);
3360: $linktext = &mt($linktext);
1.448 albertel 3361: return qq{<a href="$link" title="$title" $target>$linktext</a>}.
3362: &help_open_topic('View_recent_activity');
1.208 matthew 3363: }
3364:
1.781 raeburn 3365: sub slot_reservations_link {
3366: my ($linktext,$sname,$sdom,$target) = @_;
3367: my $link ="/adm/slotrequest?command=showresv&origin=aboutme";
3368: my $title = 'View slot reservation history';
3369: if (defined($sname) && $sname !~ /^\s*$/ &&
3370: defined($sdom) && $sdom !~ /^\s*$/) {
3371: $link .= "&uname=$sname&udom=$sdom";
3372: $title .= ' of this student';
3373: }
3374: if (defined($target) && $target !~ /^\s*$/) {
3375: $target = qq{target="$target"};
3376: } else {
3377: $target = '';
3378: }
3379: $title = &mt($title);
3380: $linktext = &mt($linktext);
3381: return qq{<a href="$link" title="$title" $target>$linktext</a>};
3382: # FIXME uncomment when help item created: &help_open_topic('Slot_Reservation_History');
3383:
3384: }
3385:
1.508 www 3386: # ===================================================== Display a student photo
3387:
3388:
1.509 albertel 3389: sub student_image_tag {
1.508 www 3390: my ($domain,$user)=@_;
3391: my $imgsrc=&Apache::lonnet::studentphoto($domain,$user,'jpg');
3392: if (($imgsrc) && ($imgsrc ne '/adm/lonKaputt/lonlogo_broken.gif')) {
3393: return '<img src="'.$imgsrc.'" align="right" />';
3394: } else {
3395: return '';
3396: }
3397: }
3398:
1.112 bowersj2 3399: =pod
3400:
3401: =back
3402:
3403: =head1 Access .tab File Data
3404:
3405: =over 4
3406:
1.648 raeburn 3407: =item * &languageids()
1.112 bowersj2 3408:
3409: returns list of all language ids
3410:
3411: =cut
3412:
1.14 harris41 3413: sub languageids {
1.16 harris41 3414: return sort(keys(%language));
1.14 harris41 3415: }
3416:
1.112 bowersj2 3417: =pod
3418:
1.648 raeburn 3419: =item * &languagedescription()
1.112 bowersj2 3420:
3421: returns description of a specified language id
3422:
3423: =cut
3424:
1.14 harris41 3425: sub languagedescription {
1.125 www 3426: my $code=shift;
3427: return ($supported_language{$code}?'* ':'').
3428: $language{$code}.
1.126 www 3429: ($supported_language{$code}?' ('.&mt('interface available').')':'');
1.145 www 3430: }
3431:
1.1048 foxr 3432: =pod
3433:
3434: =item * &plainlanguagedescription
3435:
3436: Returns both the plain language description (e.g. 'Creoles and Pidgins, English-based (Other)')
3437: and the language character encoding (e.g. ISO) separated by a ' - ' string.
3438:
3439: =cut
3440:
1.145 www 3441: sub plainlanguagedescription {
3442: my $code=shift;
3443: return $language{$code};
3444: }
3445:
1.1048 foxr 3446: =pod
3447:
3448: =item * &supportedlanguagecode
3449:
3450: Returns the supported language code (e.g. sptutf maps to pt) given a language
3451: code.
3452:
3453: =cut
3454:
1.145 www 3455: sub supportedlanguagecode {
3456: my $code=shift;
3457: return $supported_language{$code};
1.97 www 3458: }
3459:
1.112 bowersj2 3460: =pod
3461:
1.1048 foxr 3462: =item * &latexlanguage()
3463:
3464: Given a language key code returns the correspondnig language to use
3465: to select the correct hyphenation on LaTeX printouts. This is undef if there
3466: is no supported hyphenation for the language code.
3467:
3468: =cut
3469:
3470: sub latexlanguage {
3471: my $code = shift;
3472: return $latex_language{$code};
3473: }
3474:
3475: =pod
3476:
3477: =item * &latexhyphenation()
3478:
3479: Same as above but what's supplied is the language as it might be stored
3480: in the metadata.
3481:
3482: =cut
3483:
3484: sub latexhyphenation {
3485: my $key = shift;
3486: return $latex_language_bykey{$key};
3487: }
3488:
3489: =pod
3490:
1.648 raeburn 3491: =item * ©rightids()
1.112 bowersj2 3492:
3493: returns list of all copyrights
3494:
3495: =cut
3496:
3497: sub copyrightids {
3498: return sort(keys(%cprtag));
3499: }
3500:
3501: =pod
3502:
1.648 raeburn 3503: =item * ©rightdescription()
1.112 bowersj2 3504:
3505: returns description of a specified copyright id
3506:
3507: =cut
3508:
3509: sub copyrightdescription {
1.166 www 3510: return &mt($cprtag{shift(@_)});
1.112 bowersj2 3511: }
1.197 matthew 3512:
3513: =pod
3514:
1.648 raeburn 3515: =item * &source_copyrightids()
1.192 taceyjo1 3516:
3517: returns list of all source copyrights
3518:
3519: =cut
3520:
3521: sub source_copyrightids {
3522: return sort(keys(%scprtag));
3523: }
3524:
3525: =pod
3526:
1.648 raeburn 3527: =item * &source_copyrightdescription()
1.192 taceyjo1 3528:
3529: returns description of a specified source copyright id
3530:
3531: =cut
3532:
3533: sub source_copyrightdescription {
3534: return &mt($scprtag{shift(@_)});
3535: }
1.112 bowersj2 3536:
3537: =pod
3538:
1.648 raeburn 3539: =item * &filecategories()
1.112 bowersj2 3540:
3541: returns list of all file categories
3542:
3543: =cut
3544:
3545: sub filecategories {
3546: return sort(keys(%category_extensions));
3547: }
3548:
3549: =pod
3550:
1.648 raeburn 3551: =item * &filecategorytypes()
1.112 bowersj2 3552:
3553: returns list of file types belonging to a given file
3554: category
3555:
3556: =cut
3557:
3558: sub filecategorytypes {
1.356 albertel 3559: my ($cat) = @_;
3560: return @{$category_extensions{lc($cat)}};
1.112 bowersj2 3561: }
3562:
3563: =pod
3564:
1.648 raeburn 3565: =item * &fileembstyle()
1.112 bowersj2 3566:
3567: returns embedding style for a specified file type
3568:
3569: =cut
3570:
3571: sub fileembstyle {
3572: return $fe{lc(shift(@_))};
1.169 www 3573: }
3574:
1.351 www 3575: sub filemimetype {
3576: return $fm{lc(shift(@_))};
3577: }
3578:
1.169 www 3579:
3580: sub filecategoryselect {
3581: my ($name,$value)=@_;
1.189 matthew 3582: return &select_form($value,$name,
1.970 raeburn 3583: {'' => &mt('Any category'), map { $_,$_ } sort(keys(%category_extensions))});
1.112 bowersj2 3584: }
3585:
3586: =pod
3587:
1.648 raeburn 3588: =item * &filedescription()
1.112 bowersj2 3589:
3590: returns description for a specified file type
3591:
3592: =cut
3593:
3594: sub filedescription {
1.188 matthew 3595: my $file_description = $fd{lc(shift())};
3596: $file_description =~ s:([\[\]]):~$1:g;
3597: return &mt($file_description);
1.112 bowersj2 3598: }
3599:
3600: =pod
3601:
1.648 raeburn 3602: =item * &filedescriptionex()
1.112 bowersj2 3603:
3604: returns description for a specified file type with
3605: extra formatting
3606:
3607: =cut
3608:
3609: sub filedescriptionex {
3610: my $ex=shift;
1.188 matthew 3611: my $file_description = $fd{lc($ex)};
3612: $file_description =~ s:([\[\]]):~$1:g;
3613: return '.'.$ex.' '.&mt($file_description);
1.112 bowersj2 3614: }
3615:
3616: # End of .tab access
3617: =pod
3618:
3619: =back
3620:
3621: =cut
3622:
3623: # ------------------------------------------------------------------ File Types
3624: sub fileextensions {
3625: return sort(keys(%fe));
3626: }
3627:
1.97 www 3628: # ----------------------------------------------------------- Display Languages
3629: # returns a hash with all desired display languages
3630: #
3631:
3632: sub display_languages {
3633: my %languages=();
1.695 raeburn 3634: foreach my $lang (&Apache::lonlocal::preferred_languages()) {
1.356 albertel 3635: $languages{$lang}=1;
1.97 www 3636: }
3637: &get_unprocessed_cgi($ENV{'QUERY_STRING'},['displaylanguage']);
1.258 albertel 3638: if ($env{'form.displaylanguage'}) {
1.356 albertel 3639: foreach my $lang (split(/\s*(\,|\;|\:)\s*/,$env{'form.displaylanguage'})) {
3640: $languages{$lang}=1;
1.97 www 3641: }
3642: }
3643: return %languages;
1.14 harris41 3644: }
3645:
1.582 albertel 3646: sub languages {
3647: my ($possible_langs) = @_;
1.695 raeburn 3648: my @preferred_langs = &Apache::lonlocal::preferred_languages();
1.582 albertel 3649: if (!ref($possible_langs)) {
3650: if( wantarray ) {
3651: return @preferred_langs;
3652: } else {
3653: return $preferred_langs[0];
3654: }
3655: }
3656: my %possibilities = map { $_ => 1 } (@$possible_langs);
3657: my @preferred_possibilities;
3658: foreach my $preferred_lang (@preferred_langs) {
3659: if (exists($possibilities{$preferred_lang})) {
3660: push(@preferred_possibilities, $preferred_lang);
3661: }
3662: }
3663: if( wantarray ) {
3664: return @preferred_possibilities;
3665: }
3666: return $preferred_possibilities[0];
3667: }
3668:
1.742 raeburn 3669: sub user_lang {
3670: my ($touname,$toudom,$fromcid) = @_;
3671: my @userlangs;
3672: if (($fromcid ne '') && ($env{'course.'.$fromcid.'.languages'} ne '')) {
3673: @userlangs=(@userlangs,split(/\s*(\,|\;|\:)\s*/,
3674: $env{'course.'.$fromcid.'.languages'}));
3675: } else {
3676: my %langhash = &getlangs($touname,$toudom);
3677: if ($langhash{'languages'} ne '') {
3678: @userlangs = split(/\s*(\,|\;|\:)\s*/,$langhash{'languages'});
3679: } else {
3680: my %domdefs = &Apache::lonnet::get_domain_defaults($toudom);
3681: if ($domdefs{'lang_def'} ne '') {
3682: @userlangs = ($domdefs{'lang_def'});
3683: }
3684: }
3685: }
3686: my @languages=&Apache::lonlocal::get_genlanguages(@userlangs);
3687: my $user_lh = Apache::localize->get_handle(@languages);
3688: return $user_lh;
3689: }
3690:
3691:
1.112 bowersj2 3692: ###############################################################
3693: ## Student Answer Attempts ##
3694: ###############################################################
3695:
3696: =pod
3697:
3698: =head1 Alternate Problem Views
3699:
3700: =over 4
3701:
1.648 raeburn 3702: =item * &get_previous_attempt($symb, $username, $domain, $course,
1.112 bowersj2 3703: $getattempt, $regexp, $gradesub)
3704:
3705: Return string with previous attempt on problem. Arguments:
3706:
3707: =over 4
3708:
3709: =item * $symb: Problem, including path
3710:
3711: =item * $username: username of the desired student
3712:
3713: =item * $domain: domain of the desired student
1.14 harris41 3714:
1.112 bowersj2 3715: =item * $course: Course ID
1.14 harris41 3716:
1.112 bowersj2 3717: =item * $getattempt: Leave blank for all attempts, otherwise put
3718: something
1.14 harris41 3719:
1.112 bowersj2 3720: =item * $regexp: if string matches this regexp, the string will be
3721: sent to $gradesub
1.14 harris41 3722:
1.112 bowersj2 3723: =item * $gradesub: routine that processes the string if it matches $regexp
1.14 harris41 3724:
1.112 bowersj2 3725: =back
1.14 harris41 3726:
1.112 bowersj2 3727: The output string is a table containing all desired attempts, if any.
1.16 harris41 3728:
1.112 bowersj2 3729: =cut
1.1 albertel 3730:
3731: sub get_previous_attempt {
1.43 ng 3732: my ($symb,$username,$domain,$course,$getattempt,$regexp,$gradesub)=@_;
1.1 albertel 3733: my $prevattempts='';
1.43 ng 3734: no strict 'refs';
1.1 albertel 3735: if ($symb) {
1.3 albertel 3736: my (%returnhash)=
3737: &Apache::lonnet::restore($symb,$course,$domain,$username);
1.1 albertel 3738: if ($returnhash{'version'}) {
3739: my %lasthash=();
3740: my $version;
3741: for ($version=1;$version<=$returnhash{'version'};$version++) {
1.356 albertel 3742: foreach my $key (sort(split(/\:/,$returnhash{$version.':keys'}))) {
3743: $lasthash{$key}=$returnhash{$version.':'.$key};
1.19 harris41 3744: }
1.1 albertel 3745: }
1.596 albertel 3746: $prevattempts=&start_data_table().&start_data_table_header_row();
3747: $prevattempts.='<th>'.&mt('History').'</th>';
1.978 raeburn 3748: my (%typeparts,%lasthidden);
1.945 raeburn 3749: my $showsurv=&Apache::lonnet::allowed('vas',$env{'request.course.id'});
1.356 albertel 3750: foreach my $key (sort(keys(%lasthash))) {
3751: my ($ign,@parts) = split(/\./,$key);
1.41 ng 3752: if ($#parts > 0) {
1.31 albertel 3753: my $data=$parts[-1];
1.989 raeburn 3754: next if ($data eq 'foilorder');
1.31 albertel 3755: pop(@parts);
1.1010 www 3756: $prevattempts.='<th>'.&mt('Part ').join('.',@parts).'<br />'.$data.' </th>';
1.945 raeburn 3757: if ($data eq 'type') {
3758: unless ($showsurv) {
3759: my $id = join(',',@parts);
3760: $typeparts{$ign.'.'.$id} = $lasthash{$key};
1.978 raeburn 3761: if (($lasthash{$key} eq 'anonsurvey') || ($lasthash{$key} eq 'anonsurveycred')) {
3762: $lasthidden{$ign.'.'.$id} = 1;
3763: }
1.945 raeburn 3764: }
1.1010 www 3765: }
1.31 albertel 3766: } else {
1.41 ng 3767: if ($#parts == 0) {
3768: $prevattempts.='<th>'.$parts[0].'</th>';
3769: } else {
3770: $prevattempts.='<th>'.$ign.'</th>';
3771: }
1.31 albertel 3772: }
1.16 harris41 3773: }
1.596 albertel 3774: $prevattempts.=&end_data_table_header_row();
1.40 ng 3775: if ($getattempt eq '') {
3776: for ($version=1;$version<=$returnhash{'version'};$version++) {
1.945 raeburn 3777: my @hidden;
3778: if (%typeparts) {
3779: foreach my $id (keys(%typeparts)) {
3780: if (($returnhash{$version.':'.$id.'.type'} eq 'anonsurvey') || ($returnhash{$version.':'.$id.'.type'} eq 'anonsurveycred')) {
3781: push(@hidden,$id);
3782: }
3783: }
3784: }
3785: $prevattempts.=&start_data_table_row().
3786: '<td>'.&mt('Transaction [_1]',$version).'</td>';
3787: if (@hidden) {
3788: foreach my $key (sort(keys(%lasthash))) {
1.989 raeburn 3789: next if ($key =~ /\.foilorder$/);
1.945 raeburn 3790: my $hide;
3791: foreach my $id (@hidden) {
3792: if ($key =~ /^\Q$id\E/) {
3793: $hide = 1;
3794: last;
3795: }
3796: }
3797: if ($hide) {
3798: my ($id,$data) = ($key =~ /^(.+)\.([^.]+)$/);
3799: if (($data eq 'award') || ($data eq 'awarddetail')) {
3800: my $value = &format_previous_attempt_value($key,
3801: $returnhash{$version.':'.$key});
3802: $prevattempts.='<td>'.$value.' </td>';
3803: } else {
3804: $prevattempts.='<td> </td>';
3805: }
3806: } else {
3807: if ($key =~ /\./) {
3808: my $value = &format_previous_attempt_value($key,
3809: $returnhash{$version.':'.$key});
3810: $prevattempts.='<td>'.$value.' </td>';
3811: } else {
3812: $prevattempts.='<td> </td>';
3813: }
3814: }
3815: }
3816: } else {
3817: foreach my $key (sort(keys(%lasthash))) {
1.989 raeburn 3818: next if ($key =~ /\.foilorder$/);
1.945 raeburn 3819: my $value = &format_previous_attempt_value($key,
3820: $returnhash{$version.':'.$key});
3821: $prevattempts.='<td>'.$value.' </td>';
3822: }
3823: }
3824: $prevattempts.=&end_data_table_row();
1.40 ng 3825: }
1.1 albertel 3826: }
1.945 raeburn 3827: my @currhidden = keys(%lasthidden);
1.596 albertel 3828: $prevattempts.=&start_data_table_row().'<td>'.&mt('Current').'</td>';
1.356 albertel 3829: foreach my $key (sort(keys(%lasthash))) {
1.989 raeburn 3830: next if ($key =~ /\.foilorder$/);
1.945 raeburn 3831: if (%typeparts) {
3832: my $hidden;
3833: foreach my $id (@currhidden) {
3834: if ($key =~ /^\Q$id\E/) {
3835: $hidden = 1;
3836: last;
3837: }
3838: }
3839: if ($hidden) {
3840: my ($id,$data) = ($key =~ /^(.+)\.([^.]+)$/);
3841: if (($data eq 'award') || ($data eq 'awarddetail')) {
3842: my $value = &format_previous_attempt_value($key,$lasthash{$key});
3843: if ($key =~/$regexp$/ && (defined &$gradesub)) {
3844: $value = &$gradesub($value);
3845: }
3846: $prevattempts.='<td>'.$value.' </td>';
3847: } else {
3848: $prevattempts.='<td> </td>';
3849: }
3850: } else {
3851: my $value = &format_previous_attempt_value($key,$lasthash{$key});
3852: if ($key =~/$regexp$/ && (defined &$gradesub)) {
3853: $value = &$gradesub($value);
3854: }
3855: $prevattempts.='<td>'.$value.' </td>';
3856: }
3857: } else {
3858: my $value = &format_previous_attempt_value($key,$lasthash{$key});
3859: if ($key =~/$regexp$/ && (defined &$gradesub)) {
3860: $value = &$gradesub($value);
3861: }
3862: $prevattempts.='<td>'.$value.' </td>';
3863: }
1.16 harris41 3864: }
1.596 albertel 3865: $prevattempts.= &end_data_table_row().&end_data_table();
1.1 albertel 3866: } else {
1.596 albertel 3867: $prevattempts=
3868: &start_data_table().&start_data_table_row().
3869: '<td>'.&mt('Nothing submitted - no attempts.').'</td>'.
3870: &end_data_table_row().&end_data_table();
1.1 albertel 3871: }
3872: } else {
1.596 albertel 3873: $prevattempts=
3874: &start_data_table().&start_data_table_row().
3875: '<td>'.&mt('No data.').'</td>'.
3876: &end_data_table_row().&end_data_table();
1.1 albertel 3877: }
1.10 albertel 3878: }
3879:
1.581 albertel 3880: sub format_previous_attempt_value {
3881: my ($key,$value) = @_;
1.1011 www 3882: if (($key =~ /timestamp/) || ($key=~/duedate/)) {
1.581 albertel 3883: $value = &Apache::lonlocal::locallocaltime($value);
3884: } elsif (ref($value) eq 'ARRAY') {
3885: $value = '('.join(', ', @{ $value }).')';
1.988 raeburn 3886: } elsif ($key =~ /answerstring$/) {
3887: my %answers = &Apache::lonnet::str2hash($value);
3888: my @anskeys = sort(keys(%answers));
3889: if (@anskeys == 1) {
3890: my $answer = $answers{$anskeys[0]};
1.1001 raeburn 3891: if ($answer =~ m{\0}) {
3892: $answer =~ s{\0}{,}g;
1.988 raeburn 3893: }
3894: my $tag_internal_answer_name = 'INTERNAL';
3895: if ($anskeys[0] eq $tag_internal_answer_name) {
3896: $value = $answer;
3897: } else {
3898: $value = $anskeys[0].'='.$answer;
3899: }
3900: } else {
3901: foreach my $ans (@anskeys) {
3902: my $answer = $answers{$ans};
1.1001 raeburn 3903: if ($answer =~ m{\0}) {
3904: $answer =~ s{\0}{,}g;
1.988 raeburn 3905: }
3906: $value .= $ans.'='.$answer.'<br />';;
3907: }
3908: }
1.581 albertel 3909: } else {
3910: $value = &unescape($value);
3911: }
3912: return $value;
3913: }
3914:
3915:
1.107 albertel 3916: sub relative_to_absolute {
3917: my ($url,$output)=@_;
3918: my $parser=HTML::TokeParser->new(\$output);
3919: my $token;
3920: my $thisdir=$url;
3921: my @rlinks=();
3922: while ($token=$parser->get_token) {
3923: if ($token->[0] eq 'S') {
3924: if ($token->[1] eq 'a') {
3925: if ($token->[2]->{'href'}) {
3926: $rlinks[$#rlinks+1]=$token->[2]->{'href'};
3927: }
3928: } elsif ($token->[1] eq 'img' || $token->[1] eq 'embed' ) {
3929: $rlinks[$#rlinks+1]=$token->[2]->{'src'};
3930: } elsif ($token->[1] eq 'base') {
3931: $thisdir=$token->[2]->{'href'};
3932: }
3933: }
3934: }
3935: $thisdir=~s-/[^/]*$--;
1.356 albertel 3936: foreach my $link (@rlinks) {
1.726 raeburn 3937: unless (($link=~/^https?\:\/\//i) ||
1.356 albertel 3938: ($link=~/^\//) ||
3939: ($link=~/^javascript:/i) ||
3940: ($link=~/^mailto:/i) ||
3941: ($link=~/^\#/)) {
3942: my $newlocation=&Apache::lonnet::hreflocation($thisdir,$link);
3943: $output=~s/(\"|\'|\=\s*)\Q$link\E(\"|\'|\s|\>)/$1$newlocation$2/;
1.107 albertel 3944: }
3945: }
3946: # -------------------------------------------------- Deal with Applet codebases
3947: $output=~s/(\<applet[^\>]+)(codebase\=[^\S\>]+)*([^\>]*)\>/$1.($2?$2:' codebase="'.$thisdir.'"').$3.'>'/gei;
3948: return $output;
3949: }
3950:
1.112 bowersj2 3951: =pod
3952:
1.648 raeburn 3953: =item * &get_student_view()
1.112 bowersj2 3954:
3955: show a snapshot of what student was looking at
3956:
3957: =cut
3958:
1.10 albertel 3959: sub get_student_view {
1.186 albertel 3960: my ($symb,$username,$domain,$courseid,$target,$moreenv) = @_;
1.114 www 3961: my ($map,$id,$feedurl) = &Apache::lonnet::decode_symb($symb);
1.186 albertel 3962: my (%form);
1.10 albertel 3963: my @elements=('symb','courseid','domain','username');
3964: foreach my $element (@elements) {
1.186 albertel 3965: $form{'grade_'.$element}=eval '$'.$element #'
1.10 albertel 3966: }
1.186 albertel 3967: if (defined($moreenv)) {
3968: %form=(%form,%{$moreenv});
3969: }
1.236 albertel 3970: if (defined($target)) { $form{'grade_target'} = $target; }
1.107 albertel 3971: $feedurl=&Apache::lonnet::clutter($feedurl);
1.650 www 3972: my ($userview,$response)=&Apache::lonnet::ssi_body($feedurl,%form);
1.11 albertel 3973: $userview=~s/\<body[^\>]*\>//gi;
3974: $userview=~s/\<\/body\>//gi;
3975: $userview=~s/\<html\>//gi;
3976: $userview=~s/\<\/html\>//gi;
3977: $userview=~s/\<head\>//gi;
3978: $userview=~s/\<\/head\>//gi;
3979: $userview=~s/action\s*\=/would_be_action\=/gi;
1.107 albertel 3980: $userview=&relative_to_absolute($feedurl,$userview);
1.650 www 3981: if (wantarray) {
3982: return ($userview,$response);
3983: } else {
3984: return $userview;
3985: }
3986: }
3987:
3988: sub get_student_view_with_retries {
3989: my ($symb,$retries,$username,$domain,$courseid,$target,$moreenv) = @_;
3990:
3991: my $ok = 0; # True if we got a good response.
3992: my $content;
3993: my $response;
3994:
3995: # Try to get the student_view done. within the retries count:
3996:
3997: do {
3998: ($content, $response) = &get_student_view($symb,$username,$domain,$courseid,$target,$moreenv);
3999: $ok = $response->is_success;
4000: if (!$ok) {
4001: &Apache::lonnet::logthis("Failed get_student_view_with_retries on $symb: ".$response->is_success.', '.$response->code.', '.$response->message);
4002: }
4003: $retries--;
4004: } while (!$ok && ($retries > 0));
4005:
4006: if (!$ok) {
4007: $content = ''; # On error return an empty content.
4008: }
1.651 www 4009: if (wantarray) {
4010: return ($content, $response);
4011: } else {
4012: return $content;
4013: }
1.11 albertel 4014: }
4015:
1.112 bowersj2 4016: =pod
4017:
1.648 raeburn 4018: =item * &get_student_answers()
1.112 bowersj2 4019:
4020: show a snapshot of how student was answering problem
4021:
4022: =cut
4023:
1.11 albertel 4024: sub get_student_answers {
1.100 sakharuk 4025: my ($symb,$username,$domain,$courseid,%form) = @_;
1.114 www 4026: my ($map,$id,$feedurl) = &Apache::lonnet::decode_symb($symb);
1.186 albertel 4027: my (%moreenv);
1.11 albertel 4028: my @elements=('symb','courseid','domain','username');
4029: foreach my $element (@elements) {
1.186 albertel 4030: $moreenv{'grade_'.$element}=eval '$'.$element #'
1.10 albertel 4031: }
1.186 albertel 4032: $moreenv{'grade_target'}='answer';
4033: %moreenv=(%form,%moreenv);
1.497 raeburn 4034: $feedurl = &Apache::lonnet::clutter($feedurl);
4035: my $userview=&Apache::lonnet::ssi($feedurl,%moreenv);
1.10 albertel 4036: return $userview;
1.1 albertel 4037: }
1.116 albertel 4038:
4039: =pod
4040:
4041: =item * &submlink()
4042:
1.242 albertel 4043: Inputs: $text $uname $udom $symb $target
1.116 albertel 4044:
4045: Returns: A link to grades.pm such as to see the SUBM view of a student
4046:
4047: =cut
4048:
4049: ###############################################
4050: sub submlink {
1.242 albertel 4051: my ($text,$uname,$udom,$symb,$target)=@_;
1.116 albertel 4052: if (!($uname && $udom)) {
4053: (my $cursymb, my $courseid,$udom,$uname)=
1.463 albertel 4054: &Apache::lonnet::whichuser($symb);
1.116 albertel 4055: if (!$symb) { $symb=$cursymb; }
4056: }
1.254 matthew 4057: if (!$symb) { $symb=&Apache::lonnet::symbread(); }
1.369 www 4058: $symb=&escape($symb);
1.960 bisitz 4059: if ($target) { $target=" target=\"$target\""; }
4060: return
4061: '<a href="/adm/grades?command=submission'.
4062: '&symb='.$symb.
4063: '&student='.$uname.
4064: '&userdom='.$udom.'"'.
4065: $target.'>'.$text.'</a>';
1.242 albertel 4066: }
4067: ##############################################
4068:
4069: =pod
4070:
4071: =item * &pgrdlink()
4072:
4073: Inputs: $text $uname $udom $symb $target
4074:
4075: Returns: A link to grades.pm such as to see the PGRD view of a student
4076:
4077: =cut
4078:
4079: ###############################################
4080: sub pgrdlink {
4081: my $link=&submlink(@_);
4082: $link=~s/(&command=submission)/$1&showgrading=yes/;
4083: return $link;
4084: }
4085: ##############################################
4086:
4087: =pod
4088:
4089: =item * &pprmlink()
4090:
4091: Inputs: $text $uname $udom $symb $target
4092:
4093: Returns: A link to parmset.pm such as to see the PPRM view of a
1.283 albertel 4094: student and a specific resource
1.242 albertel 4095:
4096: =cut
4097:
4098: ###############################################
4099: sub pprmlink {
4100: my ($text,$uname,$udom,$symb,$target)=@_;
4101: if (!($uname && $udom)) {
4102: (my $cursymb, my $courseid,$udom,$uname)=
1.463 albertel 4103: &Apache::lonnet::whichuser($symb);
1.242 albertel 4104: if (!$symb) { $symb=$cursymb; }
4105: }
1.254 matthew 4106: if (!$symb) { $symb=&Apache::lonnet::symbread(); }
1.369 www 4107: $symb=&escape($symb);
1.242 albertel 4108: if ($target) { $target="target=\"$target\""; }
1.595 albertel 4109: return '<a href="/adm/parmset?command=set&'.
4110: 'symb='.$symb.'&uname='.$uname.
4111: '&udom='.$udom.'" '.$target.'>'.$text.'</a>';
1.116 albertel 4112: }
4113: ##############################################
1.37 matthew 4114:
1.112 bowersj2 4115: =pod
4116:
4117: =back
4118:
4119: =cut
4120:
1.37 matthew 4121: ###############################################
1.51 www 4122:
4123:
4124: sub timehash {
1.687 raeburn 4125: my ($thistime) = @_;
4126: my $timezone = &Apache::lonlocal::gettimezone();
4127: my $dt = DateTime->from_epoch(epoch => $thistime)
4128: ->set_time_zone($timezone);
4129: my $wday = $dt->day_of_week();
4130: if ($wday == 7) { $wday = 0; }
4131: return ( 'second' => $dt->second(),
4132: 'minute' => $dt->minute(),
4133: 'hour' => $dt->hour(),
4134: 'day' => $dt->day_of_month(),
4135: 'month' => $dt->month(),
4136: 'year' => $dt->year(),
4137: 'weekday' => $wday,
4138: 'dayyear' => $dt->day_of_year(),
4139: 'dlsav' => $dt->is_dst() );
1.51 www 4140: }
4141:
1.370 www 4142: sub utc_string {
4143: my ($date)=@_;
1.371 www 4144: return strftime("%Y%m%dT%H%M%SZ",gmtime($date));
1.370 www 4145: }
4146:
1.51 www 4147: sub maketime {
4148: my %th=@_;
1.687 raeburn 4149: my ($epoch_time,$timezone,$dt);
4150: $timezone = &Apache::lonlocal::gettimezone();
4151: eval {
4152: $dt = DateTime->new( year => $th{'year'},
4153: month => $th{'month'},
4154: day => $th{'day'},
4155: hour => $th{'hour'},
4156: minute => $th{'minute'},
4157: second => $th{'second'},
4158: time_zone => $timezone,
4159: );
4160: };
4161: if (!$@) {
4162: $epoch_time = $dt->epoch;
4163: if ($epoch_time) {
4164: return $epoch_time;
4165: }
4166: }
1.51 www 4167: return POSIX::mktime(
4168: ($th{'seconds'},$th{'minutes'},$th{'hours'},
1.210 www 4169: $th{'day'},$th{'month'}-1,$th{'year'}-1900,0,0,-1));
1.70 www 4170: }
4171:
4172: #########################################
1.51 www 4173:
4174: sub findallcourses {
1.482 raeburn 4175: my ($roles,$uname,$udom) = @_;
1.355 albertel 4176: my %roles;
4177: if (ref($roles)) { %roles = map { $_ => 1 } @{$roles}; }
1.348 albertel 4178: my %courses;
1.51 www 4179: my $now=time;
1.482 raeburn 4180: if (!defined($uname)) {
4181: $uname = $env{'user.name'};
4182: }
4183: if (!defined($udom)) {
4184: $udom = $env{'user.domain'};
4185: }
4186: if (($uname ne $env{'user.name'}) || ($udom ne $env{'user.domain'})) {
1.1073 raeburn 4187: my %roleshash = &Apache::lonnet::dump('roles',$udom,$uname);
1.482 raeburn 4188: if (!%roles) {
4189: %roles = (
4190: cc => 1,
1.907 raeburn 4191: co => 1,
1.482 raeburn 4192: in => 1,
4193: ep => 1,
4194: ta => 1,
4195: cr => 1,
4196: st => 1,
4197: );
4198: }
4199: foreach my $entry (keys(%roleshash)) {
4200: my ($trole,$tend,$tstart) = split(/_/,$roleshash{$entry});
4201: if ($trole =~ /^cr/) {
4202: next if (!exists($roles{$trole}) && !exists($roles{'cr'}));
4203: } else {
4204: next if (!exists($roles{$trole}));
4205: }
4206: if ($tend) {
4207: next if ($tend < $now);
4208: }
4209: if ($tstart) {
4210: next if ($tstart > $now);
4211: }
1.1058 raeburn 4212: my ($cdom,$cnum,$sec,$cnumpart,$secpart,$role);
1.482 raeburn 4213: (undef,$cdom,$cnumpart,$secpart) = split(/\//,$entry);
1.1058 raeburn 4214: my $value = $trole.'/'.$cdom.'/';
1.482 raeburn 4215: if ($secpart eq '') {
4216: ($cnum,$role) = split(/_/,$cnumpart);
4217: $sec = 'none';
1.1058 raeburn 4218: $value .= $cnum.'/';
1.482 raeburn 4219: } else {
4220: $cnum = $cnumpart;
4221: ($sec,$role) = split(/_/,$secpart);
1.1058 raeburn 4222: $value .= $cnum.'/'.$sec;
4223: }
4224: if (ref($courses{$cdom.'_'.$cnum}{$sec}) eq 'ARRAY') {
4225: unless (grep(/^\Q$value\E$/,@{$courses{$cdom.'_'.$cnum}{$sec}})) {
4226: push(@{$courses{$cdom.'_'.$cnum}{$sec}},$value);
4227: }
4228: } else {
4229: @{$courses{$cdom.'_'.$cnum}{$sec}} = ($value);
1.490 raeburn 4230: }
1.482 raeburn 4231: }
4232: } else {
4233: foreach my $key (keys(%env)) {
1.483 albertel 4234: if ( $key=~m{^user\.role\.(\w+)\./($match_domain)/($match_courseid)/?(\w*)$} ||
4235: $key=~m{^user\.role\.(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_courseid)/?(\w*)$}) {
1.482 raeburn 4236: my ($role,$cdom,$cnum,$sec) = ($1,$2,$3,$4);
4237: next if ($role eq 'ca' || $role eq 'aa');
4238: next if (%roles && !exists($roles{$role}));
4239: my ($starttime,$endtime)=split(/\./,$env{$key});
4240: my $active=1;
4241: if ($starttime) {
4242: if ($now<$starttime) { $active=0; }
4243: }
4244: if ($endtime) {
4245: if ($now>$endtime) { $active=0; }
4246: }
4247: if ($active) {
1.1058 raeburn 4248: my $value = $role.'/'.$cdom.'/'.$cnum.'/';
1.482 raeburn 4249: if ($sec eq '') {
4250: $sec = 'none';
1.1058 raeburn 4251: } else {
4252: $value .= $sec;
4253: }
4254: if (ref($courses{$cdom.'_'.$cnum}{$sec}) eq 'ARRAY') {
4255: unless (grep(/^\Q$value\E$/,@{$courses{$cdom.'_'.$cnum}{$sec}})) {
4256: push(@{$courses{$cdom.'_'.$cnum}{$sec}},$value);
4257: }
4258: } else {
4259: @{$courses{$cdom.'_'.$cnum}{$sec}} = ($value);
1.482 raeburn 4260: }
1.474 raeburn 4261: }
4262: }
1.51 www 4263: }
4264: }
1.474 raeburn 4265: return %courses;
1.51 www 4266: }
1.37 matthew 4267:
1.54 www 4268: ###############################################
1.474 raeburn 4269:
4270: sub blockcheck {
1.1062 raeburn 4271: my ($setters,$activity,$uname,$udom,$url) = @_;
1.490 raeburn 4272:
4273: if (!defined($udom)) {
4274: $udom = $env{'user.domain'};
4275: }
4276: if (!defined($uname)) {
4277: $uname = $env{'user.name'};
4278: }
4279:
4280: # If uname and udom are for a course, check for blocks in the course.
4281:
4282: if (&Apache::lonnet::is_course($udom,$uname)) {
1.1062 raeburn 4283: my ($startblock,$endblock,$triggerblock) =
4284: &get_blocks($setters,$activity,$udom,$uname,$url);
4285: return ($startblock,$endblock,$triggerblock);
1.490 raeburn 4286: }
1.474 raeburn 4287:
1.502 raeburn 4288: my $startblock = 0;
4289: my $endblock = 0;
1.1062 raeburn 4290: my $triggerblock = '';
1.482 raeburn 4291: my %live_courses = &findallcourses(undef,$uname,$udom);
1.474 raeburn 4292:
1.490 raeburn 4293: # If uname is for a user, and activity is course-specific, i.e.,
4294: # boards, chat or groups, check for blocking in current course only.
1.474 raeburn 4295:
1.490 raeburn 4296: if (($activity eq 'boards' || $activity eq 'chat' ||
4297: $activity eq 'groups') && ($env{'request.course.id'})) {
4298: foreach my $key (keys(%live_courses)) {
4299: if ($key ne $env{'request.course.id'}) {
4300: delete($live_courses{$key});
4301: }
4302: }
4303: }
4304:
4305: my $otheruser = 0;
4306: my %own_courses;
4307: if ((($uname ne $env{'user.name'})) || ($udom ne $env{'user.domain'})) {
4308: # Resource belongs to user other than current user.
4309: $otheruser = 1;
4310: # Gather courses for current user
4311: %own_courses =
4312: &findallcourses(undef,$env{'user.name'},$env{'user.domain'});
4313: }
4314:
4315: # Gather active course roles - course coordinator, instructor,
4316: # exam proctor, ta, student, or custom role.
1.474 raeburn 4317:
4318: foreach my $course (keys(%live_courses)) {
1.482 raeburn 4319: my ($cdom,$cnum);
4320: if ((defined($env{'course.'.$course.'.domain'})) && (defined($env{'course.'.$course.'.num'}))) {
4321: $cdom = $env{'course.'.$course.'.domain'};
4322: $cnum = $env{'course.'.$course.'.num'};
4323: } else {
1.490 raeburn 4324: ($cdom,$cnum) = split(/_/,$course);
1.482 raeburn 4325: }
4326: my $no_ownblock = 0;
4327: my $no_userblock = 0;
1.533 raeburn 4328: if ($otheruser && $activity ne 'com') {
1.490 raeburn 4329: # Check if current user has 'evb' priv for this
4330: if (defined($own_courses{$course})) {
4331: foreach my $sec (keys(%{$own_courses{$course}})) {
4332: my $checkrole = 'cm./'.$cdom.'/'.$cnum;
4333: if ($sec ne 'none') {
4334: $checkrole .= '/'.$sec;
4335: }
4336: if (&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) {
4337: $no_ownblock = 1;
4338: last;
4339: }
4340: }
4341: }
4342: # if they have 'evb' priv and are currently not playing student
4343: next if (($no_ownblock) &&
4344: ($env{'request.role'} !~ m{^st\./$cdom/$cnum}));
4345: }
1.474 raeburn 4346: foreach my $sec (keys(%{$live_courses{$course}})) {
1.482 raeburn 4347: my $checkrole = 'cm./'.$cdom.'/'.$cnum;
1.474 raeburn 4348: if ($sec ne 'none') {
1.482 raeburn 4349: $checkrole .= '/'.$sec;
1.474 raeburn 4350: }
1.490 raeburn 4351: if ($otheruser) {
4352: # Resource belongs to user other than current user.
4353: # Assemble privs for that user, and check for 'evb' priv.
1.1058 raeburn 4354: my (%allroles,%userroles);
4355: if (ref($live_courses{$course}{$sec}) eq 'ARRAY') {
4356: foreach my $entry (@{$live_courses{$course}{$sec}}) {
4357: my ($trole,$tdom,$tnum,$tsec);
4358: if ($entry =~ /^cr/) {
4359: ($trole,$tdom,$tnum,$tsec) =
4360: ($entry =~ m|^(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_username)/?(\w*)$|);
4361: } else {
4362: ($trole,$tdom,$tnum,$tsec) = split(/\//,$entry);
4363: }
4364: my ($spec,$area,$trest);
4365: $area = '/'.$tdom.'/'.$tnum;
4366: $trest = $tnum;
4367: if ($tsec ne '') {
4368: $area .= '/'.$tsec;
4369: $trest .= '/'.$tsec;
4370: }
4371: $spec = $trole.'.'.$area;
4372: if ($trole =~ /^cr/) {
4373: &Apache::lonnet::custom_roleprivs(\%allroles,$trole,
4374: $tdom,$spec,$trest,$area);
4375: } else {
4376: &Apache::lonnet::standard_roleprivs(\%allroles,$trole,
4377: $tdom,$spec,$trest,$area);
4378: }
4379: }
4380: my ($author,$adv) = &Apache::lonnet::set_userprivs(\%userroles,\%allroles);
4381: if ($userroles{'user.priv.'.$checkrole} =~ /evb\&([^\:]*)/) {
4382: if ($1) {
4383: $no_userblock = 1;
4384: last;
4385: }
1.486 raeburn 4386: }
4387: }
1.490 raeburn 4388: } else {
4389: # Resource belongs to current user
4390: # Check for 'evb' priv via lonnet::allowed().
1.482 raeburn 4391: if (&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) {
4392: $no_ownblock = 1;
4393: last;
4394: }
1.474 raeburn 4395: }
4396: }
4397: # if they have the evb priv and are currently not playing student
1.482 raeburn 4398: next if (($no_ownblock) &&
1.491 albertel 4399: ($env{'request.role'} !~ m{^st\./\Q$cdom\E/\Q$cnum\E}));
1.482 raeburn 4400: next if ($no_userblock);
1.474 raeburn 4401:
1.866 kalberla 4402: # Retrieve blocking times and identity of locker for course
1.490 raeburn 4403: # of specified user, unless user has 'evb' privilege.
1.502 raeburn 4404:
1.1062 raeburn 4405: my ($start,$end,$trigger) =
4406: &get_blocks($setters,$activity,$cdom,$cnum,$url);
1.502 raeburn 4407: if (($start != 0) &&
4408: (($startblock == 0) || ($startblock > $start))) {
4409: $startblock = $start;
1.1062 raeburn 4410: if ($trigger ne '') {
4411: $triggerblock = $trigger;
4412: }
1.502 raeburn 4413: }
4414: if (($end != 0) &&
4415: (($endblock == 0) || ($endblock < $end))) {
4416: $endblock = $end;
1.1062 raeburn 4417: if ($trigger ne '') {
4418: $triggerblock = $trigger;
4419: }
1.502 raeburn 4420: }
1.490 raeburn 4421: }
1.1062 raeburn 4422: return ($startblock,$endblock,$triggerblock);
1.490 raeburn 4423: }
4424:
4425: sub get_blocks {
1.1062 raeburn 4426: my ($setters,$activity,$cdom,$cnum,$url) = @_;
1.490 raeburn 4427: my $startblock = 0;
4428: my $endblock = 0;
1.1062 raeburn 4429: my $triggerblock = '';
1.490 raeburn 4430: my $course = $cdom.'_'.$cnum;
4431: $setters->{$course} = {};
4432: $setters->{$course}{'staff'} = [];
4433: $setters->{$course}{'times'} = [];
1.1062 raeburn 4434: $setters->{$course}{'triggers'} = [];
4435: my (@blockers,%triggered);
4436: my $now = time;
4437: my %commblocks = &Apache::lonnet::get_comm_blocks($cdom,$cnum);
4438: if ($activity eq 'docs') {
4439: @blockers = &Apache::lonnet::has_comm_blocking('bre',undef,$url,\%commblocks);
4440: foreach my $block (@blockers) {
4441: if ($block =~ /^firstaccess____(.+)$/) {
4442: my $item = $1;
4443: my $type = 'map';
4444: my $timersymb = $item;
4445: if ($item eq 'course') {
4446: $type = 'course';
4447: } elsif ($item =~ /___\d+___/) {
4448: $type = 'resource';
4449: } else {
4450: $timersymb = &Apache::lonnet::symbread($item);
4451: }
4452: my $start = $env{'course.'.$cdom.'_'.$cnum.'.firstaccess.'.$timersymb};
4453: my $end = $start + $env{'course.'.$cdom.'_'.$cnum.'.timerinterval.'.$timersymb};
4454: $triggered{$block} = {
4455: start => $start,
4456: end => $end,
4457: type => $type,
4458: };
4459: }
4460: }
4461: } else {
4462: foreach my $block (keys(%commblocks)) {
4463: if ($block =~ m/^(\d+)____(\d+)$/) {
4464: my ($start,$end) = ($1,$2);
4465: if ($start <= time && $end >= time) {
4466: if (ref($commblocks{$block}) eq 'HASH') {
4467: if (ref($commblocks{$block}{'blocks'}) eq 'HASH') {
4468: if ($commblocks{$block}{'blocks'}{$activity} eq 'on') {
4469: unless(grep(/^\Q$block\E$/,@blockers)) {
4470: push(@blockers,$block);
4471: }
4472: }
4473: }
4474: }
4475: }
4476: } elsif ($block =~ /^firstaccess____(.+)$/) {
4477: my $item = $1;
4478: my $timersymb = $item;
4479: my $type = 'map';
4480: if ($item eq 'course') {
4481: $type = 'course';
4482: } elsif ($item =~ /___\d+___/) {
4483: $type = 'resource';
4484: } else {
4485: $timersymb = &Apache::lonnet::symbread($item);
4486: }
4487: my $start = $env{'course.'.$cdom.'_'.$cnum.'.firstaccess.'.$timersymb};
4488: my $end = $start + $env{'course.'.$cdom.'_'.$cnum.'.timerinterval.'.$timersymb};
4489: if ($start && $end) {
4490: if (($start <= time) && ($end >= time)) {
4491: unless (grep(/^\Q$block\E$/,@blockers)) {
4492: push(@blockers,$block);
4493: $triggered{$block} = {
4494: start => $start,
4495: end => $end,
4496: type => $type,
4497: };
4498: }
4499: }
1.490 raeburn 4500: }
1.1062 raeburn 4501: }
4502: }
4503: }
4504: foreach my $blocker (@blockers) {
4505: my ($staff_name,$staff_dom,$title,$blocks) =
4506: &parse_block_record($commblocks{$blocker});
4507: push(@{$$setters{$course}{'staff'}},[$staff_name,$staff_dom]);
4508: my ($start,$end,$triggertype);
4509: if ($blocker =~ m/^(\d+)____(\d+)$/) {
4510: ($start,$end) = ($1,$2);
4511: } elsif (ref($triggered{$blocker}) eq 'HASH') {
4512: $start = $triggered{$blocker}{'start'};
4513: $end = $triggered{$blocker}{'end'};
4514: $triggertype = $triggered{$blocker}{'type'};
4515: }
4516: if ($start) {
4517: push(@{$$setters{$course}{'times'}}, [$start,$end]);
4518: if ($triggertype) {
4519: push(@{$$setters{$course}{'triggers'}},$triggertype);
4520: } else {
4521: push(@{$$setters{$course}{'triggers'}},0);
4522: }
4523: if ( ($startblock == 0) || ($startblock > $start) ) {
4524: $startblock = $start;
4525: if ($triggertype) {
4526: $triggerblock = $blocker;
1.474 raeburn 4527: }
4528: }
1.1062 raeburn 4529: if ( ($endblock == 0) || ($endblock < $end) ) {
4530: $endblock = $end;
4531: if ($triggertype) {
4532: $triggerblock = $blocker;
4533: }
4534: }
1.474 raeburn 4535: }
4536: }
1.1062 raeburn 4537: return ($startblock,$endblock,$triggerblock);
1.474 raeburn 4538: }
4539:
4540: sub parse_block_record {
4541: my ($record) = @_;
4542: my ($setuname,$setudom,$title,$blocks);
4543: if (ref($record) eq 'HASH') {
4544: ($setuname,$setudom) = split(/:/,$record->{'setter'});
4545: $title = &unescape($record->{'event'});
4546: $blocks = $record->{'blocks'};
4547: } else {
4548: my @data = split(/:/,$record,3);
4549: if (scalar(@data) eq 2) {
4550: $title = $data[1];
4551: ($setuname,$setudom) = split(/@/,$data[0]);
4552: } else {
4553: ($setuname,$setudom,$title) = @data;
4554: }
4555: $blocks = { 'com' => 'on' };
4556: }
4557: return ($setuname,$setudom,$title,$blocks);
4558: }
4559:
1.854 kalberla 4560: sub blocking_status {
1.1062 raeburn 4561: my ($activity,$uname,$udom,$url) = @_;
1.1061 raeburn 4562: my %setters;
1.890 droeschl 4563:
1.1061 raeburn 4564: # check for active blocking
1.1062 raeburn 4565: my ($startblock,$endblock,$triggerblock) =
4566: &blockcheck(\%setters,$activity,$uname,$udom,$url);
4567: my $blocked = 0;
4568: if ($startblock && $endblock) {
4569: $blocked = 1;
4570: }
1.890 droeschl 4571:
1.1061 raeburn 4572: # caller just wants to know whether a block is active
4573: if (!wantarray) { return $blocked; }
4574:
4575: # build a link to a popup window containing the details
4576: my $querystring = "?activity=$activity";
4577: # $uname and $udom decide whose portfolio the user is trying to look at
1.1062 raeburn 4578: if ($activity eq 'port') {
4579: $querystring .= "&udom=$udom" if $udom;
4580: $querystring .= "&uname=$uname" if $uname;
4581: } elsif ($activity eq 'docs') {
4582: $querystring .= '&url='.&HTML::Entities::encode($url,'&"');
4583: }
1.1061 raeburn 4584:
4585: my $output .= <<'END_MYBLOCK';
4586: function openWindow(url, wdwName, w, h, toolbar,scrollbar) {
4587: var options = "width=" + w + ",height=" + h + ",";
4588: options += "resizable=yes,scrollbars="+scrollbar+",status=no,";
4589: options += "menubar=no,toolbar="+toolbar+",location=no,directories=no";
4590: var newWin = window.open(url, wdwName, options);
4591: newWin.focus();
4592: }
1.890 droeschl 4593: END_MYBLOCK
1.854 kalberla 4594:
1.1061 raeburn 4595: $output = Apache::lonhtmlcommon::scripttag($output);
1.890 droeschl 4596:
1.1061 raeburn 4597: my $popupUrl = "/adm/blockingstatus/$querystring";
1.1062 raeburn 4598: my $text = &mt('Communication Blocked');
4599: if ($activity eq 'docs') {
4600: $text = &mt('Content Access Blocked');
1.1063 raeburn 4601: } elsif ($activity eq 'printout') {
4602: $text = &mt('Printing Blocked');
1.1062 raeburn 4603: }
1.1061 raeburn 4604: $output .= <<"END_BLOCK";
1.867 kalberla 4605: <div class='LC_comblock'>
1.869 kalberla 4606: <a onclick='openWindow("$popupUrl","Blocking Table",600,300,"no","no");return false;' href='/adm/blockingstatus/$querystring'
1.890 droeschl 4607: title='$text'>
4608: <img class='LC_noBorder LC_middle' title='$text' src='/res/adm/pages/comblock.png' alt='$text'/></a>
1.869 kalberla 4609: <a onclick='openWindow("$popupUrl","Blocking Table",600,300,"no","no");return false;' href='/adm/blockingstatus/$querystring'
1.890 droeschl 4610: title='$text'>$text</a>
1.867 kalberla 4611: </div>
4612:
4613: END_BLOCK
1.474 raeburn 4614:
1.1061 raeburn 4615: return ($blocked, $output);
1.854 kalberla 4616: }
1.490 raeburn 4617:
1.60 matthew 4618: ###############################################
4619:
1.682 raeburn 4620: sub check_ip_acc {
4621: my ($acc)=@_;
4622: &Apache::lonxml::debug("acc is $acc");
4623: if (!defined($acc) || $acc =~ /^\s*$/ || $acc =~/^\s*no\s*$/i) {
4624: return 1;
4625: }
4626: my $allowed=0;
4627: my $ip=$env{'request.host'} || $ENV{'REMOTE_ADDR'};
4628:
4629: my $name;
4630: foreach my $pattern (split(',',$acc)) {
4631: $pattern =~ s/^\s*//;
4632: $pattern =~ s/\s*$//;
4633: if ($pattern =~ /\*$/) {
4634: #35.8.*
4635: $pattern=~s/\*//;
4636: if ($ip =~ /^\Q$pattern\E/) { $allowed=1; }
4637: } elsif ($pattern =~ /(\d+\.\d+\.\d+)\.\[(\d+)-(\d+)\]$/) {
4638: #35.8.3.[34-56]
4639: my $low=$2;
4640: my $high=$3;
4641: $pattern=$1;
4642: if ($ip =~ /^\Q$pattern\E/) {
4643: my $last=(split(/\./,$ip))[3];
4644: if ($last <=$high && $last >=$low) { $allowed=1; }
4645: }
4646: } elsif ($pattern =~ /^\*/) {
4647: #*.msu.edu
4648: $pattern=~s/\*//;
4649: if (!defined($name)) {
4650: use Socket;
4651: my $netaddr=inet_aton($ip);
4652: ($name)=gethostbyaddr($netaddr,AF_INET);
4653: }
4654: if ($name =~ /\Q$pattern\E$/i) { $allowed=1; }
4655: } elsif ($pattern =~ /\d+\.\d+\.\d+\.\d+/) {
4656: #127.0.0.1
4657: if ($ip =~ /^\Q$pattern\E/) { $allowed=1; }
4658: } else {
4659: #some.name.com
4660: if (!defined($name)) {
4661: use Socket;
4662: my $netaddr=inet_aton($ip);
4663: ($name)=gethostbyaddr($netaddr,AF_INET);
4664: }
4665: if ($name =~ /\Q$pattern\E$/i) { $allowed=1; }
4666: }
4667: if ($allowed) { last; }
4668: }
4669: return $allowed;
4670: }
4671:
4672: ###############################################
4673:
1.60 matthew 4674: =pod
4675:
1.112 bowersj2 4676: =head1 Domain Template Functions
4677:
4678: =over 4
4679:
4680: =item * &determinedomain()
1.60 matthew 4681:
4682: Inputs: $domain (usually will be undef)
4683:
1.63 www 4684: Returns: Determines which domain should be used for designs
1.60 matthew 4685:
4686: =cut
1.54 www 4687:
1.60 matthew 4688: ###############################################
1.63 www 4689: sub determinedomain {
4690: my $domain=shift;
1.531 albertel 4691: if (! $domain) {
1.60 matthew 4692: # Determine domain if we have not been given one
1.893 raeburn 4693: $domain = &Apache::lonnet::default_login_domain();
1.258 albertel 4694: if ($env{'user.domain'}) { $domain=$env{'user.domain'}; }
4695: if ($env{'request.role.domain'}) {
4696: $domain=$env{'request.role.domain'};
1.60 matthew 4697: }
4698: }
1.63 www 4699: return $domain;
4700: }
4701: ###############################################
1.517 raeburn 4702:
1.518 albertel 4703: sub devalidate_domconfig_cache {
4704: my ($udom)=@_;
4705: &Apache::lonnet::devalidate_cache_new('domainconfig',$udom);
4706: }
4707:
4708: # ---------------------- Get domain configuration for a domain
4709: sub get_domainconf {
4710: my ($udom) = @_;
4711: my $cachetime=1800;
4712: my ($result,$cached)=&Apache::lonnet::is_cached_new('domainconfig',$udom);
4713: if (defined($cached)) { return %{$result}; }
4714:
4715: my %domconfig = &Apache::lonnet::get_dom('configuration',
1.948 raeburn 4716: ['login','rolecolors','autoenroll'],$udom);
1.632 raeburn 4717: my (%designhash,%legacy);
1.518 albertel 4718: if (keys(%domconfig) > 0) {
4719: if (ref($domconfig{'login'}) eq 'HASH') {
1.632 raeburn 4720: if (keys(%{$domconfig{'login'}})) {
4721: foreach my $key (keys(%{$domconfig{'login'}})) {
1.699 raeburn 4722: if (ref($domconfig{'login'}{$key}) eq 'HASH') {
1.946 raeburn 4723: if ($key eq 'loginvia') {
4724: if (ref($domconfig{'login'}{'loginvia'}) eq 'HASH') {
1.1013 raeburn 4725: foreach my $hostname (keys(%{$domconfig{'login'}{'loginvia'}})) {
1.948 raeburn 4726: if (ref($domconfig{'login'}{'loginvia'}{$hostname}) eq 'HASH') {
4727: if ($domconfig{'login'}{'loginvia'}{$hostname}{'server'}) {
4728: my $server = $domconfig{'login'}{'loginvia'}{$hostname}{'server'};
4729: $designhash{$udom.'.login.loginvia'} = $server;
4730: if ($domconfig{'login'}{'loginvia'}{$hostname}{'serverpath'} eq 'custom') {
4731:
4732: $designhash{$udom.'.login.loginvia_'.$hostname} = $server.':'.$domconfig{'login'}{'loginvia'}{$hostname}{'custompath'};
4733: } else {
1.1013 raeburn 4734: $designhash{$udom.'.login.loginvia_'.$hostname} = $server.':'.$domconfig{'login'}{'loginvia'}{$hostname}{'serverpath'};
1.948 raeburn 4735: }
4736: if ($domconfig{'login'}{'loginvia'}{$hostname}{'exempt'}) {
4737: $designhash{$udom.'.login.loginvia_exempt_'.$hostname} = $domconfig{'login'}{'loginvia'}{$hostname}{'exempt'};
4738: }
1.946 raeburn 4739: }
4740: }
4741: }
4742: }
4743: } else {
4744: foreach my $img (keys(%{$domconfig{'login'}{$key}})) {
4745: $designhash{$udom.'.login.'.$key.'_'.$img} =
4746: $domconfig{'login'}{$key}{$img};
4747: }
1.699 raeburn 4748: }
4749: } else {
4750: $designhash{$udom.'.login.'.$key}=$domconfig{'login'}{$key};
4751: }
1.632 raeburn 4752: }
4753: } else {
4754: $legacy{'login'} = 1;
1.518 albertel 4755: }
1.632 raeburn 4756: } else {
4757: $legacy{'login'} = 1;
1.518 albertel 4758: }
4759: if (ref($domconfig{'rolecolors'}) eq 'HASH') {
1.632 raeburn 4760: if (keys(%{$domconfig{'rolecolors'}})) {
4761: foreach my $role (keys(%{$domconfig{'rolecolors'}})) {
4762: if (ref($domconfig{'rolecolors'}{$role}) eq 'HASH') {
4763: foreach my $item (keys(%{$domconfig{'rolecolors'}{$role}})) {
4764: $designhash{$udom.'.'.$role.'.'.$item}=$domconfig{'rolecolors'}{$role}{$item};
4765: }
1.518 albertel 4766: }
4767: }
1.632 raeburn 4768: } else {
4769: $legacy{'rolecolors'} = 1;
1.518 albertel 4770: }
1.632 raeburn 4771: } else {
4772: $legacy{'rolecolors'} = 1;
1.518 albertel 4773: }
1.948 raeburn 4774: if (ref($domconfig{'autoenroll'}) eq 'HASH') {
4775: if ($domconfig{'autoenroll'}{'co-owners'}) {
4776: $designhash{$udom.'.autoassign.co-owners'}=$domconfig{'autoenroll'}{'co-owners'};
4777: }
4778: }
1.632 raeburn 4779: if (keys(%legacy) > 0) {
4780: my %legacyhash = &get_legacy_domconf($udom);
4781: foreach my $item (keys(%legacyhash)) {
4782: if ($item =~ /^\Q$udom\E\.login/) {
4783: if ($legacy{'login'}) {
4784: $designhash{$item} = $legacyhash{$item};
4785: }
4786: } else {
4787: if ($legacy{'rolecolors'}) {
4788: $designhash{$item} = $legacyhash{$item};
4789: }
1.518 albertel 4790: }
4791: }
4792: }
1.632 raeburn 4793: } else {
4794: %designhash = &get_legacy_domconf($udom);
1.518 albertel 4795: }
4796: &Apache::lonnet::do_cache_new('domainconfig',$udom,\%designhash,
4797: $cachetime);
4798: return %designhash;
4799: }
4800:
1.632 raeburn 4801: sub get_legacy_domconf {
4802: my ($udom) = @_;
4803: my %legacyhash;
4804: my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors';
4805: my $designfile = $designdir.'/'.$udom.'.tab';
4806: if (-e $designfile) {
4807: if ( open (my $fh,"<$designfile") ) {
4808: while (my $line = <$fh>) {
4809: next if ($line =~ /^\#/);
4810: chomp($line);
4811: my ($key,$val)=(split(/\=/,$line));
4812: if ($val) { $legacyhash{$udom.'.'.$key}=$val; }
4813: }
4814: close($fh);
4815: }
4816: }
1.1026 raeburn 4817: if (-e $Apache::lonnet::perlvar{'lonDocRoot'}.'/adm/lonDomLogos/'.$udom.'.gif') {
1.632 raeburn 4818: $legacyhash{$udom.'.login.domlogo'} = "/adm/lonDomLogos/$udom.gif";
4819: }
4820: return %legacyhash;
4821: }
4822:
1.63 www 4823: =pod
4824:
1.112 bowersj2 4825: =item * &domainlogo()
1.63 www 4826:
4827: Inputs: $domain (usually will be undef)
4828:
4829: Returns: A link to a domain logo, if the domain logo exists.
4830: If the domain logo does not exist, a description of the domain.
4831:
4832: =cut
1.112 bowersj2 4833:
1.63 www 4834: ###############################################
4835: sub domainlogo {
1.517 raeburn 4836: my $domain = &determinedomain(shift);
1.518 albertel 4837: my %designhash = &get_domainconf($domain);
1.517 raeburn 4838: # See if there is a logo
4839: if ($designhash{$domain.'.login.domlogo'} ne '') {
1.519 raeburn 4840: my $imgsrc = $designhash{$domain.'.login.domlogo'};
1.538 albertel 4841: if ($imgsrc =~ m{^/(adm|res)/}) {
4842: if ($imgsrc =~ m{^/res/}) {
4843: my $local_name = &Apache::lonnet::filelocation('',$imgsrc);
4844: &Apache::lonnet::repcopy($local_name);
4845: }
4846: $imgsrc = &lonhttpdurl($imgsrc);
1.519 raeburn 4847: }
4848: return '<img src="'.$imgsrc.'" alt="'.$domain.'" />';
1.514 albertel 4849: } elsif (defined(&Apache::lonnet::domain($domain,'description'))) {
4850: return &Apache::lonnet::domain($domain,'description');
1.59 www 4851: } else {
1.60 matthew 4852: return '';
1.59 www 4853: }
4854: }
1.63 www 4855: ##############################################
4856:
4857: =pod
4858:
1.112 bowersj2 4859: =item * &designparm()
1.63 www 4860:
4861: Inputs: $which parameter; $domain (usually will be undef)
4862:
4863: Returns: value of designparamter $which
4864:
4865: =cut
1.112 bowersj2 4866:
1.397 albertel 4867:
1.400 albertel 4868: ##############################################
1.397 albertel 4869: sub designparm {
4870: my ($which,$domain)=@_;
4871: if (exists($env{'environment.color.'.$which})) {
1.817 bisitz 4872: return $env{'environment.color.'.$which};
1.96 www 4873: }
1.63 www 4874: $domain=&determinedomain($domain);
1.1016 raeburn 4875: my %domdesign;
4876: unless ($domain eq 'public') {
4877: %domdesign = &get_domainconf($domain);
4878: }
1.520 raeburn 4879: my $output;
1.517 raeburn 4880: if ($domdesign{$domain.'.'.$which} ne '') {
1.817 bisitz 4881: $output = $domdesign{$domain.'.'.$which};
1.63 www 4882: } else {
1.520 raeburn 4883: $output = $defaultdesign{$which};
4884: }
4885: if (($which =~ /^(student|coordinator|author|admin)\.img$/) ||
1.635 raeburn 4886: ($which =~ /login\.(img|logo|domlogo|login)/)) {
1.538 albertel 4887: if ($output =~ m{^/(adm|res)/}) {
1.817 bisitz 4888: if ($output =~ m{^/res/}) {
4889: my $local_name = &Apache::lonnet::filelocation('',$output);
4890: &Apache::lonnet::repcopy($local_name);
4891: }
1.520 raeburn 4892: $output = &lonhttpdurl($output);
4893: }
1.63 www 4894: }
1.520 raeburn 4895: return $output;
1.63 www 4896: }
1.59 www 4897:
1.822 bisitz 4898: ##############################################
4899: =pod
4900:
1.832 bisitz 4901: =item * &authorspace()
4902:
1.1028 raeburn 4903: Inputs: $url (usually will be undef).
1.832 bisitz 4904:
1.1028 raeburn 4905: Returns: Path to Construction Space containing the resource or
4906: directory being viewed (or for which action is being taken).
4907: If $url is provided, and begins /priv/<domain>/<uname>
4908: the path will be that portion of the $context argument.
4909: Otherwise the path will be for the author space of the current
4910: user when the current role is author, or for that of the
4911: co-author/assistant co-author space when the current role
4912: is co-author or assistant co-author.
1.832 bisitz 4913:
4914: =cut
4915:
4916: sub authorspace {
1.1028 raeburn 4917: my ($url) = @_;
4918: if ($url ne '') {
4919: if ($url =~ m{^(/priv/$match_domain/$match_username/)}) {
4920: return $1;
4921: }
4922: }
1.832 bisitz 4923: my $caname = '';
1.1024 www 4924: my $cadom = '';
1.1028 raeburn 4925: if ($env{'request.role'} =~ /^(?:ca|aa)/) {
1.1024 www 4926: ($cadom,$caname) =
1.832 bisitz 4927: ($env{'request.role'}=~/($match_domain)\/($match_username)$/);
1.1028 raeburn 4928: } elsif ($env{'request.role'} =~ m{^au\./($match_domain)/}) {
1.832 bisitz 4929: $caname = $env{'user.name'};
1.1024 www 4930: $cadom = $env{'user.domain'};
1.832 bisitz 4931: }
1.1028 raeburn 4932: if (($caname ne '') && ($cadom ne '')) {
4933: return "/priv/$cadom/$caname/";
4934: }
4935: return;
1.832 bisitz 4936: }
4937:
4938: ##############################################
4939: =pod
4940:
1.822 bisitz 4941: =item * &head_subbox()
4942:
4943: Inputs: $content (contains HTML code with page functions, etc.)
4944:
4945: Returns: HTML div with $content
4946: To be included in page header
4947:
4948: =cut
4949:
4950: sub head_subbox {
4951: my ($content)=@_;
4952: my $output =
1.993 raeburn 4953: '<div class="LC_head_subbox">'
1.822 bisitz 4954: .$content
4955: .'</div>'
4956: }
4957:
4958: ##############################################
4959: =pod
4960:
4961: =item * &CSTR_pageheader()
4962:
1.1026 raeburn 4963: Input: (optional) filename from which breadcrumb trail is built.
4964: In most cases no input as needed, as $env{'request.filename'}
4965: is appropriate for use in building the breadcrumb trail.
1.822 bisitz 4966:
4967: Returns: HTML div with CSTR path and recent box
4968: To be included on Construction Space pages
4969:
4970: =cut
4971:
4972: sub CSTR_pageheader {
1.1026 raeburn 4973: my ($trailfile) = @_;
4974: if ($trailfile eq '') {
4975: $trailfile = $env{'request.filename'};
4976: }
4977:
4978: # this is for resources; directories have customtitle, and crumbs
4979: # and select recent are created in lonpubdir.pm
4980:
4981: my $londocroot = $Apache::lonnet::perlvar{'lonDocRoot'};
1.1022 www 4982: my ($udom,$uname,$thisdisfn)=
1.1026 raeburn 4983: ($trailfile =~ m{^\Q$londocroot\E/priv/([^/]+)/([^/]+)/(.*)$});
4984: my $formaction = "/priv/$udom/$uname/$thisdisfn";
4985: $formaction =~ s{/+}{/}g;
1.822 bisitz 4986:
4987: my $parentpath = '';
4988: my $lastitem = '';
4989: if ($thisdisfn =~ m-(.+/)([^/]*)$-) {
4990: $parentpath = $1;
4991: $lastitem = $2;
4992: } else {
4993: $lastitem = $thisdisfn;
4994: }
1.921 bisitz 4995:
4996: my $output =
1.822 bisitz 4997: '<div>'
4998: .&Apache::loncommon::help_open_menu('','',3,'Authoring') #FIXME: Broken? Where is it?
4999: .'<b>'.&mt('Construction Space:').'</b> '
5000: .'<form name="dirs" method="post" action="'.$formaction
1.921 bisitz 5001: .'" target="_top">' #FIXME lonpubdir: target="_parent"
1.1024 www 5002: .&Apache::lonhtmlcommon::crumbs($uname.'/'.$parentpath,'_top','/priv/'.$udom,undef,undef);
1.921 bisitz 5003:
5004: if ($lastitem) {
5005: $output .=
5006: '<span class="LC_filename">'
5007: .$lastitem
5008: .'</span>';
5009: }
5010: $output .=
5011: '<br />'
1.822 bisitz 5012: #FIXME lonpubdir: &Apache::lonhtmlcommon::crumbs($uname.$thisdisfn.'/','_top','/priv','','+1',1)."</b></tt><br />"
5013: .&Apache::lonhtmlcommon::select_recent('construct','recent','this.form.action=this.form.recent.value;this.form.submit()')
5014: .'</form>'
5015: .&Apache::lonmenu::constspaceform()
5016: .'</div>';
1.921 bisitz 5017:
5018: return $output;
1.822 bisitz 5019: }
5020:
1.60 matthew 5021: ###############################################
5022: ###############################################
5023:
5024: =pod
5025:
1.112 bowersj2 5026: =back
5027:
1.549 albertel 5028: =head1 HTML Helpers
1.112 bowersj2 5029:
5030: =over 4
5031:
5032: =item * &bodytag()
1.60 matthew 5033:
5034: Returns a uniform header for LON-CAPA web pages.
5035:
5036: Inputs:
5037:
1.112 bowersj2 5038: =over 4
5039:
5040: =item * $title, A title to be displayed on the page.
5041:
5042: =item * $function, the current role (can be undef).
5043:
5044: =item * $addentries, extra parameters for the <body> tag.
5045:
5046: =item * $bodyonly, if defined, only return the <body> tag.
5047:
5048: =item * $domain, if defined, force a given domain.
5049:
5050: =item * $forcereg, if page should register as content page (relevant for
1.86 www 5051: text interface only)
1.60 matthew 5052:
1.814 bisitz 5053: =item * $no_nav_bar, if true, keep the 'what is this' info but remove the
5054: navigational links
1.317 albertel 5055:
1.338 albertel 5056: =item * $bgcolor, used to override the bgcolor on a webpage to a specific value
5057:
1.460 albertel 5058: =item * $args, optional argument valid values are
5059: no_auto_mt_title -> prevents &mt()ing the title arg
1.562 albertel 5060: inherit_jsmath -> when creating popup window in a page,
5061: should it have jsmath forced on by the
5062: current page
1.460 albertel 5063:
1.1096 raeburn 5064: =item * $advtoolsref, optional argument, ref to an array containing
5065: inlineremote items to be added in "Functions" menu below
5066: breadcrumbs.
5067:
1.112 bowersj2 5068: =back
5069:
1.60 matthew 5070: Returns: A uniform header for LON-CAPA web pages.
5071: If $bodyonly is nonzero, a string containing a <body> tag will be returned.
5072: If $bodyonly is undef or zero, an html string containing a <body> tag and
5073: other decorations will be returned.
5074:
5075: =cut
5076:
1.54 www 5077: sub bodytag {
1.831 bisitz 5078: my ($title,$function,$addentries,$bodyonly,$domain,$forcereg,
1.1096 raeburn 5079: $no_nav_bar,$bgcolor,$args,$advtoolsref)=@_;
1.339 albertel 5080:
1.954 raeburn 5081: my $public;
5082: if ((($env{'user.name'} eq 'public') && ($env{'user.domain'} eq 'public'))
5083: || ($env{'user.name'} eq '') && ($env{'user.domain'} eq '')) {
5084: $public = 1;
5085: }
1.460 albertel 5086: if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
1.339 albertel 5087:
1.183 matthew 5088: $function = &get_users_function() if (!$function);
1.339 albertel 5089: my $img = &designparm($function.'.img',$domain);
5090: my $font = &designparm($function.'.font',$domain);
5091: my $pgbg = $bgcolor || &designparm($function.'.pgbg',$domain);
5092:
1.803 bisitz 5093: my %design = ( 'style' => 'margin-top: 0',
1.535 albertel 5094: 'bgcolor' => $pgbg,
1.339 albertel 5095: 'text' => $font,
5096: 'alink' => &designparm($function.'.alink',$domain),
5097: 'vlink' => &designparm($function.'.vlink',$domain),
5098: 'link' => &designparm($function.'.link',$domain),);
1.438 albertel 5099: @design{keys(%$addentries)} = @$addentries{keys(%$addentries)};
1.339 albertel 5100:
1.63 www 5101: # role and realm
1.378 raeburn 5102: my ($role,$realm) = split(/\./,$env{'request.role'},2);
5103: if ($role eq 'ca') {
1.479 albertel 5104: my ($rdom,$rname) = ($realm =~ m{^/($match_domain)/($match_username)$});
1.500 albertel 5105: $realm = &plainname($rname,$rdom);
1.378 raeburn 5106: }
1.55 www 5107: # realm
1.258 albertel 5108: if ($env{'request.course.id'}) {
1.378 raeburn 5109: if ($env{'request.role'} !~ /^cr/) {
5110: $role = &Apache::lonnet::plaintext($role,&course_type());
5111: }
1.898 raeburn 5112: if ($env{'request.course.sec'}) {
5113: $role .= (' 'x2).'- '.&mt('section:').' '.$env{'request.course.sec'};
5114: }
1.359 albertel 5115: $realm = $env{'course.'.$env{'request.course.id'}.'.description'};
1.378 raeburn 5116: } else {
5117: $role = &Apache::lonnet::plaintext($role);
1.54 www 5118: }
1.433 albertel 5119:
1.359 albertel 5120: if (!$realm) { $realm=' '; }
1.330 albertel 5121:
1.438 albertel 5122: my $extra_body_attr = &make_attr_string($forcereg,\%design);
1.329 albertel 5123:
1.101 www 5124: # construct main body tag
1.359 albertel 5125: my $bodytag = "<body $extra_body_attr>".
1.562 albertel 5126: &Apache::lontexconvert::init_math_support($args->{'inherit_jsmath'});
1.252 albertel 5127:
1.530 albertel 5128: if ($bodyonly) {
1.60 matthew 5129: return $bodytag;
1.798 tempelho 5130: }
1.359 albertel 5131:
1.410 albertel 5132: my $name = &plainname($env{'user.name'},$env{'user.domain'});
1.954 raeburn 5133: if ($public) {
1.433 albertel 5134: undef($role);
1.434 albertel 5135: } else {
1.1070 raeburn 5136: $name = &aboutmewrapper($name,$env{'user.name'},$env{'user.domain'},
5137: undef,'LC_menubuttons_link');
1.433 albertel 5138: }
1.359 albertel 5139:
1.762 bisitz 5140: my $titleinfo = '<h1>'.$title.'</h1>';
1.359 albertel 5141: #
5142: # Extra info if you are the DC
5143: my $dc_info = '';
5144: if ($env{'user.adv'} && exists($env{'user.role.dc./'.
5145: $env{'course.'.$env{'request.course.id'}.
5146: '.domain'}.'/'})) {
5147: my $cid = $env{'request.course.id'};
1.917 raeburn 5148: $dc_info = $cid.' '.$env{'course.'.$cid.'.internal.coursecode'};
1.380 www 5149: $dc_info =~ s/\s+$//;
1.359 albertel 5150: }
5151:
1.898 raeburn 5152: $role = '<span class="LC_nobreak">('.$role.')</span>' if $role;
1.853 droeschl 5153: &get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['inhibitmenu']);
5154:
1.916 droeschl 5155: if ($no_nav_bar || $env{'form.inhibitmenu'} eq 'yes') {
5156: return $bodytag;
5157: }
1.903 droeschl 5158:
5159: if ($env{'request.state'} eq 'construct') { $forcereg=1; }
5160:
5161: # if ($env{'request.state'} eq 'construct') {
5162: # $titleinfo = &CSTR_pageheader(); #FIXME: Will be removed once all scripts have their own calls
5163: # }
5164:
1.359 albertel 5165:
5166:
1.916 droeschl 5167: if ($env{'request.noversionuri'} =~ m{^/res/adm/pages/}) {
1.917 raeburn 5168: if ($dc_info) {
5169: $dc_info = qq|<span class="LC_cusr_subheading">$dc_info</span>|;
5170: }
1.916 droeschl 5171: $bodytag .= qq|<div id="LC_nav_bar">$name $role<br />
5172: <em>$realm</em> $dc_info</div>|;
1.903 droeschl 5173: return $bodytag;
5174: }
1.894 droeschl 5175:
1.927 raeburn 5176: unless ($env{'request.symb'} =~ m/\.page___\d+___/) {
5177: $bodytag .= qq|<div id="LC_nav_bar">$name $role</div>|;
5178: }
1.916 droeschl 5179:
1.903 droeschl 5180: $bodytag .= Apache::lonhtmlcommon::scripttag(
5181: Apache::lonmenu::utilityfunctions(), 'start');
1.816 bisitz 5182:
1.903 droeschl 5183: $bodytag .= Apache::lonmenu::primary_menu();
1.852 droeschl 5184:
1.917 raeburn 5185: if ($dc_info) {
5186: $dc_info = &dc_courseid_toggle($dc_info);
5187: }
5188: $bodytag .= qq|<div id="LC_realm">$realm $dc_info</div>|;
1.916 droeschl 5189:
1.903 droeschl 5190: #don't show menus for public users
1.954 raeburn 5191: if (!$public){
1.903 droeschl 5192: $bodytag .= Apache::lonmenu::secondary_menu();
5193: $bodytag .= Apache::lonmenu::serverform();
1.920 raeburn 5194: $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end');
5195: if ($env{'request.state'} eq 'construct') {
1.962 droeschl 5196: $bodytag .= &Apache::lonmenu::innerregister($forcereg,
1.920 raeburn 5197: $args->{'bread_crumbs'});
1.1096 raeburn 5198: } elsif ($forcereg) {
5199: $bodytag .= &Apache::lonmenu::innerregister($forcereg,undef,
5200: $args->{'group'});
5201: } else {
5202: $bodytag .=
5203: &Apache::lonmenu::prepare_functions($env{'request.noversionuri'},
5204: $forcereg,$args->{'group'},
5205: $args->{'bread_crumbs'},
5206: $advtoolsref);
1.920 raeburn 5207: }
1.903 droeschl 5208: }else{
5209: # this is to seperate menu from content when there's no secondary
5210: # menu. Especially needed for public accessible ressources.
5211: $bodytag .= '<hr style="clear:both" />';
5212: $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end');
1.235 raeburn 5213: }
1.903 droeschl 5214:
1.235 raeburn 5215: return $bodytag;
1.182 matthew 5216: }
5217:
1.917 raeburn 5218: sub dc_courseid_toggle {
5219: my ($dc_info) = @_;
1.980 raeburn 5220: return ' <span id="dccidtext" class="LC_cusr_subheading LC_nobreak">'.
1.1069 raeburn 5221: '<a href="javascript:showCourseID();" class="LC_menubuttons_link">'.
1.917 raeburn 5222: &mt('(More ...)').'</a></span>'.
5223: '<div id="dccid" class="LC_dccid">'.$dc_info.'</div>';
5224: }
5225:
1.330 albertel 5226: sub make_attr_string {
5227: my ($register,$attr_ref) = @_;
5228:
5229: if ($attr_ref && !ref($attr_ref)) {
5230: die("addentries Must be a hash ref ".
5231: join(':',caller(1))." ".
5232: join(':',caller(0))." ");
5233: }
5234:
5235: if ($register) {
1.339 albertel 5236: my ($on_load,$on_unload);
5237: foreach my $key (keys(%{$attr_ref})) {
5238: if (lc($key) eq 'onload') {
5239: $on_load.=$attr_ref->{$key}.';';
5240: delete($attr_ref->{$key});
5241:
5242: } elsif (lc($key) eq 'onunload') {
5243: $on_unload.=$attr_ref->{$key}.';';
5244: delete($attr_ref->{$key});
5245: }
5246: }
1.953 droeschl 5247: $attr_ref->{'onload'} = $on_load;
5248: $attr_ref->{'onunload'}= $on_unload;
1.330 albertel 5249: }
1.339 albertel 5250:
1.330 albertel 5251: my $attr_string;
5252: foreach my $attr (keys(%$attr_ref)) {
5253: $attr_string .= " $attr=\"".$attr_ref->{$attr}.'" ';
5254: }
5255: return $attr_string;
5256: }
5257:
5258:
1.182 matthew 5259: ###############################################
1.251 albertel 5260: ###############################################
5261:
5262: =pod
5263:
5264: =item * &endbodytag()
5265:
5266: Returns a uniform footer for LON-CAPA web pages.
5267:
1.635 raeburn 5268: Inputs: 1 - optional reference to an args hash
5269: If in the hash, key for noredirectlink has a value which evaluates to true,
5270: a 'Continue' link is not displayed if the page contains an
5271: internal redirect in the <head></head> section,
5272: i.e., $env{'internal.head.redirect'} exists
1.251 albertel 5273:
5274: =cut
5275:
5276: sub endbodytag {
1.635 raeburn 5277: my ($args) = @_;
1.1080 raeburn 5278: my $endbodytag;
5279: unless ((ref($args) eq 'HASH') && ($args->{'notbody'})) {
5280: $endbodytag='</body>';
5281: }
1.269 albertel 5282: $endbodytag=&Apache::lontexconvert::jsMath_process()."\n".$endbodytag;
1.315 albertel 5283: if ( exists( $env{'internal.head.redirect'} ) ) {
1.635 raeburn 5284: if (!(ref($args) eq 'HASH' && $args->{'noredirectlink'})) {
5285: $endbodytag=
5286: "<br /><a href=\"$env{'internal.head.redirect'}\">".
5287: &mt('Continue').'</a>'.
5288: $endbodytag;
5289: }
1.315 albertel 5290: }
1.251 albertel 5291: return $endbodytag;
5292: }
5293:
1.352 albertel 5294: =pod
5295:
5296: =item * &standard_css()
5297:
5298: Returns a style sheet
5299:
5300: Inputs: (all optional)
5301: domain -> force to color decorate a page for a specific
5302: domain
5303: function -> force usage of a specific rolish color scheme
5304: bgcolor -> override the default page bgcolor
5305:
5306: =cut
5307:
1.343 albertel 5308: sub standard_css {
1.345 albertel 5309: my ($function,$domain,$bgcolor) = @_;
1.352 albertel 5310: $function = &get_users_function() if (!$function);
5311: my $img = &designparm($function.'.img', $domain);
5312: my $tabbg = &designparm($function.'.tabbg', $domain);
5313: my $font = &designparm($function.'.font', $domain);
1.801 tempelho 5314: my $fontmenu = &designparm($function.'.fontmenu', $domain);
1.791 tempelho 5315: #second colour for later usage
1.345 albertel 5316: my $sidebg = &designparm($function.'.sidebg',$domain);
1.382 albertel 5317: my $pgbg_or_bgcolor =
5318: $bgcolor ||
1.352 albertel 5319: &designparm($function.'.pgbg', $domain);
1.382 albertel 5320: my $pgbg = &designparm($function.'.pgbg', $domain);
1.352 albertel 5321: my $alink = &designparm($function.'.alink', $domain);
5322: my $vlink = &designparm($function.'.vlink', $domain);
5323: my $link = &designparm($function.'.link', $domain);
5324:
1.602 albertel 5325: my $sans = 'Verdana,Arial,Helvetica,sans-serif';
1.395 albertel 5326: my $mono = 'monospace';
1.850 bisitz 5327: my $data_table_head = $sidebg;
5328: my $data_table_light = '#FAFAFA';
1.1060 bisitz 5329: my $data_table_dark = '#E0E0E0';
1.470 banghart 5330: my $data_table_darker = '#CCCCCC';
1.349 albertel 5331: my $data_table_highlight = '#FFFF00';
1.352 albertel 5332: my $mail_new = '#FFBB77';
5333: my $mail_new_hover = '#DD9955';
5334: my $mail_read = '#BBBB77';
5335: my $mail_read_hover = '#999944';
5336: my $mail_replied = '#AAAA88';
5337: my $mail_replied_hover = '#888855';
5338: my $mail_other = '#99BBBB';
5339: my $mail_other_hover = '#669999';
1.391 albertel 5340: my $table_header = '#DDDDDD';
1.489 raeburn 5341: my $feedback_link_bg = '#BBBBBB';
1.911 bisitz 5342: my $lg_border_color = '#C8C8C8';
1.952 onken 5343: my $button_hover = '#BF2317';
1.392 albertel 5344:
1.608 albertel 5345: my $border = ($env{'browser.type'} eq 'explorer' ||
1.911 bisitz 5346: $env{'browser.type'} eq 'safari' ) ? '0 2px 0 2px'
5347: : '0 3px 0 4px';
1.448 albertel 5348:
1.523 albertel 5349:
1.343 albertel 5350: return <<END;
1.947 droeschl 5351:
5352: /* needed for iframe to allow 100% height in FF */
5353: body, html {
5354: margin: 0;
5355: padding: 0 0.5%;
5356: height: 99%; /* to avoid scrollbars */
5357: }
5358:
1.795 www 5359: body {
1.911 bisitz 5360: font-family: $sans;
5361: line-height:130%;
5362: font-size:0.83em;
5363: color:$font;
1.795 www 5364: }
5365:
1.959 onken 5366: a:focus,
5367: a:focus img {
1.795 www 5368: color: red;
5369: }
1.698 harmsja 5370:
1.911 bisitz 5371: form, .inline {
5372: display: inline;
1.795 www 5373: }
1.721 harmsja 5374:
1.795 www 5375: .LC_right {
1.911 bisitz 5376: text-align:right;
1.795 www 5377: }
5378:
5379: .LC_middle {
1.911 bisitz 5380: vertical-align:middle;
1.795 www 5381: }
1.721 harmsja 5382:
1.911 bisitz 5383: .LC_400Box {
5384: width:400px;
5385: }
1.721 harmsja 5386:
1.947 droeschl 5387: .LC_iframecontainer {
5388: width: 98%;
5389: margin: 0;
5390: position: fixed;
5391: top: 8.5em;
5392: bottom: 0;
5393: }
5394:
5395: .LC_iframecontainer iframe{
5396: border: none;
5397: width: 100%;
5398: height: 100%;
5399: }
5400:
1.778 bisitz 5401: .LC_filename {
5402: font-family: $mono;
5403: white-space:pre;
1.921 bisitz 5404: font-size: 120%;
1.778 bisitz 5405: }
5406:
5407: .LC_fileicon {
5408: border: none;
5409: height: 1.3em;
5410: vertical-align: text-bottom;
5411: margin-right: 0.3em;
5412: text-decoration:none;
5413: }
5414:
1.1008 www 5415: .LC_setting {
5416: text-decoration:underline;
5417: }
5418:
1.350 albertel 5419: .LC_error {
5420: color: red;
5421: }
1.795 www 5422:
1.1097 bisitz 5423: .LC_warning {
5424: color: darkorange;
5425: }
5426:
1.457 albertel 5427: .LC_diff_removed {
1.733 bisitz 5428: color: red;
1.394 albertel 5429: }
1.532 albertel 5430:
5431: .LC_info,
1.457 albertel 5432: .LC_success,
5433: .LC_diff_added {
1.350 albertel 5434: color: green;
5435: }
1.795 www 5436:
1.802 bisitz 5437: div.LC_confirm_box {
5438: background-color: #FAFAFA;
5439: border: 1px solid $lg_border_color;
5440: margin-right: 0;
5441: padding: 5px;
5442: }
5443:
5444: div.LC_confirm_box .LC_error img,
5445: div.LC_confirm_box .LC_success img {
5446: vertical-align: middle;
5447: }
5448:
1.440 albertel 5449: .LC_icon {
1.771 droeschl 5450: border: none;
1.790 droeschl 5451: vertical-align: middle;
1.771 droeschl 5452: }
5453:
1.543 albertel 5454: .LC_docs_spacer {
5455: width: 25px;
5456: height: 1px;
1.771 droeschl 5457: border: none;
1.543 albertel 5458: }
1.346 albertel 5459:
1.532 albertel 5460: .LC_internal_info {
1.735 bisitz 5461: color: #999999;
1.532 albertel 5462: }
5463:
1.794 www 5464: .LC_discussion {
1.1050 www 5465: background: $data_table_dark;
1.911 bisitz 5466: border: 1px solid black;
5467: margin: 2px;
1.794 www 5468: }
5469:
5470: .LC_disc_action_left {
1.1050 www 5471: background: $sidebg;
1.911 bisitz 5472: text-align: left;
1.1050 www 5473: padding: 4px;
5474: margin: 2px;
1.794 www 5475: }
5476:
5477: .LC_disc_action_right {
1.1050 www 5478: background: $sidebg;
1.911 bisitz 5479: text-align: right;
1.1050 www 5480: padding: 4px;
5481: margin: 2px;
1.794 www 5482: }
5483:
5484: .LC_disc_new_item {
1.911 bisitz 5485: background: white;
5486: border: 2px solid red;
1.1050 www 5487: margin: 4px;
5488: padding: 4px;
1.794 www 5489: }
5490:
5491: .LC_disc_old_item {
1.911 bisitz 5492: background: white;
1.1050 www 5493: margin: 4px;
5494: padding: 4px;
1.794 www 5495: }
5496:
1.458 albertel 5497: table.LC_pastsubmission {
5498: border: 1px solid black;
5499: margin: 2px;
5500: }
5501:
1.924 bisitz 5502: table#LC_menubuttons {
1.345 albertel 5503: width: 100%;
5504: background: $pgbg;
1.392 albertel 5505: border: 2px;
1.402 albertel 5506: border-collapse: separate;
1.803 bisitz 5507: padding: 0;
1.345 albertel 5508: }
1.392 albertel 5509:
1.801 tempelho 5510: table#LC_title_bar a {
5511: color: $fontmenu;
5512: }
1.836 bisitz 5513:
1.807 droeschl 5514: table#LC_title_bar {
1.819 tempelho 5515: clear: both;
1.836 bisitz 5516: display: none;
1.807 droeschl 5517: }
5518:
1.795 www 5519: table#LC_title_bar,
1.933 droeschl 5520: table.LC_breadcrumbs, /* obsolete? */
1.393 albertel 5521: table#LC_title_bar.LC_with_remote {
1.359 albertel 5522: width: 100%;
1.392 albertel 5523: border-color: $pgbg;
5524: border-style: solid;
5525: border-width: $border;
1.379 albertel 5526: background: $pgbg;
1.801 tempelho 5527: color: $fontmenu;
1.392 albertel 5528: border-collapse: collapse;
1.803 bisitz 5529: padding: 0;
1.819 tempelho 5530: margin: 0;
1.359 albertel 5531: }
1.795 www 5532:
1.933 droeschl 5533: ul.LC_breadcrumb_tools_outerlist {
1.913 droeschl 5534: margin: 0;
5535: padding: 0;
1.933 droeschl 5536: position: relative;
5537: list-style: none;
1.913 droeschl 5538: }
1.933 droeschl 5539: ul.LC_breadcrumb_tools_outerlist li {
1.913 droeschl 5540: display: inline;
5541: }
1.933 droeschl 5542:
5543: .LC_breadcrumb_tools_navigation {
1.913 droeschl 5544: padding: 0;
1.933 droeschl 5545: margin: 0;
5546: float: left;
1.913 droeschl 5547: }
1.933 droeschl 5548: .LC_breadcrumb_tools_tools {
5549: padding: 0;
5550: margin: 0;
1.913 droeschl 5551: float: right;
5552: }
5553:
1.359 albertel 5554: table#LC_title_bar td {
5555: background: $tabbg;
5556: }
1.795 www 5557:
1.911 bisitz 5558: table#LC_menubuttons img {
1.803 bisitz 5559: border: none;
1.346 albertel 5560: }
1.795 www 5561:
1.842 droeschl 5562: .LC_breadcrumbs_component {
1.911 bisitz 5563: float: right;
5564: margin: 0 1em;
1.357 albertel 5565: }
1.842 droeschl 5566: .LC_breadcrumbs_component img {
1.911 bisitz 5567: vertical-align: middle;
1.777 tempelho 5568: }
1.795 www 5569:
1.383 albertel 5570: td.LC_table_cell_checkbox {
5571: text-align: center;
5572: }
1.795 www 5573:
5574: .LC_fontsize_small {
1.911 bisitz 5575: font-size: 70%;
1.705 tempelho 5576: }
5577:
1.844 bisitz 5578: #LC_breadcrumbs {
1.911 bisitz 5579: clear:both;
5580: background: $sidebg;
5581: border-bottom: 1px solid $lg_border_color;
5582: line-height: 2.5em;
1.933 droeschl 5583: overflow: hidden;
1.911 bisitz 5584: margin: 0;
5585: padding: 0;
1.995 raeburn 5586: text-align: left;
1.819 tempelho 5587: }
1.862 bisitz 5588:
1.1098 bisitz 5589: .LC_head_subbox, .LC_actionbox {
1.911 bisitz 5590: clear:both;
5591: background: #F8F8F8; /* $sidebg; */
1.915 droeschl 5592: border: 1px solid $sidebg;
1.1098 bisitz 5593: margin: 0 0 10px 0;
1.966 bisitz 5594: padding: 3px;
1.995 raeburn 5595: text-align: left;
1.822 bisitz 5596: }
5597:
1.795 www 5598: .LC_fontsize_medium {
1.911 bisitz 5599: font-size: 85%;
1.705 tempelho 5600: }
5601:
1.795 www 5602: .LC_fontsize_large {
1.911 bisitz 5603: font-size: 120%;
1.705 tempelho 5604: }
5605:
1.346 albertel 5606: .LC_menubuttons_inline_text {
5607: color: $font;
1.698 harmsja 5608: font-size: 90%;
1.701 harmsja 5609: padding-left:3px;
1.346 albertel 5610: }
5611:
1.934 droeschl 5612: .LC_menubuttons_inline_text img{
5613: vertical-align: middle;
5614: }
5615:
1.1051 www 5616: li.LC_menubuttons_inline_text img {
1.951 onken 5617: cursor:pointer;
1.1002 droeschl 5618: text-decoration: none;
1.951 onken 5619: }
5620:
1.526 www 5621: .LC_menubuttons_link {
5622: text-decoration: none;
5623: }
1.795 www 5624:
1.522 albertel 5625: .LC_menubuttons_category {
1.521 www 5626: color: $font;
1.526 www 5627: background: $pgbg;
1.521 www 5628: font-size: larger;
5629: font-weight: bold;
5630: }
5631:
1.346 albertel 5632: td.LC_menubuttons_text {
1.911 bisitz 5633: color: $font;
1.346 albertel 5634: }
1.706 harmsja 5635:
1.346 albertel 5636: .LC_current_location {
5637: background: $tabbg;
5638: }
1.795 www 5639:
1.938 bisitz 5640: table.LC_data_table {
1.347 albertel 5641: border: 1px solid #000000;
1.402 albertel 5642: border-collapse: separate;
1.426 albertel 5643: border-spacing: 1px;
1.610 albertel 5644: background: $pgbg;
1.347 albertel 5645: }
1.795 www 5646:
1.422 albertel 5647: .LC_data_table_dense {
5648: font-size: small;
5649: }
1.795 www 5650:
1.507 raeburn 5651: table.LC_nested_outer {
5652: border: 1px solid #000000;
1.589 raeburn 5653: border-collapse: collapse;
1.803 bisitz 5654: border-spacing: 0;
1.507 raeburn 5655: width: 100%;
5656: }
1.795 www 5657:
1.879 raeburn 5658: table.LC_innerpickbox,
1.507 raeburn 5659: table.LC_nested {
1.803 bisitz 5660: border: none;
1.589 raeburn 5661: border-collapse: collapse;
1.803 bisitz 5662: border-spacing: 0;
1.507 raeburn 5663: width: 100%;
5664: }
1.795 www 5665:
1.911 bisitz 5666: table.LC_data_table tr th,
5667: table.LC_calendar tr th,
1.879 raeburn 5668: table.LC_prior_tries tr th,
5669: table.LC_innerpickbox tr th {
1.349 albertel 5670: font-weight: bold;
5671: background-color: $data_table_head;
1.801 tempelho 5672: color:$fontmenu;
1.701 harmsja 5673: font-size:90%;
1.347 albertel 5674: }
1.795 www 5675:
1.879 raeburn 5676: table.LC_innerpickbox tr th,
5677: table.LC_innerpickbox tr td {
5678: vertical-align: top;
5679: }
5680:
1.711 raeburn 5681: table.LC_data_table tr.LC_info_row > td {
1.735 bisitz 5682: background-color: #CCCCCC;
1.711 raeburn 5683: font-weight: bold;
5684: text-align: left;
5685: }
1.795 www 5686:
1.912 bisitz 5687: table.LC_data_table tr.LC_odd_row > td {
5688: background-color: $data_table_light;
5689: padding: 2px;
5690: vertical-align: top;
5691: }
5692:
1.809 bisitz 5693: table.LC_pick_box tr > td.LC_odd_row {
1.349 albertel 5694: background-color: $data_table_light;
1.912 bisitz 5695: vertical-align: top;
5696: }
5697:
5698: table.LC_data_table tr.LC_even_row > td {
5699: background-color: $data_table_dark;
1.425 albertel 5700: padding: 2px;
1.900 bisitz 5701: vertical-align: top;
1.347 albertel 5702: }
1.795 www 5703:
1.809 bisitz 5704: table.LC_pick_box tr > td.LC_even_row {
1.349 albertel 5705: background-color: $data_table_dark;
1.900 bisitz 5706: vertical-align: top;
1.347 albertel 5707: }
1.795 www 5708:
1.425 albertel 5709: table.LC_data_table tr.LC_data_table_highlight td {
5710: background-color: $data_table_darker;
5711: }
1.795 www 5712:
1.639 raeburn 5713: table.LC_data_table tr td.LC_leftcol_header {
5714: background-color: $data_table_head;
5715: font-weight: bold;
5716: }
1.795 www 5717:
1.451 albertel 5718: table.LC_data_table tr.LC_empty_row td,
1.507 raeburn 5719: table.LC_nested tr.LC_empty_row td {
1.421 albertel 5720: font-weight: bold;
5721: font-style: italic;
5722: text-align: center;
5723: padding: 8px;
1.347 albertel 5724: }
1.795 www 5725:
1.940 bisitz 5726: table.LC_data_table tr.LC_empty_row td {
5727: background-color: $sidebg;
5728: }
5729:
5730: table.LC_nested tr.LC_empty_row td {
5731: background-color: #FFFFFF;
5732: }
5733:
1.890 droeschl 5734: table.LC_caption {
5735: }
5736:
1.507 raeburn 5737: table.LC_nested tr.LC_empty_row td {
1.465 albertel 5738: padding: 4ex
5739: }
1.795 www 5740:
1.507 raeburn 5741: table.LC_nested_outer tr th {
5742: font-weight: bold;
1.801 tempelho 5743: color:$fontmenu;
1.507 raeburn 5744: background-color: $data_table_head;
1.701 harmsja 5745: font-size: small;
1.507 raeburn 5746: border-bottom: 1px solid #000000;
5747: }
1.795 www 5748:
1.507 raeburn 5749: table.LC_nested_outer tr td.LC_subheader {
5750: background-color: $data_table_head;
5751: font-weight: bold;
5752: font-size: small;
5753: border-bottom: 1px solid #000000;
5754: text-align: right;
1.451 albertel 5755: }
1.795 www 5756:
1.507 raeburn 5757: table.LC_nested tr.LC_info_row td {
1.735 bisitz 5758: background-color: #CCCCCC;
1.451 albertel 5759: font-weight: bold;
5760: font-size: small;
1.507 raeburn 5761: text-align: center;
5762: }
1.795 www 5763:
1.589 raeburn 5764: table.LC_nested tr.LC_info_row td.LC_left_item,
5765: table.LC_nested_outer tr th.LC_left_item {
1.507 raeburn 5766: text-align: left;
1.451 albertel 5767: }
1.795 www 5768:
1.507 raeburn 5769: table.LC_nested td {
1.735 bisitz 5770: background-color: #FFFFFF;
1.451 albertel 5771: font-size: small;
1.507 raeburn 5772: }
1.795 www 5773:
1.507 raeburn 5774: table.LC_nested_outer tr th.LC_right_item,
5775: table.LC_nested tr.LC_info_row td.LC_right_item,
5776: table.LC_nested tr.LC_odd_row td.LC_right_item,
5777: table.LC_nested tr td.LC_right_item {
1.451 albertel 5778: text-align: right;
5779: }
5780:
1.507 raeburn 5781: table.LC_nested tr.LC_odd_row td {
1.735 bisitz 5782: background-color: #EEEEEE;
1.451 albertel 5783: }
5784:
1.473 raeburn 5785: table.LC_createuser {
5786: }
5787:
5788: table.LC_createuser tr.LC_section_row td {
1.701 harmsja 5789: font-size: small;
1.473 raeburn 5790: }
5791:
5792: table.LC_createuser tr.LC_info_row td {
1.735 bisitz 5793: background-color: #CCCCCC;
1.473 raeburn 5794: font-weight: bold;
5795: text-align: center;
5796: }
5797:
1.349 albertel 5798: table.LC_calendar {
5799: border: 1px solid #000000;
5800: border-collapse: collapse;
1.917 raeburn 5801: width: 98%;
1.349 albertel 5802: }
1.795 www 5803:
1.349 albertel 5804: table.LC_calendar_pickdate {
5805: font-size: xx-small;
5806: }
1.795 www 5807:
1.349 albertel 5808: table.LC_calendar tr td {
5809: border: 1px solid #000000;
5810: vertical-align: top;
1.917 raeburn 5811: width: 14%;
1.349 albertel 5812: }
1.795 www 5813:
1.349 albertel 5814: table.LC_calendar tr td.LC_calendar_day_empty {
5815: background-color: $data_table_dark;
5816: }
1.795 www 5817:
1.779 bisitz 5818: table.LC_calendar tr td.LC_calendar_day_current {
5819: background-color: $data_table_highlight;
1.777 tempelho 5820: }
1.795 www 5821:
1.938 bisitz 5822: table.LC_data_table tr td.LC_mail_new {
1.349 albertel 5823: background-color: $mail_new;
5824: }
1.795 www 5825:
1.938 bisitz 5826: table.LC_data_table tr.LC_mail_new:hover {
1.349 albertel 5827: background-color: $mail_new_hover;
5828: }
1.795 www 5829:
1.938 bisitz 5830: table.LC_data_table tr td.LC_mail_read {
1.349 albertel 5831: background-color: $mail_read;
5832: }
1.795 www 5833:
1.938 bisitz 5834: /*
5835: table.LC_data_table tr.LC_mail_read:hover {
1.349 albertel 5836: background-color: $mail_read_hover;
5837: }
1.938 bisitz 5838: */
1.795 www 5839:
1.938 bisitz 5840: table.LC_data_table tr td.LC_mail_replied {
1.349 albertel 5841: background-color: $mail_replied;
5842: }
1.795 www 5843:
1.938 bisitz 5844: /*
5845: table.LC_data_table tr.LC_mail_replied:hover {
1.349 albertel 5846: background-color: $mail_replied_hover;
5847: }
1.938 bisitz 5848: */
1.795 www 5849:
1.938 bisitz 5850: table.LC_data_table tr td.LC_mail_other {
1.349 albertel 5851: background-color: $mail_other;
5852: }
1.795 www 5853:
1.938 bisitz 5854: /*
5855: table.LC_data_table tr.LC_mail_other:hover {
1.349 albertel 5856: background-color: $mail_other_hover;
5857: }
1.938 bisitz 5858: */
1.494 raeburn 5859:
1.777 tempelho 5860: table.LC_data_table tr > td.LC_browser_file,
5861: table.LC_data_table tr > td.LC_browser_file_published {
1.899 bisitz 5862: background: #AAEE77;
1.389 albertel 5863: }
1.795 www 5864:
1.777 tempelho 5865: table.LC_data_table tr > td.LC_browser_file_locked,
5866: table.LC_data_table tr > td.LC_browser_file_unpublished {
1.389 albertel 5867: background: #FFAA99;
1.387 albertel 5868: }
1.795 www 5869:
1.777 tempelho 5870: table.LC_data_table tr > td.LC_browser_file_obsolete {
1.899 bisitz 5871: background: #888888;
1.779 bisitz 5872: }
1.795 www 5873:
1.777 tempelho 5874: table.LC_data_table tr > td.LC_browser_file_modified,
1.779 bisitz 5875: table.LC_data_table tr > td.LC_browser_file_metamodified {
1.899 bisitz 5876: background: #F8F866;
1.777 tempelho 5877: }
1.795 www 5878:
1.696 bisitz 5879: table.LC_data_table tr.LC_browser_folder > td {
1.899 bisitz 5880: background: #E0E8FF;
1.387 albertel 5881: }
1.696 bisitz 5882:
1.707 bisitz 5883: table.LC_data_table tr > td.LC_roles_is {
1.911 bisitz 5884: /* background: #77FF77; */
1.707 bisitz 5885: }
1.795 www 5886:
1.707 bisitz 5887: table.LC_data_table tr > td.LC_roles_future {
1.939 bisitz 5888: border-right: 8px solid #FFFF77;
1.707 bisitz 5889: }
1.795 www 5890:
1.707 bisitz 5891: table.LC_data_table tr > td.LC_roles_will {
1.939 bisitz 5892: border-right: 8px solid #FFAA77;
1.707 bisitz 5893: }
1.795 www 5894:
1.707 bisitz 5895: table.LC_data_table tr > td.LC_roles_expired {
1.939 bisitz 5896: border-right: 8px solid #FF7777;
1.707 bisitz 5897: }
1.795 www 5898:
1.707 bisitz 5899: table.LC_data_table tr > td.LC_roles_will_not {
1.939 bisitz 5900: border-right: 8px solid #AAFF77;
1.707 bisitz 5901: }
1.795 www 5902:
1.707 bisitz 5903: table.LC_data_table tr > td.LC_roles_selected {
1.939 bisitz 5904: border-right: 8px solid #11CC55;
1.707 bisitz 5905: }
5906:
1.388 albertel 5907: span.LC_current_location {
1.701 harmsja 5908: font-size:larger;
1.388 albertel 5909: background: $pgbg;
5910: }
1.387 albertel 5911:
1.1029 www 5912: span.LC_current_nav_location {
5913: font-weight:bold;
5914: background: $sidebg;
5915: }
5916:
1.395 albertel 5917: span.LC_parm_menu_item {
5918: font-size: larger;
5919: }
1.795 www 5920:
1.395 albertel 5921: span.LC_parm_scope_all {
5922: color: red;
5923: }
1.795 www 5924:
1.395 albertel 5925: span.LC_parm_scope_folder {
5926: color: green;
5927: }
1.795 www 5928:
1.395 albertel 5929: span.LC_parm_scope_resource {
5930: color: orange;
5931: }
1.795 www 5932:
1.395 albertel 5933: span.LC_parm_part {
5934: color: blue;
5935: }
1.795 www 5936:
1.911 bisitz 5937: span.LC_parm_folder,
5938: span.LC_parm_symb {
1.395 albertel 5939: font-size: x-small;
5940: font-family: $mono;
5941: color: #AAAAAA;
5942: }
5943:
1.977 bisitz 5944: ul.LC_parm_parmlist li {
5945: display: inline-block;
5946: padding: 0.3em 0.8em;
5947: vertical-align: top;
5948: width: 150px;
5949: border-top:1px solid $lg_border_color;
5950: }
5951:
1.795 www 5952: td.LC_parm_overview_level_menu,
5953: td.LC_parm_overview_map_menu,
5954: td.LC_parm_overview_parm_selectors,
5955: td.LC_parm_overview_restrictions {
1.396 albertel 5956: border: 1px solid black;
5957: border-collapse: collapse;
5958: }
1.795 www 5959:
1.396 albertel 5960: table.LC_parm_overview_restrictions td {
5961: border-width: 1px 4px 1px 4px;
5962: border-style: solid;
5963: border-color: $pgbg;
5964: text-align: center;
5965: }
1.795 www 5966:
1.396 albertel 5967: table.LC_parm_overview_restrictions th {
5968: background: $tabbg;
5969: border-width: 1px 4px 1px 4px;
5970: border-style: solid;
5971: border-color: $pgbg;
5972: }
1.795 www 5973:
1.398 albertel 5974: table#LC_helpmenu {
1.803 bisitz 5975: border: none;
1.398 albertel 5976: height: 55px;
1.803 bisitz 5977: border-spacing: 0;
1.398 albertel 5978: }
5979:
5980: table#LC_helpmenu fieldset legend {
5981: font-size: larger;
5982: }
1.795 www 5983:
1.397 albertel 5984: table#LC_helpmenu_links {
5985: width: 100%;
5986: border: 1px solid black;
5987: background: $pgbg;
1.803 bisitz 5988: padding: 0;
1.397 albertel 5989: border-spacing: 1px;
5990: }
1.795 www 5991:
1.397 albertel 5992: table#LC_helpmenu_links tr td {
5993: padding: 1px;
5994: background: $tabbg;
1.399 albertel 5995: text-align: center;
5996: font-weight: bold;
1.397 albertel 5997: }
1.396 albertel 5998:
1.795 www 5999: table#LC_helpmenu_links a:link,
6000: table#LC_helpmenu_links a:visited,
1.397 albertel 6001: table#LC_helpmenu_links a:active {
6002: text-decoration: none;
6003: color: $font;
6004: }
1.795 www 6005:
1.397 albertel 6006: table#LC_helpmenu_links a:hover {
6007: text-decoration: underline;
6008: color: $vlink;
6009: }
1.396 albertel 6010:
1.417 albertel 6011: .LC_chrt_popup_exists {
6012: border: 1px solid #339933;
6013: margin: -1px;
6014: }
1.795 www 6015:
1.417 albertel 6016: .LC_chrt_popup_up {
6017: border: 1px solid yellow;
6018: margin: -1px;
6019: }
1.795 www 6020:
1.417 albertel 6021: .LC_chrt_popup {
6022: border: 1px solid #8888FF;
6023: background: #CCCCFF;
6024: }
1.795 www 6025:
1.421 albertel 6026: table.LC_pick_box {
6027: border-collapse: separate;
6028: background: white;
6029: border: 1px solid black;
6030: border-spacing: 1px;
6031: }
1.795 www 6032:
1.421 albertel 6033: table.LC_pick_box td.LC_pick_box_title {
1.850 bisitz 6034: background: $sidebg;
1.421 albertel 6035: font-weight: bold;
1.900 bisitz 6036: text-align: left;
1.740 bisitz 6037: vertical-align: top;
1.421 albertel 6038: width: 184px;
6039: padding: 8px;
6040: }
1.795 www 6041:
1.579 raeburn 6042: table.LC_pick_box td.LC_pick_box_value {
6043: text-align: left;
6044: padding: 8px;
6045: }
1.795 www 6046:
1.579 raeburn 6047: table.LC_pick_box td.LC_pick_box_select {
6048: text-align: left;
6049: padding: 8px;
6050: }
1.795 www 6051:
1.424 albertel 6052: table.LC_pick_box td.LC_pick_box_separator {
1.803 bisitz 6053: padding: 0;
1.421 albertel 6054: height: 1px;
6055: background: black;
6056: }
1.795 www 6057:
1.421 albertel 6058: table.LC_pick_box td.LC_pick_box_submit {
6059: text-align: right;
6060: }
1.795 www 6061:
1.579 raeburn 6062: table.LC_pick_box td.LC_evenrow_value {
6063: text-align: left;
6064: padding: 8px;
6065: background-color: $data_table_light;
6066: }
1.795 www 6067:
1.579 raeburn 6068: table.LC_pick_box td.LC_oddrow_value {
6069: text-align: left;
6070: padding: 8px;
6071: background-color: $data_table_light;
6072: }
1.795 www 6073:
1.579 raeburn 6074: span.LC_helpform_receipt_cat {
6075: font-weight: bold;
6076: }
1.795 www 6077:
1.424 albertel 6078: table.LC_group_priv_box {
6079: background: white;
6080: border: 1px solid black;
6081: border-spacing: 1px;
6082: }
1.795 www 6083:
1.424 albertel 6084: table.LC_group_priv_box td.LC_pick_box_title {
6085: background: $tabbg;
6086: font-weight: bold;
6087: text-align: right;
6088: width: 184px;
6089: }
1.795 www 6090:
1.424 albertel 6091: table.LC_group_priv_box td.LC_groups_fixed {
6092: background: $data_table_light;
6093: text-align: center;
6094: }
1.795 www 6095:
1.424 albertel 6096: table.LC_group_priv_box td.LC_groups_optional {
6097: background: $data_table_dark;
6098: text-align: center;
6099: }
1.795 www 6100:
1.424 albertel 6101: table.LC_group_priv_box td.LC_groups_functionality {
6102: background: $data_table_darker;
6103: text-align: center;
6104: font-weight: bold;
6105: }
1.795 www 6106:
1.424 albertel 6107: table.LC_group_priv td {
6108: text-align: left;
1.803 bisitz 6109: padding: 0;
1.424 albertel 6110: }
6111:
6112: .LC_navbuttons {
6113: margin: 2ex 0ex 2ex 0ex;
6114: }
1.795 www 6115:
1.423 albertel 6116: .LC_topic_bar {
6117: font-weight: bold;
6118: background: $tabbg;
1.918 wenzelju 6119: margin: 1em 0em 1em 2em;
1.805 bisitz 6120: padding: 3px;
1.918 wenzelju 6121: font-size: 1.2em;
1.423 albertel 6122: }
1.795 www 6123:
1.423 albertel 6124: .LC_topic_bar span {
1.918 wenzelju 6125: left: 0.5em;
6126: position: absolute;
1.423 albertel 6127: vertical-align: middle;
1.918 wenzelju 6128: font-size: 1.2em;
1.423 albertel 6129: }
1.795 www 6130:
1.423 albertel 6131: table.LC_course_group_status {
6132: margin: 20px;
6133: }
1.795 www 6134:
1.423 albertel 6135: table.LC_status_selector td {
6136: vertical-align: top;
6137: text-align: center;
1.424 albertel 6138: padding: 4px;
6139: }
1.795 www 6140:
1.599 albertel 6141: div.LC_feedback_link {
1.616 albertel 6142: clear: both;
1.829 kalberla 6143: background: $sidebg;
1.779 bisitz 6144: width: 100%;
1.829 kalberla 6145: padding-bottom: 10px;
6146: border: 1px $tabbg solid;
1.833 kalberla 6147: height: 22px;
6148: line-height: 22px;
6149: padding-top: 5px;
6150: }
6151:
6152: div.LC_feedback_link img {
6153: height: 22px;
1.867 kalberla 6154: vertical-align:middle;
1.829 kalberla 6155: }
6156:
1.911 bisitz 6157: div.LC_feedback_link a {
1.829 kalberla 6158: text-decoration: none;
1.489 raeburn 6159: }
1.795 www 6160:
1.867 kalberla 6161: div.LC_comblock {
1.911 bisitz 6162: display:inline;
1.867 kalberla 6163: color:$font;
6164: font-size:90%;
6165: }
6166:
6167: div.LC_feedback_link div.LC_comblock {
6168: padding-left:5px;
6169: }
6170:
6171: div.LC_feedback_link div.LC_comblock a {
6172: color:$font;
6173: }
6174:
1.489 raeburn 6175: span.LC_feedback_link {
1.858 bisitz 6176: /* background: $feedback_link_bg; */
1.599 albertel 6177: font-size: larger;
6178: }
1.795 www 6179:
1.599 albertel 6180: span.LC_message_link {
1.858 bisitz 6181: /* background: $feedback_link_bg; */
1.599 albertel 6182: font-size: larger;
6183: position: absolute;
6184: right: 1em;
1.489 raeburn 6185: }
1.421 albertel 6186:
1.515 albertel 6187: table.LC_prior_tries {
1.524 albertel 6188: border: 1px solid #000000;
6189: border-collapse: separate;
6190: border-spacing: 1px;
1.515 albertel 6191: }
1.523 albertel 6192:
1.515 albertel 6193: table.LC_prior_tries td {
1.524 albertel 6194: padding: 2px;
1.515 albertel 6195: }
1.523 albertel 6196:
6197: .LC_answer_correct {
1.795 www 6198: background: lightgreen;
6199: color: darkgreen;
6200: padding: 6px;
1.523 albertel 6201: }
1.795 www 6202:
1.523 albertel 6203: .LC_answer_charged_try {
1.797 www 6204: background: #FFAAAA;
1.795 www 6205: color: darkred;
6206: padding: 6px;
1.523 albertel 6207: }
1.795 www 6208:
1.779 bisitz 6209: .LC_answer_not_charged_try,
1.523 albertel 6210: .LC_answer_no_grade,
6211: .LC_answer_late {
1.795 www 6212: background: lightyellow;
1.523 albertel 6213: color: black;
1.795 www 6214: padding: 6px;
1.523 albertel 6215: }
1.795 www 6216:
1.523 albertel 6217: .LC_answer_previous {
1.795 www 6218: background: lightblue;
6219: color: darkblue;
6220: padding: 6px;
1.523 albertel 6221: }
1.795 www 6222:
1.779 bisitz 6223: .LC_answer_no_message {
1.777 tempelho 6224: background: #FFFFFF;
6225: color: black;
1.795 www 6226: padding: 6px;
1.779 bisitz 6227: }
1.795 www 6228:
1.779 bisitz 6229: .LC_answer_unknown {
6230: background: orange;
6231: color: black;
1.795 www 6232: padding: 6px;
1.777 tempelho 6233: }
1.795 www 6234:
1.529 albertel 6235: span.LC_prior_numerical,
6236: span.LC_prior_string,
6237: span.LC_prior_custom,
6238: span.LC_prior_reaction,
6239: span.LC_prior_math {
1.925 bisitz 6240: font-family: $mono;
1.523 albertel 6241: white-space: pre;
6242: }
6243:
1.525 albertel 6244: span.LC_prior_string {
1.925 bisitz 6245: font-family: $mono;
1.525 albertel 6246: white-space: pre;
6247: }
6248:
1.523 albertel 6249: table.LC_prior_option {
6250: width: 100%;
6251: border-collapse: collapse;
6252: }
1.795 www 6253:
1.911 bisitz 6254: table.LC_prior_rank,
1.795 www 6255: table.LC_prior_match {
1.528 albertel 6256: border-collapse: collapse;
6257: }
1.795 www 6258:
1.528 albertel 6259: table.LC_prior_option tr td,
6260: table.LC_prior_rank tr td,
6261: table.LC_prior_match tr td {
1.524 albertel 6262: border: 1px solid #000000;
1.515 albertel 6263: }
6264:
1.855 bisitz 6265: .LC_nobreak {
1.544 albertel 6266: white-space: nowrap;
1.519 raeburn 6267: }
6268:
1.576 raeburn 6269: span.LC_cusr_emph {
6270: font-style: italic;
6271: }
6272:
1.633 raeburn 6273: span.LC_cusr_subheading {
6274: font-weight: normal;
6275: font-size: 85%;
6276: }
6277:
1.861 bisitz 6278: div.LC_docs_entry_move {
1.859 bisitz 6279: border: 1px solid #BBBBBB;
1.545 albertel 6280: background: #DDDDDD;
1.861 bisitz 6281: width: 22px;
1.859 bisitz 6282: padding: 1px;
6283: margin: 0;
1.545 albertel 6284: }
6285:
1.861 bisitz 6286: table.LC_data_table tr > td.LC_docs_entry_commands,
6287: table.LC_data_table tr > td.LC_docs_entry_parameter {
1.545 albertel 6288: font-size: x-small;
6289: }
1.795 www 6290:
1.861 bisitz 6291: .LC_docs_entry_parameter {
6292: white-space: nowrap;
6293: }
6294:
1.544 albertel 6295: .LC_docs_copy {
1.545 albertel 6296: color: #000099;
1.544 albertel 6297: }
1.795 www 6298:
1.544 albertel 6299: .LC_docs_cut {
1.545 albertel 6300: color: #550044;
1.544 albertel 6301: }
1.795 www 6302:
1.544 albertel 6303: .LC_docs_rename {
1.545 albertel 6304: color: #009900;
1.544 albertel 6305: }
1.795 www 6306:
1.544 albertel 6307: .LC_docs_remove {
1.545 albertel 6308: color: #990000;
6309: }
6310:
1.547 albertel 6311: .LC_docs_reinit_warn,
6312: .LC_docs_ext_edit {
6313: font-size: x-small;
6314: }
6315:
1.545 albertel 6316: table.LC_docs_adddocs td,
6317: table.LC_docs_adddocs th {
6318: border: 1px solid #BBBBBB;
6319: padding: 4px;
6320: background: #DDDDDD;
1.543 albertel 6321: }
6322:
1.584 albertel 6323: table.LC_sty_begin {
6324: background: #BBFFBB;
6325: }
1.795 www 6326:
1.584 albertel 6327: table.LC_sty_end {
6328: background: #FFBBBB;
6329: }
6330:
1.589 raeburn 6331: table.LC_double_column {
1.803 bisitz 6332: border-width: 0;
1.589 raeburn 6333: border-collapse: collapse;
6334: width: 100%;
6335: padding: 2px;
6336: }
6337:
6338: table.LC_double_column tr td.LC_left_col {
1.590 raeburn 6339: top: 2px;
1.589 raeburn 6340: left: 2px;
6341: width: 47%;
6342: vertical-align: top;
6343: }
6344:
6345: table.LC_double_column tr td.LC_right_col {
6346: top: 2px;
1.779 bisitz 6347: right: 2px;
1.589 raeburn 6348: width: 47%;
6349: vertical-align: top;
6350: }
6351:
1.591 raeburn 6352: div.LC_left_float {
6353: float: left;
6354: padding-right: 5%;
1.597 albertel 6355: padding-bottom: 4px;
1.591 raeburn 6356: }
6357:
6358: div.LC_clear_float_header {
1.597 albertel 6359: padding-bottom: 2px;
1.591 raeburn 6360: }
6361:
6362: div.LC_clear_float_footer {
1.597 albertel 6363: padding-top: 10px;
1.591 raeburn 6364: clear: both;
6365: }
6366:
1.597 albertel 6367: div.LC_grade_show_user {
1.941 bisitz 6368: /* border-left: 5px solid $sidebg; */
6369: border-top: 5px solid #000000;
6370: margin: 50px 0 0 0;
1.936 bisitz 6371: padding: 15px 0 5px 10px;
1.597 albertel 6372: }
1.795 www 6373:
1.936 bisitz 6374: div.LC_grade_show_user_odd_row {
1.941 bisitz 6375: /* border-left: 5px solid #000000; */
6376: }
6377:
6378: div.LC_grade_show_user div.LC_Box {
6379: margin-right: 50px;
1.597 albertel 6380: }
6381:
6382: div.LC_grade_submissions,
6383: div.LC_grade_message_center,
1.936 bisitz 6384: div.LC_grade_info_links {
1.597 albertel 6385: margin: 5px;
6386: width: 99%;
6387: background: #FFFFFF;
6388: }
1.795 www 6389:
1.597 albertel 6390: div.LC_grade_submissions_header,
1.936 bisitz 6391: div.LC_grade_message_center_header {
1.705 tempelho 6392: font-weight: bold;
6393: font-size: large;
1.597 albertel 6394: }
1.795 www 6395:
1.597 albertel 6396: div.LC_grade_submissions_body,
1.936 bisitz 6397: div.LC_grade_message_center_body {
1.597 albertel 6398: border: 1px solid black;
6399: width: 99%;
6400: background: #FFFFFF;
6401: }
1.795 www 6402:
1.613 albertel 6403: table.LC_scantron_action {
6404: width: 100%;
6405: }
1.795 www 6406:
1.613 albertel 6407: table.LC_scantron_action tr th {
1.698 harmsja 6408: font-weight:bold;
6409: font-style:normal;
1.613 albertel 6410: }
1.795 www 6411:
1.779 bisitz 6412: .LC_edit_problem_header,
1.614 albertel 6413: div.LC_edit_problem_footer {
1.705 tempelho 6414: font-weight: normal;
6415: font-size: medium;
1.602 albertel 6416: margin: 2px;
1.1060 bisitz 6417: background-color: $sidebg;
1.600 albertel 6418: }
1.795 www 6419:
1.600 albertel 6420: div.LC_edit_problem_header,
1.602 albertel 6421: div.LC_edit_problem_header div,
1.614 albertel 6422: div.LC_edit_problem_footer,
6423: div.LC_edit_problem_footer div,
1.602 albertel 6424: div.LC_edit_problem_editxml_header,
6425: div.LC_edit_problem_editxml_header div {
1.600 albertel 6426: margin-top: 5px;
6427: }
1.795 www 6428:
1.600 albertel 6429: div.LC_edit_problem_header_title {
1.705 tempelho 6430: font-weight: bold;
6431: font-size: larger;
1.602 albertel 6432: background: $tabbg;
6433: padding: 3px;
1.1060 bisitz 6434: margin: 0 0 5px 0;
1.602 albertel 6435: }
1.795 www 6436:
1.602 albertel 6437: table.LC_edit_problem_header_title {
6438: width: 100%;
1.600 albertel 6439: background: $tabbg;
1.602 albertel 6440: }
6441:
6442: div.LC_edit_problem_discards {
6443: float: left;
6444: padding-bottom: 5px;
6445: }
1.795 www 6446:
1.602 albertel 6447: div.LC_edit_problem_saves {
6448: float: right;
6449: padding-bottom: 5px;
1.600 albertel 6450: }
1.795 www 6451:
1.911 bisitz 6452: img.stift {
1.803 bisitz 6453: border-width: 0;
6454: vertical-align: middle;
1.677 riegler 6455: }
1.680 riegler 6456:
1.923 bisitz 6457: table td.LC_mainmenu_col_fieldset {
1.680 riegler 6458: vertical-align: top;
1.777 tempelho 6459: }
1.795 www 6460:
1.716 raeburn 6461: div.LC_createcourse {
1.911 bisitz 6462: margin: 10px 10px 10px 10px;
1.716 raeburn 6463: }
6464:
1.917 raeburn 6465: .LC_dccid {
6466: margin: 0.2em 0 0 0;
6467: padding: 0;
6468: font-size: 90%;
6469: display:none;
6470: }
6471:
1.897 wenzelju 6472: ol.LC_primary_menu a:hover,
1.721 harmsja 6473: ol#LC_MenuBreadcrumbs a:hover,
6474: ol#LC_PathBreadcrumbs a:hover,
1.897 wenzelju 6475: ul#LC_secondary_menu a:hover,
1.721 harmsja 6476: .LC_FormSectionClearButton input:hover
1.795 www 6477: ul.LC_TabContent li:hover a {
1.952 onken 6478: color:$button_hover;
1.911 bisitz 6479: text-decoration:none;
1.693 droeschl 6480: }
6481:
1.779 bisitz 6482: h1 {
1.911 bisitz 6483: padding: 0;
6484: line-height:130%;
1.693 droeschl 6485: }
1.698 harmsja 6486:
1.911 bisitz 6487: h2,
6488: h3,
6489: h4,
6490: h5,
6491: h6 {
6492: margin: 5px 0 5px 0;
6493: padding: 0;
6494: line-height:130%;
1.693 droeschl 6495: }
1.795 www 6496:
6497: .LC_hcell {
1.911 bisitz 6498: padding:3px 15px 3px 15px;
6499: margin: 0;
6500: background-color:$tabbg;
6501: color:$fontmenu;
6502: border-bottom:solid 1px $lg_border_color;
1.693 droeschl 6503: }
1.795 www 6504:
1.840 bisitz 6505: .LC_Box > .LC_hcell {
1.911 bisitz 6506: margin: 0 -10px 10px -10px;
1.835 bisitz 6507: }
6508:
1.721 harmsja 6509: .LC_noBorder {
1.911 bisitz 6510: border: 0;
1.698 harmsja 6511: }
1.693 droeschl 6512:
1.721 harmsja 6513: .LC_FormSectionClearButton input {
1.911 bisitz 6514: background-color:transparent;
6515: border: none;
6516: cursor:pointer;
6517: text-decoration:underline;
1.693 droeschl 6518: }
1.763 bisitz 6519:
6520: .LC_help_open_topic {
1.911 bisitz 6521: color: #FFFFFF;
6522: background-color: #EEEEFF;
6523: margin: 1px;
6524: padding: 4px;
6525: border: 1px solid #000033;
6526: white-space: nowrap;
6527: /* vertical-align: middle; */
1.759 neumanie 6528: }
1.693 droeschl 6529:
1.911 bisitz 6530: dl,
6531: ul,
6532: div,
6533: fieldset {
6534: margin: 10px 10px 10px 0;
6535: /* overflow: hidden; */
1.693 droeschl 6536: }
1.795 www 6537:
1.838 bisitz 6538: fieldset > legend {
1.911 bisitz 6539: font-weight: bold;
6540: padding: 0 5px 0 5px;
1.838 bisitz 6541: }
6542:
1.813 bisitz 6543: #LC_nav_bar {
1.911 bisitz 6544: float: left;
1.995 raeburn 6545: background-color: $pgbg_or_bgcolor;
1.966 bisitz 6546: margin: 0 0 2px 0;
1.807 droeschl 6547: }
6548:
1.916 droeschl 6549: #LC_realm {
6550: margin: 0.2em 0 0 0;
6551: padding: 0;
6552: font-weight: bold;
6553: text-align: center;
1.995 raeburn 6554: background-color: $pgbg_or_bgcolor;
1.916 droeschl 6555: }
6556:
1.911 bisitz 6557: #LC_nav_bar em {
6558: font-weight: bold;
6559: font-style: normal;
1.807 droeschl 6560: }
6561:
1.897 wenzelju 6562: ol.LC_primary_menu {
1.911 bisitz 6563: float: right;
1.934 droeschl 6564: margin: 0;
1.1076 raeburn 6565: padding: 0;
1.995 raeburn 6566: background-color: $pgbg_or_bgcolor;
1.807 droeschl 6567: }
6568:
1.852 droeschl 6569: ol#LC_PathBreadcrumbs {
1.911 bisitz 6570: margin: 0;
1.693 droeschl 6571: }
6572:
1.897 wenzelju 6573: ol.LC_primary_menu li {
1.1076 raeburn 6574: color: RGB(80, 80, 80);
6575: vertical-align: middle;
6576: text-align: left;
6577: list-style: none;
6578: float: left;
6579: }
6580:
6581: ol.LC_primary_menu li a {
6582: display: block;
6583: margin: 0;
6584: padding: 0 5px 0 10px;
6585: text-decoration: none;
6586: }
6587:
6588: ol.LC_primary_menu li ul {
6589: display: none;
6590: width: 10em;
6591: background-color: $data_table_light;
6592: }
6593:
6594: ol.LC_primary_menu li:hover ul, ol.LC_primary_menu li.hover ul {
6595: display: block;
6596: position: absolute;
6597: margin: 0;
6598: padding: 0;
1.1078 raeburn 6599: z-index: 2;
1.1076 raeburn 6600: }
6601:
6602: ol.LC_primary_menu li:hover li, ol.LC_primary_menu li.hover li {
6603: font-size: 90%;
1.911 bisitz 6604: vertical-align: top;
1.1076 raeburn 6605: float: none;
1.1079 raeburn 6606: border-left: 1px solid black;
6607: border-right: 1px solid black;
1.1076 raeburn 6608: }
6609:
6610: ol.LC_primary_menu li:hover li a, ol.LC_primary_menu li.hover li a {
1.1078 raeburn 6611: background-color:$data_table_light;
1.1076 raeburn 6612: }
6613:
6614: ol.LC_primary_menu li li a:hover {
6615: color:$button_hover;
6616: background-color:$data_table_dark;
1.693 droeschl 6617: }
6618:
1.897 wenzelju 6619: ol.LC_primary_menu li img {
1.911 bisitz 6620: vertical-align: bottom;
1.934 droeschl 6621: height: 1.1em;
1.1077 raeburn 6622: margin: 0.2em 0 0 0;
1.693 droeschl 6623: }
6624:
1.897 wenzelju 6625: ol.LC_primary_menu a {
1.911 bisitz 6626: color: RGB(80, 80, 80);
6627: text-decoration: none;
1.693 droeschl 6628: }
1.795 www 6629:
1.949 droeschl 6630: ol.LC_primary_menu a.LC_new_message {
6631: font-weight:bold;
6632: color: darkred;
6633: }
6634:
1.975 raeburn 6635: ol.LC_docs_parameters {
6636: margin-left: 0;
6637: padding: 0;
6638: list-style: none;
6639: }
6640:
6641: ol.LC_docs_parameters li {
6642: margin: 0;
6643: padding-right: 20px;
6644: display: inline;
6645: }
6646:
1.976 raeburn 6647: ol.LC_docs_parameters li:before {
6648: content: "\\002022 \\0020";
6649: }
6650:
6651: li.LC_docs_parameters_title {
6652: font-weight: bold;
6653: }
6654:
6655: ol.LC_docs_parameters li.LC_docs_parameters_title:before {
6656: content: "";
6657: }
6658:
1.897 wenzelju 6659: ul#LC_secondary_menu {
1.1107 raeburn 6660: clear: right;
1.911 bisitz 6661: color: $fontmenu;
6662: background: $tabbg;
6663: list-style: none;
6664: padding: 0;
6665: margin: 0;
6666: width: 100%;
1.995 raeburn 6667: text-align: left;
1.1107 raeburn 6668: float: left;
1.808 droeschl 6669: }
6670:
1.897 wenzelju 6671: ul#LC_secondary_menu li {
1.911 bisitz 6672: font-weight: bold;
6673: line-height: 1.8em;
1.1107 raeburn 6674: border-right: 1px solid black;
6675: float: left;
6676: }
6677:
6678: ul#LC_secondary_menu li.LC_hoverable:hover, ul#LC_secondary_menu li.hover {
6679: background-color: $data_table_light;
6680: }
6681:
6682: ul#LC_secondary_menu li a {
1.911 bisitz 6683: padding: 0 0.8em;
1.1107 raeburn 6684: }
6685:
6686: ul#LC_secondary_menu li ul {
6687: display: none;
6688: }
6689:
6690: ul#LC_secondary_menu li:hover ul, ul#LC_secondary_menu li.hover ul {
6691: display: block;
6692: position: absolute;
6693: margin: 0;
6694: padding: 0;
6695: list-style:none;
6696: float: none;
6697: background-color: $data_table_light;
6698: z-index: 2;
6699: margin-left: -1px;
6700: }
6701:
6702: ul#LC_secondary_menu li ul li {
6703: font-size: 90%;
6704: vertical-align: top;
6705: border-left: 1px solid black;
1.911 bisitz 6706: border-right: 1px solid black;
1.1107 raeburn 6707: background-color: $data_table_light
6708: list-style:none;
6709: float: none;
6710: }
6711:
6712: ul#LC_secondary_menu li ul li:hover, ul#LC_secondary_menu li ul li.hover {
6713: background-color: $data_table_dark;
1.807 droeschl 6714: }
6715:
1.847 tempelho 6716: ul.LC_TabContent {
1.911 bisitz 6717: display:block;
6718: background: $sidebg;
6719: border-bottom: solid 1px $lg_border_color;
6720: list-style:none;
1.1020 raeburn 6721: margin: -1px -10px 0 -10px;
1.911 bisitz 6722: padding: 0;
1.693 droeschl 6723: }
6724:
1.795 www 6725: ul.LC_TabContent li,
6726: ul.LC_TabContentBigger li {
1.911 bisitz 6727: float:left;
1.741 harmsja 6728: }
1.795 www 6729:
1.897 wenzelju 6730: ul#LC_secondary_menu li a {
1.911 bisitz 6731: color: $fontmenu;
6732: text-decoration: none;
1.693 droeschl 6733: }
1.795 www 6734:
1.721 harmsja 6735: ul.LC_TabContent {
1.952 onken 6736: min-height:20px;
1.721 harmsja 6737: }
1.795 www 6738:
6739: ul.LC_TabContent li {
1.911 bisitz 6740: vertical-align:middle;
1.959 onken 6741: padding: 0 16px 0 10px;
1.911 bisitz 6742: background-color:$tabbg;
6743: border-bottom:solid 1px $lg_border_color;
1.1020 raeburn 6744: border-left: solid 1px $font;
1.721 harmsja 6745: }
1.795 www 6746:
1.847 tempelho 6747: ul.LC_TabContent .right {
1.911 bisitz 6748: float:right;
1.847 tempelho 6749: }
6750:
1.911 bisitz 6751: ul.LC_TabContent li a,
6752: ul.LC_TabContent li {
6753: color:rgb(47,47,47);
6754: text-decoration:none;
6755: font-size:95%;
6756: font-weight:bold;
1.952 onken 6757: min-height:20px;
6758: }
6759:
1.959 onken 6760: ul.LC_TabContent li a:hover,
6761: ul.LC_TabContent li a:focus {
1.952 onken 6762: color: $button_hover;
1.959 onken 6763: background:none;
6764: outline:none;
1.952 onken 6765: }
6766:
6767: ul.LC_TabContent li:hover {
6768: color: $button_hover;
6769: cursor:pointer;
1.721 harmsja 6770: }
1.795 www 6771:
1.911 bisitz 6772: ul.LC_TabContent li.active {
1.952 onken 6773: color: $font;
1.911 bisitz 6774: background:#FFFFFF url(/adm/lonIcons/open.gif) no-repeat scroll right center;
1.952 onken 6775: border-bottom:solid 1px #FFFFFF;
6776: cursor: default;
1.744 ehlerst 6777: }
1.795 www 6778:
1.959 onken 6779: ul.LC_TabContent li.active a {
6780: color:$font;
6781: background:#FFFFFF;
6782: outline: none;
6783: }
1.1047 raeburn 6784:
6785: ul.LC_TabContent li.goback {
6786: float: left;
6787: border-left: none;
6788: }
6789:
1.870 tempelho 6790: #maincoursedoc {
1.911 bisitz 6791: clear:both;
1.870 tempelho 6792: }
6793:
6794: ul.LC_TabContentBigger {
1.911 bisitz 6795: display:block;
6796: list-style:none;
6797: padding: 0;
1.870 tempelho 6798: }
6799:
1.795 www 6800: ul.LC_TabContentBigger li {
1.911 bisitz 6801: vertical-align:bottom;
6802: height: 30px;
6803: font-size:110%;
6804: font-weight:bold;
6805: color: #737373;
1.841 tempelho 6806: }
6807:
1.957 onken 6808: ul.LC_TabContentBigger li.active {
6809: position: relative;
6810: top: 1px;
6811: }
6812:
1.870 tempelho 6813: ul.LC_TabContentBigger li a {
1.911 bisitz 6814: background:url('/adm/lonIcons/tabbgleft.gif') left bottom no-repeat;
6815: height: 30px;
6816: line-height: 30px;
6817: text-align: center;
6818: display: block;
6819: text-decoration: none;
1.958 onken 6820: outline: none;
1.741 harmsja 6821: }
1.795 www 6822:
1.870 tempelho 6823: ul.LC_TabContentBigger li.active a {
1.911 bisitz 6824: background:url('/adm/lonIcons/tabbgleft.gif') left top no-repeat;
6825: color:$font;
1.744 ehlerst 6826: }
1.795 www 6827:
1.870 tempelho 6828: ul.LC_TabContentBigger li b {
1.911 bisitz 6829: background: url('/adm/lonIcons/tabbgright.gif') no-repeat right bottom;
6830: display: block;
6831: float: left;
6832: padding: 0 30px;
1.957 onken 6833: border-bottom: 1px solid $lg_border_color;
1.870 tempelho 6834: }
6835:
1.956 onken 6836: ul.LC_TabContentBigger li:hover b {
6837: color:$button_hover;
6838: }
6839:
1.870 tempelho 6840: ul.LC_TabContentBigger li.active b {
1.911 bisitz 6841: background:url('/adm/lonIcons/tabbgright.gif') right top no-repeat;
6842: color:$font;
1.957 onken 6843: border: 0;
1.741 harmsja 6844: }
1.693 droeschl 6845:
1.870 tempelho 6846:
1.862 bisitz 6847: ul.LC_CourseBreadcrumbs {
6848: background: $sidebg;
1.1020 raeburn 6849: height: 2em;
1.862 bisitz 6850: padding-left: 10px;
1.1020 raeburn 6851: margin: 0;
1.862 bisitz 6852: list-style-position: inside;
6853: }
6854:
1.911 bisitz 6855: ol#LC_MenuBreadcrumbs,
1.862 bisitz 6856: ol#LC_PathBreadcrumbs {
1.911 bisitz 6857: padding-left: 10px;
6858: margin: 0;
1.933 droeschl 6859: height: 2.5em; /* equal to #LC_breadcrumbs line-height */
1.693 droeschl 6860: }
6861:
1.911 bisitz 6862: ol#LC_MenuBreadcrumbs li,
6863: ol#LC_PathBreadcrumbs li,
1.862 bisitz 6864: ul.LC_CourseBreadcrumbs li {
1.911 bisitz 6865: display: inline;
1.933 droeschl 6866: white-space: normal;
1.693 droeschl 6867: }
6868:
1.823 bisitz 6869: ol#LC_MenuBreadcrumbs li a,
1.862 bisitz 6870: ul.LC_CourseBreadcrumbs li a {
1.911 bisitz 6871: text-decoration: none;
6872: font-size:90%;
1.693 droeschl 6873: }
1.795 www 6874:
1.969 droeschl 6875: ol#LC_MenuBreadcrumbs h1 {
6876: display: inline;
6877: font-size: 90%;
6878: line-height: 2.5em;
6879: margin: 0;
6880: padding: 0;
6881: }
6882:
1.795 www 6883: ol#LC_PathBreadcrumbs li a {
1.911 bisitz 6884: text-decoration:none;
6885: font-size:100%;
6886: font-weight:bold;
1.693 droeschl 6887: }
1.795 www 6888:
1.840 bisitz 6889: .LC_Box {
1.911 bisitz 6890: border: solid 1px $lg_border_color;
6891: padding: 0 10px 10px 10px;
1.746 neumanie 6892: }
1.795 www 6893:
1.1020 raeburn 6894: .LC_DocsBox {
6895: border: solid 1px $lg_border_color;
6896: padding: 0 0 10px 10px;
6897: }
6898:
1.795 www 6899: .LC_AboutMe_Image {
1.911 bisitz 6900: float:left;
6901: margin-right:10px;
1.747 neumanie 6902: }
1.795 www 6903:
6904: .LC_Clear_AboutMe_Image {
1.911 bisitz 6905: clear:left;
1.747 neumanie 6906: }
1.795 www 6907:
1.721 harmsja 6908: dl.LC_ListStyleClean dt {
1.911 bisitz 6909: padding-right: 5px;
6910: display: table-header-group;
1.693 droeschl 6911: }
6912:
1.721 harmsja 6913: dl.LC_ListStyleClean dd {
1.911 bisitz 6914: display: table-row;
1.693 droeschl 6915: }
6916:
1.721 harmsja 6917: .LC_ListStyleClean,
6918: .LC_ListStyleSimple,
6919: .LC_ListStyleNormal,
1.795 www 6920: .LC_ListStyleSpecial {
1.911 bisitz 6921: /* display:block; */
6922: list-style-position: inside;
6923: list-style-type: none;
6924: overflow: hidden;
6925: padding: 0;
1.693 droeschl 6926: }
6927:
1.721 harmsja 6928: .LC_ListStyleSimple li,
6929: .LC_ListStyleSimple dd,
6930: .LC_ListStyleNormal li,
6931: .LC_ListStyleNormal dd,
6932: .LC_ListStyleSpecial li,
1.795 www 6933: .LC_ListStyleSpecial dd {
1.911 bisitz 6934: margin: 0;
6935: padding: 5px 5px 5px 10px;
6936: clear: both;
1.693 droeschl 6937: }
6938:
1.721 harmsja 6939: .LC_ListStyleClean li,
6940: .LC_ListStyleClean dd {
1.911 bisitz 6941: padding-top: 0;
6942: padding-bottom: 0;
1.693 droeschl 6943: }
6944:
1.721 harmsja 6945: .LC_ListStyleSimple dd,
1.795 www 6946: .LC_ListStyleSimple li {
1.911 bisitz 6947: border-bottom: solid 1px $lg_border_color;
1.693 droeschl 6948: }
6949:
1.721 harmsja 6950: .LC_ListStyleSpecial li,
6951: .LC_ListStyleSpecial dd {
1.911 bisitz 6952: list-style-type: none;
6953: background-color: RGB(220, 220, 220);
6954: margin-bottom: 4px;
1.693 droeschl 6955: }
6956:
1.721 harmsja 6957: table.LC_SimpleTable {
1.911 bisitz 6958: margin:5px;
6959: border:solid 1px $lg_border_color;
1.795 www 6960: }
1.693 droeschl 6961:
1.721 harmsja 6962: table.LC_SimpleTable tr {
1.911 bisitz 6963: padding: 0;
6964: border:solid 1px $lg_border_color;
1.693 droeschl 6965: }
1.795 www 6966:
6967: table.LC_SimpleTable thead {
1.911 bisitz 6968: background:rgb(220,220,220);
1.693 droeschl 6969: }
6970:
1.721 harmsja 6971: div.LC_columnSection {
1.911 bisitz 6972: display: block;
6973: clear: both;
6974: overflow: hidden;
6975: margin: 0;
1.693 droeschl 6976: }
6977:
1.721 harmsja 6978: div.LC_columnSection>* {
1.911 bisitz 6979: float: left;
6980: margin: 10px 20px 10px 0;
6981: overflow:hidden;
1.693 droeschl 6982: }
1.721 harmsja 6983:
1.795 www 6984: table em {
1.911 bisitz 6985: font-weight: bold;
6986: font-style: normal;
1.748 schulted 6987: }
1.795 www 6988:
1.779 bisitz 6989: table.LC_tableBrowseRes,
1.795 www 6990: table.LC_tableOfContent {
1.911 bisitz 6991: border:none;
6992: border-spacing: 1px;
6993: padding: 3px;
6994: background-color: #FFFFFF;
6995: font-size: 90%;
1.753 droeschl 6996: }
1.789 droeschl 6997:
1.911 bisitz 6998: table.LC_tableOfContent {
6999: border-collapse: collapse;
1.789 droeschl 7000: }
7001:
1.771 droeschl 7002: table.LC_tableBrowseRes a,
1.768 schulted 7003: table.LC_tableOfContent a {
1.911 bisitz 7004: background-color: transparent;
7005: text-decoration: none;
1.753 droeschl 7006: }
7007:
1.795 www 7008: table.LC_tableOfContent img {
1.911 bisitz 7009: border: none;
7010: height: 1.3em;
7011: vertical-align: text-bottom;
7012: margin-right: 0.3em;
1.753 droeschl 7013: }
1.757 schulted 7014:
1.795 www 7015: a#LC_content_toolbar_firsthomework {
1.911 bisitz 7016: background-image:url(/res/adm/pages/open-first-problem.gif);
1.774 ehlerst 7017: }
7018:
1.795 www 7019: a#LC_content_toolbar_everything {
1.911 bisitz 7020: background-image:url(/res/adm/pages/show-all.gif);
1.774 ehlerst 7021: }
7022:
1.795 www 7023: a#LC_content_toolbar_uncompleted {
1.911 bisitz 7024: background-image:url(/res/adm/pages/show-incomplete-problems.gif);
1.774 ehlerst 7025: }
7026:
1.795 www 7027: #LC_content_toolbar_clearbubbles {
1.911 bisitz 7028: background-image:url(/res/adm/pages/mark-discussionentries-read.gif);
1.774 ehlerst 7029: }
7030:
1.795 www 7031: a#LC_content_toolbar_changefolder {
1.911 bisitz 7032: background : url(/res/adm/pages/close-all-folders.gif) top center ;
1.757 schulted 7033: }
7034:
1.795 www 7035: a#LC_content_toolbar_changefolder_toggled {
1.911 bisitz 7036: background-image:url(/res/adm/pages/open-all-folders.gif);
1.757 schulted 7037: }
7038:
1.1043 raeburn 7039: a#LC_content_toolbar_edittoplevel {
7040: background-image:url(/res/adm/pages/edittoplevel.gif);
7041: }
7042:
1.795 www 7043: ul#LC_toolbar li a:hover {
1.911 bisitz 7044: background-position: bottom center;
1.757 schulted 7045: }
7046:
1.795 www 7047: ul#LC_toolbar {
1.911 bisitz 7048: padding: 0;
7049: margin: 2px;
7050: list-style:none;
7051: position:relative;
7052: background-color:white;
1.1082 raeburn 7053: overflow: auto;
1.757 schulted 7054: }
7055:
1.795 www 7056: ul#LC_toolbar li {
1.911 bisitz 7057: border:1px solid white;
7058: padding: 0;
7059: margin: 0;
7060: float: left;
7061: display:inline;
7062: vertical-align:middle;
1.1082 raeburn 7063: white-space: nowrap;
1.911 bisitz 7064: }
1.757 schulted 7065:
1.783 amueller 7066:
1.795 www 7067: a.LC_toolbarItem {
1.911 bisitz 7068: display:block;
7069: padding: 0;
7070: margin: 0;
7071: height: 32px;
7072: width: 32px;
7073: color:white;
7074: border: none;
7075: background-repeat:no-repeat;
7076: background-color:transparent;
1.757 schulted 7077: }
7078:
1.915 droeschl 7079: ul.LC_funclist {
7080: margin: 0;
7081: padding: 0.5em 1em 0.5em 0;
7082: }
7083:
1.933 droeschl 7084: ul.LC_funclist > li:first-child {
7085: font-weight:bold;
7086: margin-left:0.8em;
7087: }
7088:
1.915 droeschl 7089: ul.LC_funclist + ul.LC_funclist {
7090: /*
7091: left border as a seperator if we have more than
7092: one list
7093: */
7094: border-left: 1px solid $sidebg;
7095: /*
7096: this hides the left border behind the border of the
7097: outer box if element is wrapped to the next 'line'
7098: */
7099: margin-left: -1px;
7100: }
7101:
1.843 bisitz 7102: ul.LC_funclist li {
1.915 droeschl 7103: display: inline;
1.782 bisitz 7104: white-space: nowrap;
1.915 droeschl 7105: margin: 0 0 0 25px;
7106: line-height: 150%;
1.782 bisitz 7107: }
7108:
1.974 wenzelju 7109: .LC_hidden {
7110: display: none;
7111: }
7112:
1.1030 www 7113: .LCmodal-overlay {
7114: position:fixed;
7115: top:0;
7116: right:0;
7117: bottom:0;
7118: left:0;
7119: height:100%;
7120: width:100%;
7121: margin:0;
7122: padding:0;
7123: background:#999;
7124: opacity:.75;
7125: filter: alpha(opacity=75);
7126: -moz-opacity: 0.75;
7127: z-index:101;
7128: }
7129:
7130: * html .LCmodal-overlay {
7131: position: absolute;
7132: height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px');
7133: }
7134:
7135: .LCmodal-window {
7136: position:fixed;
7137: top:50%;
7138: left:50%;
7139: margin:0;
7140: padding:0;
7141: z-index:102;
7142: }
7143:
7144: * html .LCmodal-window {
7145: position:absolute;
7146: }
7147:
7148: .LCclose-window {
7149: position:absolute;
7150: width:32px;
7151: height:32px;
7152: right:8px;
7153: top:8px;
7154: background:transparent url('/res/adm/pages/process-stop.png') no-repeat scroll right top;
7155: text-indent:-99999px;
7156: overflow:hidden;
7157: cursor:pointer;
7158: }
7159:
1.1100 raeburn 7160: /*
7161: styles used by TTH when "Default set of options to pass to tth/m
7162: when converting TeX" in course settings has been set
7163:
7164: option passed: -t
7165:
7166: */
7167:
7168: td div.comp { margin-top: -0.6ex; margin-bottom: -1ex;}
7169: td div.comb { margin-top: -0.6ex; margin-bottom: -.6ex;}
7170: td div.hrcomp { line-height: 0.9; margin-top: -0.8ex; margin-bottom: -1ex;}
7171: td div.norm {line-height:normal;}
7172:
7173: /*
7174: option passed -y3
7175: */
7176:
7177: span.roman {font-family: serif; font-style: normal; font-weight: normal;}
7178: span.overacc2 {position: relative; left: .8em; top: -1.2ex;}
7179: span.overacc1 {position: relative; left: .6em; top: -1.2ex;}
7180:
1.343 albertel 7181: END
7182: }
7183:
1.306 albertel 7184: =pod
7185:
7186: =item * &headtag()
7187:
7188: Returns a uniform footer for LON-CAPA web pages.
7189:
1.307 albertel 7190: Inputs: $title - optional title for the head
7191: $head_extra - optional extra HTML to put inside the <head>
1.315 albertel 7192: $args - optional arguments
1.319 albertel 7193: force_register - if is true call registerurl so the remote is
7194: informed
1.415 albertel 7195: redirect -> array ref of
7196: 1- seconds before redirect occurs
7197: 2- url to redirect to
7198: 3- whether the side effect should occur
1.315 albertel 7199: (side effect of setting
7200: $env{'internal.head.redirect'} to the url
7201: redirected too)
1.352 albertel 7202: domain -> force to color decorate a page for a specific
7203: domain
7204: function -> force usage of a specific rolish color scheme
7205: bgcolor -> override the default page bgcolor
1.460 albertel 7206: no_auto_mt_title
7207: -> prevent &mt()ing the title arg
1.464 albertel 7208:
1.306 albertel 7209: =cut
7210:
7211: sub headtag {
1.313 albertel 7212: my ($title,$head_extra,$args) = @_;
1.306 albertel 7213:
1.363 albertel 7214: my $function = $args->{'function'} || &get_users_function();
7215: my $domain = $args->{'domain'} || &determinedomain();
7216: my $bgcolor = $args->{'bgcolor'} || &designparm($function.'.pgbg',$domain);
1.418 albertel 7217: my $url = join(':',$env{'user.name'},$env{'user.domain'},
1.458 albertel 7218: $Apache::lonnet::perlvar{'lonVersion'},
1.531 albertel 7219: #time(),
1.418 albertel 7220: $env{'environment.color.timestamp'},
1.363 albertel 7221: $function,$domain,$bgcolor);
7222:
1.369 www 7223: $url = '/adm/css/'.&escape($url).'.css';
1.363 albertel 7224:
1.308 albertel 7225: my $result =
7226: '<head>'.
1.461 albertel 7227: &font_settings();
1.319 albertel 7228:
1.1064 raeburn 7229: my $inhibitprint = &print_suppression();
7230:
1.461 albertel 7231: if (!$args->{'frameset'}) {
7232: $result .= &Apache::lonhtmlcommon::htmlareaheaders();
7233: }
1.962 droeschl 7234: if ($args->{'force_register'} && $env{'request.noversionuri'} !~ m{^/res/adm/pages/}) {
7235: $result .= Apache::lonxml::display_title();
1.319 albertel 7236: }
1.436 albertel 7237: if (!$args->{'no_nav_bar'}
7238: && !$args->{'only_body'}
7239: && !$args->{'frameset'}) {
7240: $result .= &help_menu_js();
1.1032 www 7241: $result.=&modal_window();
1.1038 www 7242: $result.=&togglebox_script();
1.1034 www 7243: $result.=&wishlist_window();
1.1041 www 7244: $result.=&LCprogressbarUpdate_script();
1.1034 www 7245: } else {
7246: if ($args->{'add_modal'}) {
7247: $result.=&modal_window();
7248: }
7249: if ($args->{'add_wishlist'}) {
7250: $result.=&wishlist_window();
7251: }
1.1038 www 7252: if ($args->{'add_togglebox'}) {
7253: $result.=&togglebox_script();
7254: }
1.1041 www 7255: if ($args->{'add_progressbar'}) {
7256: $result.=&LCprogressbarUpdate_script();
7257: }
1.436 albertel 7258: }
1.314 albertel 7259: if (ref($args->{'redirect'})) {
1.414 albertel 7260: my ($time,$url,$inhibit_continue) = @{$args->{'redirect'}};
1.315 albertel 7261: $url = &Apache::lonenc::check_encrypt($url);
1.414 albertel 7262: if (!$inhibit_continue) {
7263: $env{'internal.head.redirect'} = $url;
7264: }
1.313 albertel 7265: $result.=<<ADDMETA
7266: <meta http-equiv="pragma" content="no-cache" />
1.344 albertel 7267: <meta http-equiv="Refresh" content="$time; url=$url" />
1.313 albertel 7268: ADDMETA
7269: }
1.306 albertel 7270: if (!defined($title)) {
7271: $title = 'The LearningOnline Network with CAPA';
7272: }
1.460 albertel 7273: if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
7274: $result .= '<title> LON-CAPA '.$title.'</title>'
1.414 albertel 7275: .'<link rel="stylesheet" type="text/css" href="'.$url.'" />'
1.1064 raeburn 7276: .$inhibitprint
1.414 albertel 7277: .$head_extra;
1.962 droeschl 7278: return $result.'</head>';
1.306 albertel 7279: }
7280:
7281: =pod
7282:
1.340 albertel 7283: =item * &font_settings()
7284:
7285: Returns neccessary <meta> to set the proper encoding
7286:
7287: Inputs: none
7288:
7289: =cut
7290:
7291: sub font_settings {
7292: my $headerstring='';
1.647 www 7293: if (!$env{'browser.mathml'} && $env{'browser.unicode'}) {
1.340 albertel 7294: $headerstring.=
7295: '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
7296: }
7297: return $headerstring;
7298: }
7299:
1.341 albertel 7300: =pod
7301:
1.1064 raeburn 7302: =item * &print_suppression()
7303:
7304: In course context returns css which causes the body to be blank when media="print",
7305: if printout generation is unavailable for the current resource.
7306:
7307: This could be because:
7308:
7309: (a) printstartdate is in the future
7310:
7311: (b) printenddate is in the past
7312:
7313: (c) there is an active exam block with "printout"
7314: functionality blocked
7315:
7316: Users with pav, pfo or evb privileges are exempt.
7317:
7318: Inputs: none
7319:
7320: =cut
7321:
7322:
7323: sub print_suppression {
7324: my $noprint;
7325: if ($env{'request.course.id'}) {
7326: my $scope = $env{'request.course.id'};
7327: if ((&Apache::lonnet::allowed('pav',$scope)) ||
7328: (&Apache::lonnet::allowed('pfo',$scope))) {
7329: return;
7330: }
7331: if ($env{'request.course.sec'} ne '') {
7332: $scope .= "/$env{'request.course.sec'}";
7333: if ((&Apache::lonnet::allowed('pav',$scope)) ||
7334: (&Apache::lonnet::allowed('pfo',$scope))) {
1.1065 raeburn 7335: return;
1.1064 raeburn 7336: }
7337: }
7338: my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
7339: my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
1.1065 raeburn 7340: my $blocked = &blocking_status('printout',$cnum,$cdom);
1.1064 raeburn 7341: if ($blocked) {
7342: my $checkrole = "cm./$cdom/$cnum";
7343: if ($env{'request.course.sec'} ne '') {
7344: $checkrole .= "/$env{'request.course.sec'}";
7345: }
7346: unless ((&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) &&
7347: ($env{'request.role'} !~ m{^st\./$cdom/$cnum})) {
7348: $noprint = 1;
7349: }
7350: }
7351: unless ($noprint) {
7352: my $symb = &Apache::lonnet::symbread();
7353: if ($symb ne '') {
7354: my $navmap = Apache::lonnavmaps::navmap->new();
7355: if (ref($navmap)) {
7356: my $res = $navmap->getBySymb($symb);
7357: if (ref($res)) {
7358: if (!$res->resprintable()) {
7359: $noprint = 1;
7360: }
7361: }
7362: }
7363: }
7364: }
7365: if ($noprint) {
7366: return <<"ENDSTYLE";
7367: <style type="text/css" media="print">
7368: body { display:none }
7369: </style>
7370: ENDSTYLE
7371: }
7372: }
7373: return;
7374: }
7375:
7376: =pod
7377:
1.341 albertel 7378: =item * &xml_begin()
7379:
7380: Returns the needed doctype and <html>
7381:
7382: Inputs: none
7383:
7384: =cut
7385:
7386: sub xml_begin {
7387: my $output='';
7388:
7389: if ($env{'browser.mathml'}) {
7390: $output='<?xml version="1.0"?>'
7391: #.'<?xml-stylesheet type="text/css" href="/adm/MathML/mathml.css"?>'."\n"
7392: # .'<!DOCTYPE html SYSTEM "/adm/MathML/mathml.dtd" '
7393:
7394: # .'<!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">] >'
7395: .'<!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">'
7396: .'<html xmlns:math="http://www.w3.org/1998/Math/MathML" '
7397: .'xmlns="http://www.w3.org/1999/xhtml">';
7398: } else {
1.849 bisitz 7399: $output='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
7400: .'<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">';
1.341 albertel 7401: }
7402: return $output;
7403: }
1.340 albertel 7404:
7405: =pod
7406:
1.306 albertel 7407: =item * &start_page()
7408:
7409: Returns a complete <html> .. <body> section for LON-CAPA web pages.
7410:
1.648 raeburn 7411: Inputs:
7412:
7413: =over 4
7414:
7415: $title - optional title for the page
7416:
7417: $head_extra - optional extra HTML to incude inside the <head>
7418:
7419: $args - additional optional args supported are:
7420:
7421: =over 8
7422:
7423: only_body -> is true will set &bodytag() onlybodytag
1.317 albertel 7424: arg on
1.814 bisitz 7425: no_nav_bar -> is true will set &bodytag() no_nav_bar arg on
1.648 raeburn 7426: add_entries -> additional attributes to add to the <body>
7427: domain -> force to color decorate a page for a
1.317 albertel 7428: specific domain
1.648 raeburn 7429: function -> force usage of a specific rolish color
1.317 albertel 7430: scheme
1.648 raeburn 7431: redirect -> see &headtag()
7432: bgcolor -> override the default page bg color
7433: js_ready -> return a string ready for being used in
1.317 albertel 7434: a javascript writeln
1.648 raeburn 7435: html_encode -> return a string ready for being used in
1.320 albertel 7436: a html attribute
1.648 raeburn 7437: force_register -> if is true will turn on the &bodytag()
1.317 albertel 7438: $forcereg arg
1.648 raeburn 7439: frameset -> if true will start with a <frameset>
1.330 albertel 7440: rather than <body>
1.648 raeburn 7441: skip_phases -> hash ref of
1.338 albertel 7442: head -> skip the <html><head> generation
7443: body -> skip all <body> generation
1.648 raeburn 7444: no_auto_mt_title -> prevent &mt()ing the title arg
7445: inherit_jsmath -> when creating popup window in a page,
7446: should it have jsmath forced on by the
7447: current page
1.867 kalberla 7448: bread_crumbs -> Array containing breadcrumbs
1.983 raeburn 7449: bread_crumbs_component -> if exists show it as headline else show only the breadcrumbs
1.1096 raeburn 7450: group -> includes the current group, if page is for a
7451: specific group
1.361 albertel 7452:
1.648 raeburn 7453: =back
1.460 albertel 7454:
1.648 raeburn 7455: =back
1.562 albertel 7456:
1.306 albertel 7457: =cut
7458:
7459: sub start_page {
1.309 albertel 7460: my ($title,$head_extra,$args) = @_;
1.318 albertel 7461: #&Apache::lonnet::logthis("start_page ".join(':',caller(0)));
1.319 albertel 7462:
1.315 albertel 7463: $env{'internal.start_page'}++;
1.1096 raeburn 7464: my ($result,@advtools);
1.964 droeschl 7465:
1.338 albertel 7466: if (! exists($args->{'skip_phases'}{'head'}) ) {
1.1030 www 7467: $result .= &xml_begin() . &headtag($title, $head_extra, $args);
1.338 albertel 7468: }
7469:
7470: if (! exists($args->{'skip_phases'}{'body'}) ) {
7471: if ($args->{'frameset'}) {
7472: my $attr_string = &make_attr_string($args->{'force_register'},
7473: $args->{'add_entries'});
7474: $result .= "\n<frameset $attr_string>\n";
1.831 bisitz 7475: } else {
7476: $result .=
7477: &bodytag($title,
7478: $args->{'function'}, $args->{'add_entries'},
7479: $args->{'only_body'}, $args->{'domain'},
7480: $args->{'force_register'}, $args->{'no_nav_bar'},
1.1096 raeburn 7481: $args->{'bgcolor'}, $args,
7482: \@advtools);
1.831 bisitz 7483: }
1.330 albertel 7484: }
1.338 albertel 7485:
1.315 albertel 7486: if ($args->{'js_ready'}) {
1.713 kaisler 7487: $result = &js_ready($result);
1.315 albertel 7488: }
1.320 albertel 7489: if ($args->{'html_encode'}) {
1.713 kaisler 7490: $result = &html_encode($result);
7491: }
7492:
1.813 bisitz 7493: # Preparation for new and consistent functionlist at top of screen
7494: # if ($args->{'functionlist'}) {
7495: # $result .= &build_functionlist();
7496: #}
7497:
1.964 droeschl 7498: # Don't add anything more if only_body wanted or in const space
7499: return $result if $args->{'only_body'}
7500: || $env{'request.state'} eq 'construct';
1.813 bisitz 7501:
7502: #Breadcrumbs
1.758 kaisler 7503: if (exists($args->{'bread_crumbs'}) or exists($args->{'bread_crumbs_component'})) {
7504: &Apache::lonhtmlcommon::clear_breadcrumbs();
7505: #if any br links exists, add them to the breadcrumbs
7506: if (exists($args->{'bread_crumbs'}) and ref($args->{'bread_crumbs'}) eq 'ARRAY') {
7507: foreach my $crumb (@{$args->{'bread_crumbs'}}){
7508: &Apache::lonhtmlcommon::add_breadcrumb($crumb);
7509: }
7510: }
1.1096 raeburn 7511: # if @advtools array contains items add then to the breadcrumbs
7512: if (@advtools > 0) {
7513: &Apache::lonmenu::advtools_crumbs(@advtools);
7514: }
1.758 kaisler 7515:
7516: #if bread_crumbs_component exists show it as headline else show only the breadcrumbs
7517: if(exists($args->{'bread_crumbs_component'})){
7518: $result .= &Apache::lonhtmlcommon::breadcrumbs($args->{'bread_crumbs_component'});
7519: }else{
7520: $result .= &Apache::lonhtmlcommon::breadcrumbs();
7521: }
1.320 albertel 7522: }
1.315 albertel 7523: return $result;
1.306 albertel 7524: }
7525:
7526: sub end_page {
1.315 albertel 7527: my ($args) = @_;
7528: $env{'internal.end_page'}++;
1.330 albertel 7529: my $result;
1.335 albertel 7530: if ($args->{'discussion'}) {
7531: my ($target,$parser);
7532: if (ref($args->{'discussion'})) {
7533: ($target,$parser) =($args->{'discussion'}{'target'},
7534: $args->{'discussion'}{'parser'});
7535: }
7536: $result .= &Apache::lonxml::xmlend($target,$parser);
7537: }
1.330 albertel 7538: if ($args->{'frameset'}) {
7539: $result .= '</frameset>';
7540: } else {
1.635 raeburn 7541: $result .= &endbodytag($args);
1.330 albertel 7542: }
1.1080 raeburn 7543: unless ($args->{'notbody'}) {
7544: $result .= "\n</html>";
7545: }
1.330 albertel 7546:
1.315 albertel 7547: if ($args->{'js_ready'}) {
1.317 albertel 7548: $result = &js_ready($result);
1.315 albertel 7549: }
1.335 albertel 7550:
1.320 albertel 7551: if ($args->{'html_encode'}) {
7552: $result = &html_encode($result);
7553: }
1.335 albertel 7554:
1.315 albertel 7555: return $result;
7556: }
7557:
1.1034 www 7558: sub wishlist_window {
7559: return(<<'ENDWISHLIST');
1.1046 raeburn 7560: <script type="text/javascript">
1.1034 www 7561: // <![CDATA[
7562: // <!-- BEGIN LON-CAPA Internal
7563: function set_wishlistlink(title, path) {
7564: if (!title) {
7565: title = document.title;
7566: title = title.replace(/^LON-CAPA /,'');
7567: }
7568: if (!path) {
7569: path = location.pathname;
7570: }
7571: Win = window.open('/adm/wishlist?mode=newLink&setTitle='+title+'&setPath='+path,
7572: 'wishlistNewLink','width=560,height=350,scrollbars=0');
7573: }
7574: // END LON-CAPA Internal -->
7575: // ]]>
7576: </script>
7577: ENDWISHLIST
7578: }
7579:
1.1030 www 7580: sub modal_window {
7581: return(<<'ENDMODAL');
1.1046 raeburn 7582: <script type="text/javascript">
1.1030 www 7583: // <![CDATA[
7584: // <!-- BEGIN LON-CAPA Internal
7585: var modalWindow = {
7586: parent:"body",
7587: windowId:null,
7588: content:null,
7589: width:null,
7590: height:null,
7591: close:function()
7592: {
7593: $(".LCmodal-window").remove();
7594: $(".LCmodal-overlay").remove();
7595: },
7596: open:function()
7597: {
7598: var modal = "";
7599: modal += "<div class=\"LCmodal-overlay\"></div>";
7600: modal += "<div id=\"" + this.windowId + "\" class=\"LCmodal-window\" style=\"width:" + this.width + "px; height:" + this.height + "px; margin-top:-" + (this.height / 2) + "px; margin-left:-" + (this.width / 2) + "px;\">";
7601: modal += this.content;
7602: modal += "</div>";
7603:
7604: $(this.parent).append(modal);
7605:
7606: $(".LCmodal-window").append("<a class=\"LCclose-window\"></a>");
7607: $(".LCclose-window").click(function(){modalWindow.close();});
7608: $(".LCmodal-overlay").click(function(){modalWindow.close();});
7609: }
7610: };
1.1031 www 7611: var openMyModal = function(source,width,height,scrolling)
1.1030 www 7612: {
7613: modalWindow.windowId = "myModal";
7614: modalWindow.width = width;
7615: modalWindow.height = height;
1.1031 www 7616: modalWindow.content = "<iframe width='"+width+"' height='"+height+"' frameborder='0' scrolling='"+scrolling+"' allowtransparency='true' src='" + source + "'></iframe>";
1.1030 www 7617: modalWindow.open();
7618: };
7619: // END LON-CAPA Internal -->
7620: // ]]>
7621: </script>
7622: ENDMODAL
7623: }
7624:
7625: sub modal_link {
1.1052 www 7626: my ($link,$linktext,$width,$height,$target,$scrolling,$title)=@_;
1.1030 www 7627: unless ($width) { $width=480; }
7628: unless ($height) { $height=400; }
1.1031 www 7629: unless ($scrolling) { $scrolling='yes'; }
1.1074 raeburn 7630: my $target_attr;
7631: if (defined($target)) {
7632: $target_attr = 'target="'.$target.'"';
7633: }
7634: return <<"ENDLINK";
7635: <a href="$link" $target_attr title="$title" onclick="javascript:openMyModal('$link',$width,$height,'$scrolling'); return false;">
7636: $linktext</a>
7637: ENDLINK
1.1030 www 7638: }
7639:
1.1032 www 7640: sub modal_adhoc_script {
7641: my ($funcname,$width,$height,$content)=@_;
7642: return (<<ENDADHOC);
1.1046 raeburn 7643: <script type="text/javascript">
1.1032 www 7644: // <![CDATA[
7645: var $funcname = function()
7646: {
7647: modalWindow.windowId = "myModal";
7648: modalWindow.width = $width;
7649: modalWindow.height = $height;
7650: modalWindow.content = '$content';
7651: modalWindow.open();
7652: };
7653: // ]]>
7654: </script>
7655: ENDADHOC
7656: }
7657:
1.1041 www 7658: sub modal_adhoc_inner {
7659: my ($funcname,$width,$height,$content)=@_;
7660: my $innerwidth=$width-20;
7661: $content=&js_ready(
1.1042 www 7662: &start_page('Dialog',undef,{'only_body'=>1,'bgcolor'=>'#FFFFFF'}).
1.1041 www 7663: &start_scrollbox($width.'px',$innerwidth.'px',$height.'px').
7664: $content.
7665: &end_scrollbox().
7666: &end_page()
7667: );
7668: return &modal_adhoc_script($funcname,$width,$height,$content);
7669: }
7670:
7671: sub modal_adhoc_window {
7672: my ($funcname,$width,$height,$content,$linktext)=@_;
7673: return &modal_adhoc_inner($funcname,$width,$height,$content).
7674: "<a href=\"javascript:$funcname();void(0);\">".$linktext."</a>";
7675: }
7676:
7677: sub modal_adhoc_launch {
7678: my ($funcname,$width,$height,$content)=@_;
7679: return &modal_adhoc_inner($funcname,$width,$height,$content).(<<ENDLAUNCH);
7680: <script type="text/javascript">
7681: // <![CDATA[
7682: $funcname();
7683: // ]]>
7684: </script>
7685: ENDLAUNCH
7686: }
7687:
7688: sub modal_adhoc_close {
7689: return (<<ENDCLOSE);
7690: <script type="text/javascript">
7691: // <![CDATA[
7692: modalWindow.close();
7693: // ]]>
7694: </script>
7695: ENDCLOSE
7696: }
7697:
1.1038 www 7698: sub togglebox_script {
7699: return(<<ENDTOGGLE);
7700: <script type="text/javascript">
7701: // <![CDATA[
7702: function LCtoggleDisplay(id,hidetext,showtext) {
7703: link = document.getElementById(id + "link").childNodes[0];
7704: with (document.getElementById(id).style) {
7705: if (display == "none" ) {
7706: display = "inline";
7707: link.nodeValue = hidetext;
7708: } else {
7709: display = "none";
7710: link.nodeValue = showtext;
7711: }
7712: }
7713: }
7714: // ]]>
7715: </script>
7716: ENDTOGGLE
7717: }
7718:
1.1039 www 7719: sub start_togglebox {
7720: my ($id,$heading,$headerbg,$hidetext,$showtext)=@_;
7721: unless ($heading) { $heading=''; } else { $heading.=' '; }
7722: unless ($showtext) { $showtext=&mt('show'); }
7723: unless ($hidetext) { $hidetext=&mt('hide'); }
7724: unless ($headerbg) { $headerbg='#FFFFFF'; }
7725: return &start_data_table().
7726: &start_data_table_header_row().
7727: '<td bgcolor="'.$headerbg.'">'.$heading.
7728: '[<a id="'.$id.'link" href="javascript:LCtoggleDisplay(\''.$id.'\',\''.$hidetext.'\',\''.
7729: $showtext.'\')">'.$showtext.'</a>]</td>'.
7730: &end_data_table_header_row().
7731: '<tr id="'.$id.'" style="display:none""><td>';
7732: }
7733:
7734: sub end_togglebox {
7735: return '</td></tr>'.&end_data_table();
7736: }
7737:
1.1041 www 7738: sub LCprogressbar_script {
1.1045 www 7739: my ($id)=@_;
1.1041 www 7740: return(<<ENDPROGRESS);
7741: <script type="text/javascript">
7742: // <![CDATA[
1.1045 www 7743: \$('#progressbar$id').progressbar({
1.1041 www 7744: value: 0,
7745: change: function(event, ui) {
7746: var newVal = \$(this).progressbar('option', 'value');
7747: \$('.pblabel', this).text(LCprogressTxt);
7748: }
7749: });
7750: // ]]>
7751: </script>
7752: ENDPROGRESS
7753: }
7754:
7755: sub LCprogressbarUpdate_script {
7756: return(<<ENDPROGRESSUPDATE);
7757: <style type="text/css">
7758: .ui-progressbar { position:relative; }
7759: .pblabel { position: absolute; width: 100%; text-align: center; line-height: 1.9em; }
7760: </style>
7761: <script type="text/javascript">
7762: // <![CDATA[
1.1045 www 7763: var LCprogressTxt='---';
7764:
7765: function LCupdateProgress(percent,progresstext,id) {
1.1041 www 7766: LCprogressTxt=progresstext;
1.1045 www 7767: \$('#progressbar'+id).progressbar('value',percent);
1.1041 www 7768: }
7769: // ]]>
7770: </script>
7771: ENDPROGRESSUPDATE
7772: }
7773:
1.1042 www 7774: my $LClastpercent;
1.1045 www 7775: my $LCidcnt;
7776: my $LCcurrentid;
1.1042 www 7777:
1.1041 www 7778: sub LCprogressbar {
1.1042 www 7779: my ($r)=(@_);
7780: $LClastpercent=0;
1.1045 www 7781: $LCidcnt++;
7782: $LCcurrentid=$$.'_'.$LCidcnt;
1.1041 www 7783: my $starting=&mt('Starting');
7784: my $content=(<<ENDPROGBAR);
7785: <p>
1.1045 www 7786: <div id="progressbar$LCcurrentid">
1.1041 www 7787: <span class="pblabel">$starting</span>
7788: </div>
7789: </p>
7790: ENDPROGBAR
1.1045 www 7791: &r_print($r,$content.&LCprogressbar_script($LCcurrentid));
1.1041 www 7792: }
7793:
7794: sub LCprogressbarUpdate {
1.1042 www 7795: my ($r,$val,$text)=@_;
7796: unless ($val) {
7797: if ($LClastpercent) {
7798: $val=$LClastpercent;
7799: } else {
7800: $val=0;
7801: }
7802: }
1.1041 www 7803: if ($val<0) { $val=0; }
7804: if ($val>100) { $val=0; }
1.1042 www 7805: $LClastpercent=$val;
1.1041 www 7806: unless ($text) { $text=$val.'%'; }
7807: $text=&js_ready($text);
1.1044 www 7808: &r_print($r,<<ENDUPDATE);
1.1041 www 7809: <script type="text/javascript">
7810: // <![CDATA[
1.1045 www 7811: LCupdateProgress($val,'$text','$LCcurrentid');
1.1041 www 7812: // ]]>
7813: </script>
7814: ENDUPDATE
1.1035 www 7815: }
7816:
1.1042 www 7817: sub LCprogressbarClose {
7818: my ($r)=@_;
7819: $LClastpercent=0;
1.1044 www 7820: &r_print($r,<<ENDCLOSE);
1.1042 www 7821: <script type="text/javascript">
7822: // <![CDATA[
1.1045 www 7823: \$("#progressbar$LCcurrentid").hide('slow');
1.1042 www 7824: // ]]>
7825: </script>
7826: ENDCLOSE
1.1044 www 7827: }
7828:
7829: sub r_print {
7830: my ($r,$to_print)=@_;
7831: if ($r) {
7832: $r->print($to_print);
7833: $r->rflush();
7834: } else {
7835: print($to_print);
7836: }
1.1042 www 7837: }
7838:
1.320 albertel 7839: sub html_encode {
7840: my ($result) = @_;
7841:
1.322 albertel 7842: $result = &HTML::Entities::encode($result,'<>&"');
1.320 albertel 7843:
7844: return $result;
7845: }
1.1044 www 7846:
1.317 albertel 7847: sub js_ready {
7848: my ($result) = @_;
7849:
1.323 albertel 7850: $result =~ s/[\n\r]/ /xmsg;
7851: $result =~ s/\\/\\\\/xmsg;
7852: $result =~ s/'/\\'/xmsg;
1.372 albertel 7853: $result =~ s{</}{<\\/}xmsg;
1.317 albertel 7854:
7855: return $result;
7856: }
7857:
1.315 albertel 7858: sub validate_page {
7859: if ( exists($env{'internal.start_page'})
1.316 albertel 7860: && $env{'internal.start_page'} > 1) {
7861: &Apache::lonnet::logthis('start_page called multiple times '.
1.318 albertel 7862: $env{'internal.start_page'}.' '.
1.316 albertel 7863: $ENV{'request.filename'});
1.315 albertel 7864: }
7865: if ( exists($env{'internal.end_page'})
1.316 albertel 7866: && $env{'internal.end_page'} > 1) {
7867: &Apache::lonnet::logthis('end_page called multiple times '.
1.318 albertel 7868: $env{'internal.end_page'}.' '.
1.316 albertel 7869: $env{'request.filename'});
1.315 albertel 7870: }
7871: if ( exists($env{'internal.start_page'})
7872: && ! exists($env{'internal.end_page'})) {
1.316 albertel 7873: &Apache::lonnet::logthis('start_page called without end_page '.
7874: $env{'request.filename'});
1.315 albertel 7875: }
7876: if ( ! exists($env{'internal.start_page'})
7877: && exists($env{'internal.end_page'})) {
1.316 albertel 7878: &Apache::lonnet::logthis('end_page called without start_page'.
7879: $env{'request.filename'});
1.315 albertel 7880: }
1.306 albertel 7881: }
1.315 albertel 7882:
1.996 www 7883:
7884: sub start_scrollbox {
1.1075 raeburn 7885: my ($outerwidth,$width,$height,$id,$bgcolor)=@_;
1.998 raeburn 7886: unless ($outerwidth) { $outerwidth='520px'; }
7887: unless ($width) { $width='500px'; }
7888: unless ($height) { $height='200px'; }
1.1075 raeburn 7889: my ($table_id,$div_id,$tdcol);
1.1018 raeburn 7890: if ($id ne '') {
1.1020 raeburn 7891: $table_id = " id='table_$id'";
7892: $div_id = " id='div_$id'";
1.1018 raeburn 7893: }
1.1075 raeburn 7894: if ($bgcolor ne '') {
7895: $tdcol = "background-color: $bgcolor;";
7896: }
7897: return <<"END";
7898: <table style="width: $outerwidth; border: 1px solid none;"$table_id><tr><td style="width: $width;$tdcol"><div style="overflow:auto; width:$width; height: $height;"$div_id>
7899: END
1.996 www 7900: }
7901:
7902: sub end_scrollbox {
1.1036 www 7903: return '</div></td></tr></table>';
1.996 www 7904: }
7905:
1.318 albertel 7906: sub simple_error_page {
7907: my ($r,$title,$msg) = @_;
7908: my $page =
7909: &Apache::loncommon::start_page($title).
1.1097 bisitz 7910: '<p class="LC_error">'.&mt($msg).'</p>'.
1.318 albertel 7911: &Apache::loncommon::end_page();
7912: if (ref($r)) {
7913: $r->print($page);
1.327 albertel 7914: return;
1.318 albertel 7915: }
7916: return $page;
7917: }
1.347 albertel 7918:
7919: {
1.610 albertel 7920: my @row_count;
1.961 onken 7921:
7922: sub start_data_table_count {
7923: unshift(@row_count, 0);
7924: return;
7925: }
7926:
7927: sub end_data_table_count {
7928: shift(@row_count);
7929: return;
7930: }
7931:
1.347 albertel 7932: sub start_data_table {
1.1018 raeburn 7933: my ($add_class,$id) = @_;
1.422 albertel 7934: my $css_class = (join(' ','LC_data_table',$add_class));
1.1018 raeburn 7935: my $table_id;
7936: if (defined($id)) {
7937: $table_id = ' id="'.$id.'"';
7938: }
1.961 onken 7939: &start_data_table_count();
1.1018 raeburn 7940: return '<table class="'.$css_class.'"'.$table_id.'>'."\n";
1.347 albertel 7941: }
7942:
7943: sub end_data_table {
1.961 onken 7944: &end_data_table_count();
1.389 albertel 7945: return '</table>'."\n";;
1.347 albertel 7946: }
7947:
7948: sub start_data_table_row {
1.974 wenzelju 7949: my ($add_class, $id) = @_;
1.610 albertel 7950: $row_count[0]++;
7951: my $css_class = ($row_count[0] % 2)?'LC_odd_row':'LC_even_row';
1.900 bisitz 7952: $css_class = (join(' ',$css_class,$add_class)) unless ($add_class eq '');
1.974 wenzelju 7953: $id = (' id="'.$id.'"') unless ($id eq '');
7954: return '<tr class="'.$css_class.'"'.$id.'>'."\n";
1.347 albertel 7955: }
1.471 banghart 7956:
7957: sub continue_data_table_row {
1.974 wenzelju 7958: my ($add_class, $id) = @_;
1.610 albertel 7959: my $css_class = ($row_count[0] % 2)?'LC_odd_row':'LC_even_row';
1.974 wenzelju 7960: $css_class = (join(' ',$css_class,$add_class)) unless ($add_class eq '');
7961: $id = (' id="'.$id.'"') unless ($id eq '');
7962: return '<tr class="'.$css_class.'"'.$id.'>'."\n";
1.471 banghart 7963: }
1.347 albertel 7964:
7965: sub end_data_table_row {
1.389 albertel 7966: return '</tr>'."\n";;
1.347 albertel 7967: }
1.367 www 7968:
1.421 albertel 7969: sub start_data_table_empty_row {
1.707 bisitz 7970: # $row_count[0]++;
1.421 albertel 7971: return '<tr class="LC_empty_row" >'."\n";;
7972: }
7973:
7974: sub end_data_table_empty_row {
7975: return '</tr>'."\n";;
7976: }
7977:
1.367 www 7978: sub start_data_table_header_row {
1.389 albertel 7979: return '<tr class="LC_header_row">'."\n";;
1.367 www 7980: }
7981:
7982: sub end_data_table_header_row {
1.389 albertel 7983: return '</tr>'."\n";;
1.367 www 7984: }
1.890 droeschl 7985:
7986: sub data_table_caption {
7987: my $caption = shift;
7988: return "<caption class=\"LC_caption\">$caption</caption>";
7989: }
1.347 albertel 7990: }
7991:
1.548 albertel 7992: =pod
7993:
7994: =item * &inhibit_menu_check($arg)
7995:
7996: Checks for a inhibitmenu state and generates output to preserve it
7997:
7998: Inputs: $arg - can be any of
7999: - undef - in which case the return value is a string
8000: to add into arguments list of a uri
8001: - 'input' - in which case the return value is a HTML
8002: <form> <input> field of type hidden to
8003: preserve the value
8004: - a url - in which case the return value is the url with
8005: the neccesary cgi args added to preserve the
8006: inhibitmenu state
8007: - a ref to a url - no return value, but the string is
8008: updated to include the neccessary cgi
8009: args to preserve the inhibitmenu state
8010:
8011: =cut
8012:
8013: sub inhibit_menu_check {
8014: my ($arg) = @_;
8015: &get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['inhibitmenu']);
8016: if ($arg eq 'input') {
8017: if ($env{'form.inhibitmenu'}) {
8018: return '<input type="hidden" name="inhibitmenu" value="'.$env{'form.inhibitmenu'}.'" />';
8019: } else {
8020: return
8021: }
8022: }
8023: if ($env{'form.inhibitmenu'}) {
8024: if (ref($arg)) {
8025: $$arg .= '?inhibitmenu='.$env{'form.inhibitmenu'};
8026: } elsif ($arg eq '') {
8027: $arg .= 'inhibitmenu='.$env{'form.inhibitmenu'};
8028: } else {
8029: $arg .= '?inhibitmenu='.$env{'form.inhibitmenu'};
8030: }
8031: }
8032: if (!ref($arg)) {
8033: return $arg;
8034: }
8035: }
8036:
1.251 albertel 8037: ###############################################
1.182 matthew 8038:
8039: =pod
8040:
1.549 albertel 8041: =back
8042:
8043: =head1 User Information Routines
8044:
8045: =over 4
8046:
1.405 albertel 8047: =item * &get_users_function()
1.182 matthew 8048:
8049: Used by &bodytag to determine the current users primary role.
8050: Returns either 'student','coordinator','admin', or 'author'.
8051:
8052: =cut
8053:
8054: ###############################################
8055: sub get_users_function {
1.815 tempelho 8056: my $function = 'norole';
1.818 tempelho 8057: if ($env{'request.role'}=~/^(st)/) {
8058: $function='student';
8059: }
1.907 raeburn 8060: if ($env{'request.role'}=~/^(cc|co|in|ta|ep)/) {
1.182 matthew 8061: $function='coordinator';
8062: }
1.258 albertel 8063: if ($env{'request.role'}=~/^(su|dc|ad|li)/) {
1.182 matthew 8064: $function='admin';
8065: }
1.826 bisitz 8066: if (($env{'request.role'}=~/^(au|ca|aa)/) ||
1.1025 raeburn 8067: ($ENV{'REQUEST_URI'}=~ m{/^(/priv)})) {
1.182 matthew 8068: $function='author';
8069: }
8070: return $function;
1.54 www 8071: }
1.99 www 8072:
8073: ###############################################
8074:
1.233 raeburn 8075: =pod
8076:
1.821 raeburn 8077: =item * &show_course()
8078:
8079: Used by lonmenu.pm and lonroles.pm to determine whether to use the word
8080: 'Courses' or 'Roles' in inline navigation and on screen displaying user's roles.
8081:
8082: Inputs:
8083: None
8084:
8085: Outputs:
8086: Scalar: 1 if 'Course' to be used, 0 otherwise.
8087:
8088: =cut
8089:
8090: ###############################################
8091: sub show_course {
8092: my $course = !$env{'user.adv'};
8093: if (!$env{'user.adv'}) {
8094: foreach my $env (keys(%env)) {
8095: next if ($env !~ m/^user\.priv\./);
8096: if ($env !~ m/^user\.priv\.(?:st|cm)/) {
8097: $course = 0;
8098: last;
8099: }
8100: }
8101: }
8102: return $course;
8103: }
8104:
8105: ###############################################
8106:
8107: =pod
8108:
1.542 raeburn 8109: =item * &check_user_status()
1.274 raeburn 8110:
8111: Determines current status of supplied role for a
8112: specific user. Roles can be active, previous or future.
8113:
8114: Inputs:
8115: user's domain, user's username, course's domain,
1.375 raeburn 8116: course's number, optional section ID.
1.274 raeburn 8117:
8118: Outputs:
8119: role status: active, previous or future.
8120:
8121: =cut
8122:
8123: sub check_user_status {
1.412 raeburn 8124: my ($udom,$uname,$cdom,$crs,$role,$sec) = @_;
1.1073 raeburn 8125: my %userinfo = &Apache::lonnet::dump('roles',$udom,$uname);
1.274 raeburn 8126: my @uroles = keys %userinfo;
8127: my $srchstr;
8128: my $active_chk = 'none';
1.412 raeburn 8129: my $now = time;
1.274 raeburn 8130: if (@uroles > 0) {
1.908 raeburn 8131: if (($role eq 'cc') || ($role eq 'co') || ($sec eq '') || (!defined($sec))) {
1.274 raeburn 8132: $srchstr = '/'.$cdom.'/'.$crs.'_'.$role;
8133: } else {
1.412 raeburn 8134: $srchstr = '/'.$cdom.'/'.$crs.'/'.$sec.'_'.$role;
8135: }
8136: if (grep/^\Q$srchstr\E$/,@uroles) {
1.274 raeburn 8137: my $role_end = 0;
8138: my $role_start = 0;
8139: $active_chk = 'active';
1.412 raeburn 8140: if ($userinfo{$srchstr} =~ m/^\Q$role\E_(\d+)/) {
8141: $role_end = $1;
8142: if ($userinfo{$srchstr} =~ m/^\Q$role\E_\Q$role_end\E_(\d+)$/) {
8143: $role_start = $1;
1.274 raeburn 8144: }
8145: }
8146: if ($role_start > 0) {
1.412 raeburn 8147: if ($now < $role_start) {
1.274 raeburn 8148: $active_chk = 'future';
8149: }
8150: }
8151: if ($role_end > 0) {
1.412 raeburn 8152: if ($now > $role_end) {
1.274 raeburn 8153: $active_chk = 'previous';
8154: }
8155: }
8156: }
8157: }
8158: return $active_chk;
8159: }
8160:
8161: ###############################################
8162:
8163: =pod
8164:
1.405 albertel 8165: =item * &get_sections()
1.233 raeburn 8166:
8167: Determines all the sections for a course including
8168: sections with students and sections containing other roles.
1.419 raeburn 8169: Incoming parameters:
8170:
8171: 1. domain
8172: 2. course number
8173: 3. reference to array containing roles for which sections should
8174: be gathered (optional).
8175: 4. reference to array containing status types for which sections
8176: should be gathered (optional).
8177:
8178: If the third argument is undefined, sections are gathered for any role.
8179: If the fourth argument is undefined, sections are gathered for any status.
8180: Permissible values are 'active' or 'future' or 'previous'.
1.233 raeburn 8181:
1.374 raeburn 8182: Returns section hash (keys are section IDs, values are
8183: number of users in each section), subject to the
1.419 raeburn 8184: optional roles filter, optional status filter
1.233 raeburn 8185:
8186: =cut
8187:
8188: ###############################################
8189: sub get_sections {
1.419 raeburn 8190: my ($cdom,$cnum,$possible_roles,$possible_status) = @_;
1.366 albertel 8191: if (!defined($cdom) || !defined($cnum)) {
8192: my $cid = $env{'request.course.id'};
8193:
8194: return if (!defined($cid));
8195:
8196: $cdom = $env{'course.'.$cid.'.domain'};
8197: $cnum = $env{'course.'.$cid.'.num'};
8198: }
8199:
8200: my %sectioncount;
1.419 raeburn 8201: my $now = time;
1.240 albertel 8202:
1.366 albertel 8203: if (!defined($possible_roles) || (grep(/^st$/,@$possible_roles))) {
1.276 albertel 8204: my ($classlist) = &Apache::loncoursedata::get_classlist($cdom,$cnum);
1.240 albertel 8205: my $sec_index = &Apache::loncoursedata::CL_SECTION();
8206: my $status_index = &Apache::loncoursedata::CL_STATUS();
1.419 raeburn 8207: my $start_index = &Apache::loncoursedata::CL_START();
8208: my $end_index = &Apache::loncoursedata::CL_END();
8209: my $status;
1.366 albertel 8210: while (my ($student,$data) = each(%$classlist)) {
1.419 raeburn 8211: my ($section,$stu_status,$start,$end) = ($data->[$sec_index],
8212: $data->[$status_index],
8213: $data->[$start_index],
8214: $data->[$end_index]);
8215: if ($stu_status eq 'Active') {
8216: $status = 'active';
8217: } elsif ($end < $now) {
8218: $status = 'previous';
8219: } elsif ($start > $now) {
8220: $status = 'future';
8221: }
8222: if ($section ne '-1' && $section !~ /^\s*$/) {
8223: if ((!defined($possible_status)) || (($status ne '') &&
8224: (grep/^\Q$status\E$/,@{$possible_status}))) {
8225: $sectioncount{$section}++;
8226: }
1.240 albertel 8227: }
8228: }
8229: }
8230: my %courseroles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
8231: foreach my $user (sort(keys(%courseroles))) {
8232: if ($user !~ /^(\w{2})/) { next; }
8233: my ($role) = ($user =~ /^(\w{2})/);
8234: if ($possible_roles && !(grep(/^$role$/,@$possible_roles))) { next; }
1.419 raeburn 8235: my ($section,$status);
1.240 albertel 8236: if ($role eq 'cr' &&
8237: $user =~ m-^$role/[^/]*/[^/]*/[^/]*:[^:]*:[^:]*:(\w+)-) {
8238: $section=$1;
8239: }
8240: if ($user =~ /^$role:[^:]*:[^:]*:(\w+)/) { $section=$1; }
8241: if (!defined($section) || $section eq '-1') { next; }
1.419 raeburn 8242: my ($end,$start) = ($courseroles{$user} =~ /^([^:]*):([^:]*)$/);
8243: if ($end == -1 && $start == -1) {
8244: next; #deleted role
8245: }
8246: if (!defined($possible_status)) {
8247: $sectioncount{$section}++;
8248: } else {
8249: if ((!$end || $end >= $now) && (!$start || $start <= $now)) {
8250: $status = 'active';
8251: } elsif ($end < $now) {
8252: $status = 'future';
8253: } elsif ($start > $now) {
8254: $status = 'previous';
8255: }
8256: if (($status ne '') && (grep/^\Q$status\E$/,@{$possible_status})) {
8257: $sectioncount{$section}++;
8258: }
8259: }
1.233 raeburn 8260: }
1.366 albertel 8261: return %sectioncount;
1.233 raeburn 8262: }
8263:
1.274 raeburn 8264: ###############################################
1.294 raeburn 8265:
8266: =pod
1.405 albertel 8267:
8268: =item * &get_course_users()
8269:
1.275 raeburn 8270: Retrieves usernames:domains for users in the specified course
8271: with specific role(s), and access status.
8272:
8273: Incoming parameters:
1.277 albertel 8274: 1. course domain
8275: 2. course number
8276: 3. access status: users must have - either active,
1.275 raeburn 8277: previous, future, or all.
1.277 albertel 8278: 4. reference to array of permissible roles
1.288 raeburn 8279: 5. reference to array of section restrictions (optional)
8280: 6. reference to results object (hash of hashes).
8281: 7. reference to optional userdata hash
1.609 raeburn 8282: 8. reference to optional statushash
1.630 raeburn 8283: 9. flag if privileged users (except those set to unhide in
8284: course settings) should be excluded
1.609 raeburn 8285: Keys of top level results hash are roles.
1.275 raeburn 8286: Keys of inner hashes are username:domain, with
8287: values set to access type.
1.288 raeburn 8288: Optional userdata hash returns an array with arguments in the
8289: same order as loncoursedata::get_classlist() for student data.
8290:
1.609 raeburn 8291: Optional statushash returns
8292:
1.288 raeburn 8293: Entries for end, start, section and status are blank because
8294: of the possibility of multiple values for non-student roles.
8295:
1.275 raeburn 8296: =cut
1.405 albertel 8297:
1.275 raeburn 8298: ###############################################
1.405 albertel 8299:
1.275 raeburn 8300: sub get_course_users {
1.630 raeburn 8301: my ($cdom,$cnum,$types,$roles,$sections,$users,$userdata,$statushash,$hidepriv) = @_;
1.288 raeburn 8302: my %idx = ();
1.419 raeburn 8303: my %seclists;
1.288 raeburn 8304:
8305: $idx{udom} = &Apache::loncoursedata::CL_SDOM();
8306: $idx{uname} = &Apache::loncoursedata::CL_SNAME();
8307: $idx{end} = &Apache::loncoursedata::CL_END();
8308: $idx{start} = &Apache::loncoursedata::CL_START();
8309: $idx{id} = &Apache::loncoursedata::CL_ID();
8310: $idx{section} = &Apache::loncoursedata::CL_SECTION();
8311: $idx{fullname} = &Apache::loncoursedata::CL_FULLNAME();
8312: $idx{status} = &Apache::loncoursedata::CL_STATUS();
8313:
1.290 albertel 8314: if (grep(/^st$/,@{$roles})) {
1.276 albertel 8315: my ($classlist,$keylist)=&Apache::loncoursedata::get_classlist($cdom,$cnum);
1.278 raeburn 8316: my $now = time;
1.277 albertel 8317: foreach my $student (keys(%{$classlist})) {
1.288 raeburn 8318: my $match = 0;
1.412 raeburn 8319: my $secmatch = 0;
1.419 raeburn 8320: my $section = $$classlist{$student}[$idx{section}];
1.609 raeburn 8321: my $status = $$classlist{$student}[$idx{status}];
1.419 raeburn 8322: if ($section eq '') {
8323: $section = 'none';
8324: }
1.291 albertel 8325: if ((ref($sections) eq 'ARRAY') && (@{$sections} > 0)) {
1.420 albertel 8326: if (grep(/^all$/,@{$sections})) {
1.412 raeburn 8327: $secmatch = 1;
8328: } elsif ($$classlist{$student}[$idx{section}] eq '') {
1.420 albertel 8329: if (grep(/^none$/,@{$sections})) {
1.412 raeburn 8330: $secmatch = 1;
8331: }
8332: } else {
1.419 raeburn 8333: if (grep(/^\Q$section\E$/,@{$sections})) {
1.412 raeburn 8334: $secmatch = 1;
8335: }
1.290 albertel 8336: }
1.412 raeburn 8337: if (!$secmatch) {
8338: next;
8339: }
1.419 raeburn 8340: }
1.275 raeburn 8341: if (defined($$types{'active'})) {
1.288 raeburn 8342: if ($$classlist{$student}[$idx{status}] eq 'Active') {
1.275 raeburn 8343: push(@{$$users{st}{$student}},'active');
1.288 raeburn 8344: $match = 1;
1.275 raeburn 8345: }
8346: }
8347: if (defined($$types{'previous'})) {
1.609 raeburn 8348: if ($$classlist{$student}[$idx{status}] eq 'Expired') {
1.275 raeburn 8349: push(@{$$users{st}{$student}},'previous');
1.288 raeburn 8350: $match = 1;
1.275 raeburn 8351: }
8352: }
8353: if (defined($$types{'future'})) {
1.609 raeburn 8354: if ($$classlist{$student}[$idx{status}] eq 'Future') {
1.275 raeburn 8355: push(@{$$users{st}{$student}},'future');
1.288 raeburn 8356: $match = 1;
1.275 raeburn 8357: }
8358: }
1.609 raeburn 8359: if ($match) {
8360: push(@{$seclists{$student}},$section);
8361: if (ref($userdata) eq 'HASH') {
8362: $$userdata{$student} = $$classlist{$student};
8363: }
8364: if (ref($statushash) eq 'HASH') {
8365: $statushash->{$student}{'st'}{$section} = $status;
8366: }
1.288 raeburn 8367: }
1.275 raeburn 8368: }
8369: }
1.412 raeburn 8370: if ((@{$roles} > 1) || ((@{$roles} == 1) && ($$roles[0] ne "st"))) {
1.439 raeburn 8371: my %coursepersonnel = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
8372: my $now = time;
1.609 raeburn 8373: my %displaystatus = ( previous => 'Expired',
8374: active => 'Active',
8375: future => 'Future',
8376: );
1.630 raeburn 8377: my %nothide;
8378: if ($hidepriv) {
8379: my %coursehash=&Apache::lonnet::coursedescription($cdom.'_'.$cnum);
8380: foreach my $user (split(/\s*\,\s*/,$coursehash{'nothideprivileged'})) {
8381: if ($user !~ /:/) {
8382: $nothide{join(':',split(/[\@]/,$user))}=1;
8383: } else {
8384: $nothide{$user} = 1;
8385: }
8386: }
8387: }
1.439 raeburn 8388: foreach my $person (sort(keys(%coursepersonnel))) {
1.288 raeburn 8389: my $match = 0;
1.412 raeburn 8390: my $secmatch = 0;
1.439 raeburn 8391: my $status;
1.412 raeburn 8392: my ($role,$user,$usec) = ($person =~ /^([^:]*):([^:]+:[^:]+):([^:]*)/);
1.275 raeburn 8393: $user =~ s/:$//;
1.439 raeburn 8394: my ($end,$start) = split(/:/,$coursepersonnel{$person});
8395: if ($end == -1 || $start == -1) {
8396: next;
8397: }
8398: if (($role) && ((grep(/^\Q$role\E$/,@{$roles})) ||
8399: (grep(/^cr$/,@{$roles}) && $role =~ /^cr\//))) {
1.412 raeburn 8400: my ($uname,$udom) = split(/:/,$user);
8401: if ((ref($sections) eq 'ARRAY') && (@{$sections} > 0)) {
1.420 albertel 8402: if (grep(/^all$/,@{$sections})) {
1.412 raeburn 8403: $secmatch = 1;
8404: } elsif ($usec eq '') {
1.420 albertel 8405: if (grep(/^none$/,@{$sections})) {
1.412 raeburn 8406: $secmatch = 1;
8407: }
8408: } else {
8409: if (grep(/^\Q$usec\E$/,@{$sections})) {
8410: $secmatch = 1;
8411: }
8412: }
8413: if (!$secmatch) {
8414: next;
8415: }
1.288 raeburn 8416: }
1.419 raeburn 8417: if ($usec eq '') {
8418: $usec = 'none';
8419: }
1.275 raeburn 8420: if ($uname ne '' && $udom ne '') {
1.630 raeburn 8421: if ($hidepriv) {
8422: if ((&Apache::lonnet::privileged($uname,$udom)) &&
8423: (!$nothide{$uname.':'.$udom})) {
8424: next;
8425: }
8426: }
1.503 raeburn 8427: if ($end > 0 && $end < $now) {
1.439 raeburn 8428: $status = 'previous';
8429: } elsif ($start > $now) {
8430: $status = 'future';
8431: } else {
8432: $status = 'active';
8433: }
1.277 albertel 8434: foreach my $type (keys(%{$types})) {
1.275 raeburn 8435: if ($status eq $type) {
1.420 albertel 8436: if (!grep(/^\Q$type\E$/,@{$$users{$role}{$user}})) {
1.419 raeburn 8437: push(@{$$users{$role}{$user}},$type);
8438: }
1.288 raeburn 8439: $match = 1;
8440: }
8441: }
1.419 raeburn 8442: if (($match) && (ref($userdata) eq 'HASH')) {
8443: if (!exists($$userdata{$uname.':'.$udom})) {
8444: &get_user_info($udom,$uname,\%idx,$userdata);
8445: }
1.420 albertel 8446: if (!grep(/^\Q$usec\E$/,@{$seclists{$uname.':'.$udom}})) {
1.419 raeburn 8447: push(@{$seclists{$uname.':'.$udom}},$usec);
8448: }
1.609 raeburn 8449: if (ref($statushash) eq 'HASH') {
8450: $statushash->{$uname.':'.$udom}{$role}{$usec} = $displaystatus{$status};
8451: }
1.275 raeburn 8452: }
8453: }
8454: }
8455: }
1.290 albertel 8456: if (grep(/^ow$/,@{$roles})) {
1.279 raeburn 8457: if ((defined($cdom)) && (defined($cnum))) {
8458: my %csettings = &Apache::lonnet::get('environment',['internal.courseowner'],$cdom,$cnum);
8459: if ( defined($csettings{'internal.courseowner'}) ) {
8460: my $owner = $csettings{'internal.courseowner'};
1.609 raeburn 8461: next if ($owner eq '');
8462: my ($ownername,$ownerdom);
8463: if ($owner =~ /^([^:]+):([^:]+)$/) {
8464: $ownername = $1;
8465: $ownerdom = $2;
8466: } else {
8467: $ownername = $owner;
8468: $ownerdom = $cdom;
8469: $owner = $ownername.':'.$ownerdom;
1.439 raeburn 8470: }
8471: @{$$users{'ow'}{$owner}} = 'any';
1.290 albertel 8472: if (defined($userdata) &&
1.609 raeburn 8473: !exists($$userdata{$owner})) {
8474: &get_user_info($ownerdom,$ownername,\%idx,$userdata);
8475: if (!grep(/^none$/,@{$seclists{$owner}})) {
8476: push(@{$seclists{$owner}},'none');
8477: }
8478: if (ref($statushash) eq 'HASH') {
8479: $statushash->{$owner}{'ow'}{'none'} = 'Any';
1.419 raeburn 8480: }
1.290 albertel 8481: }
1.279 raeburn 8482: }
8483: }
8484: }
1.419 raeburn 8485: foreach my $user (keys(%seclists)) {
8486: @{$seclists{$user}} = (sort {$a <=> $b} @{$seclists{$user}});
8487: $$userdata{$user}[$idx{section}] = join(',',@{$seclists{$user}});
8488: }
1.275 raeburn 8489: }
8490: return;
8491: }
8492:
1.288 raeburn 8493: sub get_user_info {
8494: my ($udom,$uname,$idx,$userdata) = @_;
1.289 albertel 8495: $$userdata{$uname.':'.$udom}[$$idx{fullname}] =
8496: &plainname($uname,$udom,'lastname');
1.291 albertel 8497: $$userdata{$uname.':'.$udom}[$$idx{uname}] = $uname;
1.297 raeburn 8498: $$userdata{$uname.':'.$udom}[$$idx{udom}] = $udom;
1.609 raeburn 8499: my %idhash = &Apache::lonnet::idrget($udom,($uname));
8500: $$userdata{$uname.':'.$udom}[$$idx{id}] = $idhash{$uname};
1.288 raeburn 8501: return;
8502: }
1.275 raeburn 8503:
1.472 raeburn 8504: ###############################################
8505:
8506: =pod
8507:
8508: =item * &get_user_quota()
8509:
8510: Retrieves quota assigned for storage of portfolio files for a user
8511:
8512: Incoming parameters:
8513: 1. user's username
8514: 2. user's domain
8515:
8516: Returns:
1.536 raeburn 8517: 1. Disk quota (in Mb) assigned to student.
8518: 2. (Optional) Type of setting: custom or default
8519: (individually assigned or default for user's
8520: institutional status).
8521: 3. (Optional) - User's institutional status (e.g., faculty, staff
8522: or student - types as defined in localenroll::inst_usertypes
8523: for user's domain, which determines default quota for user.
8524: 4. (Optional) - Default quota which would apply to the user.
1.472 raeburn 8525:
8526: If a value has been stored in the user's environment,
1.536 raeburn 8527: it will return that, otherwise it returns the maximal default
8528: defined for the user's instituional status(es) in the domain.
1.472 raeburn 8529:
8530: =cut
8531:
8532: ###############################################
8533:
8534:
8535: sub get_user_quota {
8536: my ($uname,$udom) = @_;
1.536 raeburn 8537: my ($quota,$quotatype,$settingstatus,$defquota);
1.472 raeburn 8538: if (!defined($udom)) {
8539: $udom = $env{'user.domain'};
8540: }
8541: if (!defined($uname)) {
8542: $uname = $env{'user.name'};
8543: }
8544: if (($udom eq '' || $uname eq '') ||
8545: ($udom eq 'public') && ($uname eq 'public')) {
8546: $quota = 0;
1.536 raeburn 8547: $quotatype = 'default';
8548: $defquota = 0;
1.472 raeburn 8549: } else {
1.536 raeburn 8550: my $inststatus;
1.472 raeburn 8551: if ($udom eq $env{'user.domain'} && $uname eq $env{'user.name'}) {
8552: $quota = $env{'environment.portfolioquota'};
1.536 raeburn 8553: $inststatus = $env{'environment.inststatus'};
1.472 raeburn 8554: } else {
1.536 raeburn 8555: my %userenv =
8556: &Apache::lonnet::get('environment',['portfolioquota',
8557: 'inststatus'],$udom,$uname);
1.472 raeburn 8558: my ($tmp) = keys(%userenv);
8559: if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
8560: $quota = $userenv{'portfolioquota'};
1.536 raeburn 8561: $inststatus = $userenv{'inststatus'};
1.472 raeburn 8562: } else {
8563: undef(%userenv);
8564: }
8565: }
1.536 raeburn 8566: ($defquota,$settingstatus) = &default_quota($udom,$inststatus);
1.472 raeburn 8567: if ($quota eq '') {
1.536 raeburn 8568: $quota = $defquota;
8569: $quotatype = 'default';
8570: } else {
8571: $quotatype = 'custom';
1.472 raeburn 8572: }
8573: }
1.536 raeburn 8574: if (wantarray) {
8575: return ($quota,$quotatype,$settingstatus,$defquota);
8576: } else {
8577: return $quota;
8578: }
1.472 raeburn 8579: }
8580:
8581: ###############################################
8582:
8583: =pod
8584:
8585: =item * &default_quota()
8586:
1.536 raeburn 8587: Retrieves default quota assigned for storage of user portfolio files,
8588: given an (optional) user's institutional status.
1.472 raeburn 8589:
8590: Incoming parameters:
8591: 1. domain
1.536 raeburn 8592: 2. (Optional) institutional status(es). This is a : separated list of
8593: status types (e.g., faculty, staff, student etc.)
8594: which apply to the user for whom the default is being retrieved.
8595: If the institutional status string in undefined, the domain
8596: default quota will be returned.
1.472 raeburn 8597:
8598: Returns:
8599: 1. Default disk quota (in Mb) for user portfolios in the domain.
1.536 raeburn 8600: 2. (Optional) institutional type which determined the value of the
8601: default quota.
1.472 raeburn 8602:
8603: If a value has been stored in the domain's configuration db,
8604: it will return that, otherwise it returns 20 (for backwards
8605: compatibility with domains which have not set up a configuration
8606: db file; the original statically defined portfolio quota was 20 Mb).
8607:
1.536 raeburn 8608: If the user's status includes multiple types (e.g., staff and student),
8609: the largest default quota which applies to the user determines the
8610: default quota returned.
8611:
1.780 raeburn 8612: =back
8613:
1.472 raeburn 8614: =cut
8615:
8616: ###############################################
8617:
8618:
8619: sub default_quota {
1.536 raeburn 8620: my ($udom,$inststatus) = @_;
8621: my ($defquota,$settingstatus);
8622: my %quotahash = &Apache::lonnet::get_dom('configuration',
1.622 raeburn 8623: ['quotas'],$udom);
8624: if (ref($quotahash{'quotas'}) eq 'HASH') {
1.536 raeburn 8625: if ($inststatus ne '') {
1.765 raeburn 8626: my @statuses = map { &unescape($_); } split(/:/,$inststatus);
1.536 raeburn 8627: foreach my $item (@statuses) {
1.711 raeburn 8628: if (ref($quotahash{'quotas'}{'defaultquota'}) eq 'HASH') {
8629: if ($quotahash{'quotas'}{'defaultquota'}{$item} ne '') {
8630: if ($defquota eq '') {
8631: $defquota = $quotahash{'quotas'}{'defaultquota'}{$item};
8632: $settingstatus = $item;
8633: } elsif ($quotahash{'quotas'}{'defaultquota'}{$item} > $defquota) {
8634: $defquota = $quotahash{'quotas'}{'defaultquota'}{$item};
8635: $settingstatus = $item;
8636: }
8637: }
8638: } else {
8639: if ($quotahash{'quotas'}{$item} ne '') {
8640: if ($defquota eq '') {
8641: $defquota = $quotahash{'quotas'}{$item};
8642: $settingstatus = $item;
8643: } elsif ($quotahash{'quotas'}{$item} > $defquota) {
8644: $defquota = $quotahash{'quotas'}{$item};
8645: $settingstatus = $item;
8646: }
1.536 raeburn 8647: }
8648: }
8649: }
8650: }
8651: if ($defquota eq '') {
1.711 raeburn 8652: if (ref($quotahash{'quotas'}{'defaultquota'}) eq 'HASH') {
8653: $defquota = $quotahash{'quotas'}{'defaultquota'}{'default'};
8654: } else {
8655: $defquota = $quotahash{'quotas'}{'default'};
8656: }
1.536 raeburn 8657: $settingstatus = 'default';
8658: }
8659: } else {
8660: $settingstatus = 'default';
8661: $defquota = 20;
8662: }
8663: if (wantarray) {
8664: return ($defquota,$settingstatus);
1.472 raeburn 8665: } else {
1.536 raeburn 8666: return $defquota;
1.472 raeburn 8667: }
8668: }
8669:
1.384 raeburn 8670: sub get_secgrprole_info {
8671: my ($cdom,$cnum,$needroles,$type) = @_;
8672: my %sections_count = &get_sections($cdom,$cnum);
8673: my @sections = (sort {$a <=> $b} keys(%sections_count));
8674: my %curr_groups = &Apache::longroup::coursegroups($cdom,$cnum);
8675: my @groups = sort(keys(%curr_groups));
8676: my $allroles = [];
8677: my $rolehash;
8678: my $accesshash = {
8679: active => 'Currently has access',
8680: future => 'Will have future access',
8681: previous => 'Previously had access',
8682: };
8683: if ($needroles) {
8684: $rolehash = {'all' => 'all'};
1.385 albertel 8685: my %user_roles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
8686: if (&Apache::lonnet::error(%user_roles)) {
8687: undef(%user_roles);
8688: }
8689: foreach my $item (keys(%user_roles)) {
1.384 raeburn 8690: my ($role)=split(/\:/,$item,2);
8691: if ($role eq 'cr') { next; }
8692: if ($role =~ /^cr/) {
8693: $$rolehash{$role} = (split('/',$role))[3];
8694: } else {
8695: $$rolehash{$role} = &Apache::lonnet::plaintext($role,$type);
8696: }
8697: }
8698: foreach my $key (sort(keys(%{$rolehash}))) {
8699: push(@{$allroles},$key);
8700: }
8701: push (@{$allroles},'st');
8702: $$rolehash{'st'} = &Apache::lonnet::plaintext('st',$type);
8703: }
8704: return (\@sections,\@groups,$allroles,$rolehash,$accesshash);
8705: }
8706:
1.555 raeburn 8707: sub user_picker {
1.994 raeburn 8708: my ($dom,$srch,$forcenewuser,$caller,$cancreate,$usertype,$context) = @_;
1.555 raeburn 8709: my $currdom = $dom;
8710: my %curr_selected = (
8711: srchin => 'dom',
1.580 raeburn 8712: srchby => 'lastname',
1.555 raeburn 8713: );
8714: my $srchterm;
1.625 raeburn 8715: if ((ref($srch) eq 'HASH') && ($env{'form.origform'} ne 'crtusername')) {
1.555 raeburn 8716: if ($srch->{'srchby'} ne '') {
8717: $curr_selected{'srchby'} = $srch->{'srchby'};
8718: }
8719: if ($srch->{'srchin'} ne '') {
8720: $curr_selected{'srchin'} = $srch->{'srchin'};
8721: }
8722: if ($srch->{'srchtype'} ne '') {
8723: $curr_selected{'srchtype'} = $srch->{'srchtype'};
8724: }
8725: if ($srch->{'srchdomain'} ne '') {
8726: $currdom = $srch->{'srchdomain'};
8727: }
8728: $srchterm = $srch->{'srchterm'};
8729: }
8730: my %lt=&Apache::lonlocal::texthash(
1.573 raeburn 8731: 'usr' => 'Search criteria',
1.563 raeburn 8732: 'doma' => 'Domain/institution to search',
1.558 albertel 8733: 'uname' => 'username',
8734: 'lastname' => 'last name',
1.555 raeburn 8735: 'lastfirst' => 'last name, first name',
1.558 albertel 8736: 'crs' => 'in this course',
1.576 raeburn 8737: 'dom' => 'in selected LON-CAPA domain',
1.558 albertel 8738: 'alc' => 'all LON-CAPA',
1.573 raeburn 8739: 'instd' => 'in institutional directory for selected domain',
1.558 albertel 8740: 'exact' => 'is',
8741: 'contains' => 'contains',
1.569 raeburn 8742: 'begins' => 'begins with',
1.571 raeburn 8743: 'youm' => "You must include some text to search for.",
8744: 'thte' => "The text you are searching for must contain at least two characters when using a 'begins' type search.",
8745: 'thet' => "The text you are searching for must contain at least three characters when using a 'contains' type search.",
8746: 'yomc' => "You must choose a domain when using an institutional directory search.",
8747: 'ymcd' => "You must choose a domain when using a domain search.",
8748: 'whus' => "When using searching by last,first you must include a comma as separator between last name and first name.",
8749: 'whse' => "When searching by last,first you must include at least one character in the first name.",
8750: 'thfo' => "The following need to be corrected before the search can be run:",
1.555 raeburn 8751: );
1.563 raeburn 8752: my $domform = &select_dom_form($currdom,'srchdomain',1,1);
8753: my $srchinsel = ' <select name="srchin">';
1.555 raeburn 8754:
8755: my @srchins = ('crs','dom','alc','instd');
8756:
8757: foreach my $option (@srchins) {
8758: # FIXME 'alc' option unavailable until
8759: # loncreateuser::print_user_query_page()
8760: # has been completed.
8761: next if ($option eq 'alc');
1.880 raeburn 8762: next if (($option eq 'crs') && ($env{'form.form'} eq 'requestcrs'));
1.555 raeburn 8763: next if ($option eq 'crs' && !$env{'request.course.id'});
1.563 raeburn 8764: if ($curr_selected{'srchin'} eq $option) {
8765: $srchinsel .= '
8766: <option value="'.$option.'" selected="selected">'.$lt{$option}.'</option>';
8767: } else {
8768: $srchinsel .= '
8769: <option value="'.$option.'">'.$lt{$option}.'</option>';
8770: }
1.555 raeburn 8771: }
1.563 raeburn 8772: $srchinsel .= "\n </select>\n";
1.555 raeburn 8773:
8774: my $srchbysel = ' <select name="srchby">';
1.580 raeburn 8775: foreach my $option ('lastname','lastfirst','uname') {
1.555 raeburn 8776: if ($curr_selected{'srchby'} eq $option) {
8777: $srchbysel .= '
8778: <option value="'.$option.'" selected="selected">'.$lt{$option}.'</option>';
8779: } else {
8780: $srchbysel .= '
8781: <option value="'.$option.'">'.$lt{$option}.'</option>';
8782: }
8783: }
8784: $srchbysel .= "\n </select>\n";
8785:
8786: my $srchtypesel = ' <select name="srchtype">';
1.580 raeburn 8787: foreach my $option ('begins','contains','exact') {
1.555 raeburn 8788: if ($curr_selected{'srchtype'} eq $option) {
8789: $srchtypesel .= '
8790: <option value="'.$option.'" selected="selected">'.$lt{$option}.'</option>';
8791: } else {
8792: $srchtypesel .= '
8793: <option value="'.$option.'">'.$lt{$option}.'</option>';
8794: }
8795: }
8796: $srchtypesel .= "\n </select>\n";
8797:
1.558 albertel 8798: my ($newuserscript,$new_user_create);
1.994 raeburn 8799: my $context_dom = $env{'request.role.domain'};
8800: if ($context eq 'requestcrs') {
8801: if ($env{'form.coursedom'} ne '') {
8802: $context_dom = $env{'form.coursedom'};
8803: }
8804: }
1.556 raeburn 8805: if ($forcenewuser) {
1.576 raeburn 8806: if (ref($srch) eq 'HASH') {
1.994 raeburn 8807: if ($srch->{'srchby'} eq 'uname' && $srch->{'srchtype'} eq 'exact' && $srch->{'srchin'} eq 'dom' && $srch->{'srchdomain'} eq $context_dom) {
1.627 raeburn 8808: if ($cancreate) {
8809: $new_user_create = '<p> <input type="submit" name="forcenew" value="'.&HTML::Entities::encode(&mt('Make new user "[_1]"',$srchterm),'<>&"').'" onclick="javascript:setSearch(\'1\','.$caller.');" /> </p>';
8810: } else {
1.799 bisitz 8811: my $helplink = 'javascript:helpMenu('."'display'".')';
1.627 raeburn 8812: my %usertypetext = (
8813: official => 'institutional',
8814: unofficial => 'non-institutional',
8815: );
1.799 bisitz 8816: $new_user_create = '<p class="LC_warning">'
8817: .&mt("You are not authorized to create new $usertypetext{$usertype} users in this domain.")
8818: .' '
8819: .&mt('Please contact the [_1]helpdesk[_2] for assistance.'
8820: ,'<a href="'.$helplink.'">','</a>')
8821: .'</p><br />';
1.627 raeburn 8822: }
1.576 raeburn 8823: }
8824: }
8825:
1.556 raeburn 8826: $newuserscript = <<"ENDSCRIPT";
8827:
1.570 raeburn 8828: function setSearch(createnew,callingForm) {
1.556 raeburn 8829: if (createnew == 1) {
1.570 raeburn 8830: for (var i=0; i<callingForm.srchby.length; i++) {
8831: if (callingForm.srchby.options[i].value == 'uname') {
8832: callingForm.srchby.selectedIndex = i;
1.556 raeburn 8833: }
8834: }
1.570 raeburn 8835: for (var i=0; i<callingForm.srchin.length; i++) {
8836: if ( callingForm.srchin.options[i].value == 'dom') {
8837: callingForm.srchin.selectedIndex = i;
1.556 raeburn 8838: }
8839: }
1.570 raeburn 8840: for (var i=0; i<callingForm.srchtype.length; i++) {
8841: if (callingForm.srchtype.options[i].value == 'exact') {
8842: callingForm.srchtype.selectedIndex = i;
1.556 raeburn 8843: }
8844: }
1.570 raeburn 8845: for (var i=0; i<callingForm.srchdomain.length; i++) {
1.994 raeburn 8846: if (callingForm.srchdomain.options[i].value == '$context_dom') {
1.570 raeburn 8847: callingForm.srchdomain.selectedIndex = i;
1.556 raeburn 8848: }
8849: }
8850: }
8851: }
8852: ENDSCRIPT
1.558 albertel 8853:
1.556 raeburn 8854: }
8855:
1.555 raeburn 8856: my $output = <<"END_BLOCK";
1.556 raeburn 8857: <script type="text/javascript">
1.824 bisitz 8858: // <![CDATA[
1.570 raeburn 8859: function validateEntry(callingForm) {
1.558 albertel 8860:
1.556 raeburn 8861: var checkok = 1;
1.558 albertel 8862: var srchin;
1.570 raeburn 8863: for (var i=0; i<callingForm.srchin.length; i++) {
8864: if ( callingForm.srchin[i].checked ) {
8865: srchin = callingForm.srchin[i].value;
1.558 albertel 8866: }
8867: }
8868:
1.570 raeburn 8869: var srchtype = callingForm.srchtype.options[callingForm.srchtype.selectedIndex].value;
8870: var srchby = callingForm.srchby.options[callingForm.srchby.selectedIndex].value;
8871: var srchdomain = callingForm.srchdomain.options[callingForm.srchdomain.selectedIndex].value;
8872: var srchterm = callingForm.srchterm.value;
8873: var srchin = callingForm.srchin.options[callingForm.srchin.selectedIndex].value;
1.556 raeburn 8874: var msg = "";
8875:
8876: if (srchterm == "") {
8877: checkok = 0;
1.571 raeburn 8878: msg += "$lt{'youm'}\\n";
1.556 raeburn 8879: }
8880:
1.569 raeburn 8881: if (srchtype== 'begins') {
8882: if (srchterm.length < 2) {
8883: checkok = 0;
1.571 raeburn 8884: msg += "$lt{'thte'}\\n";
1.569 raeburn 8885: }
8886: }
8887:
1.556 raeburn 8888: if (srchtype== 'contains') {
8889: if (srchterm.length < 3) {
8890: checkok = 0;
1.571 raeburn 8891: msg += "$lt{'thet'}\\n";
1.556 raeburn 8892: }
8893: }
8894: if (srchin == 'instd') {
8895: if (srchdomain == '') {
8896: checkok = 0;
1.571 raeburn 8897: msg += "$lt{'yomc'}\\n";
1.556 raeburn 8898: }
8899: }
8900: if (srchin == 'dom') {
8901: if (srchdomain == '') {
8902: checkok = 0;
1.571 raeburn 8903: msg += "$lt{'ymcd'}\\n";
1.556 raeburn 8904: }
8905: }
8906: if (srchby == 'lastfirst') {
8907: if (srchterm.indexOf(",") == -1) {
8908: checkok = 0;
1.571 raeburn 8909: msg += "$lt{'whus'}\\n";
1.556 raeburn 8910: }
8911: if (srchterm.indexOf(",") == srchterm.length -1) {
8912: checkok = 0;
1.571 raeburn 8913: msg += "$lt{'whse'}\\n";
1.556 raeburn 8914: }
8915: }
8916: if (checkok == 0) {
1.571 raeburn 8917: alert("$lt{'thfo'}\\n"+msg);
1.556 raeburn 8918: return;
8919: }
8920: if (checkok == 1) {
1.570 raeburn 8921: callingForm.submit();
1.556 raeburn 8922: }
8923: }
8924:
8925: $newuserscript
8926:
1.824 bisitz 8927: // ]]>
1.556 raeburn 8928: </script>
1.558 albertel 8929:
8930: $new_user_create
8931:
1.555 raeburn 8932: END_BLOCK
1.558 albertel 8933:
1.876 raeburn 8934: $output .= &Apache::lonhtmlcommon::start_pick_box().
8935: &Apache::lonhtmlcommon::row_title($lt{'doma'}).
8936: $domform.
8937: &Apache::lonhtmlcommon::row_closure().
8938: &Apache::lonhtmlcommon::row_title($lt{'usr'}).
8939: $srchbysel.
8940: $srchtypesel.
8941: '<input type="text" size="15" name="srchterm" value="'.$srchterm.'" />'.
8942: $srchinsel.
8943: &Apache::lonhtmlcommon::row_closure(1).
8944: &Apache::lonhtmlcommon::end_pick_box().
8945: '<br />';
1.555 raeburn 8946: return $output;
8947: }
8948:
1.612 raeburn 8949: sub user_rule_check {
1.615 raeburn 8950: my ($usershash,$checks,$alerts,$rulematch,$inst_results,$curr_rules,$got_rules) = @_;
1.612 raeburn 8951: my $response;
8952: if (ref($usershash) eq 'HASH') {
8953: foreach my $user (keys(%{$usershash})) {
8954: my ($uname,$udom) = split(/:/,$user);
8955: next if ($udom eq '' || $uname eq '');
1.615 raeburn 8956: my ($id,$newuser);
1.612 raeburn 8957: if (ref($usershash->{$user}) eq 'HASH') {
1.615 raeburn 8958: $newuser = $usershash->{$user}->{'newuser'};
1.612 raeburn 8959: $id = $usershash->{$user}->{'id'};
8960: }
8961: my $inst_response;
8962: if (ref($checks) eq 'HASH') {
8963: if (defined($checks->{'username'})) {
1.615 raeburn 8964: ($inst_response,%{$inst_results->{$user}}) =
1.612 raeburn 8965: &Apache::lonnet::get_instuser($udom,$uname);
8966: } elsif (defined($checks->{'id'})) {
1.615 raeburn 8967: ($inst_response,%{$inst_results->{$user}}) =
1.612 raeburn 8968: &Apache::lonnet::get_instuser($udom,undef,$id);
8969: }
1.615 raeburn 8970: } else {
8971: ($inst_response,%{$inst_results->{$user}}) =
8972: &Apache::lonnet::get_instuser($udom,$uname);
8973: return;
1.612 raeburn 8974: }
1.615 raeburn 8975: if (!$got_rules->{$udom}) {
1.612 raeburn 8976: my %domconfig = &Apache::lonnet::get_dom('configuration',
8977: ['usercreation'],$udom);
8978: if (ref($domconfig{'usercreation'}) eq 'HASH') {
1.615 raeburn 8979: foreach my $item ('username','id') {
1.612 raeburn 8980: if (ref($domconfig{'usercreation'}{$item.'_rule'}) eq 'ARRAY') {
8981: $$curr_rules{$udom}{$item} =
8982: $domconfig{'usercreation'}{$item.'_rule'};
1.585 raeburn 8983: }
8984: }
8985: }
1.615 raeburn 8986: $got_rules->{$udom} = 1;
1.585 raeburn 8987: }
1.612 raeburn 8988: foreach my $item (keys(%{$checks})) {
8989: if (ref($$curr_rules{$udom}) eq 'HASH') {
8990: if (ref($$curr_rules{$udom}{$item}) eq 'ARRAY') {
8991: if (@{$$curr_rules{$udom}{$item}} > 0) {
8992: my %rule_check = &Apache::lonnet::inst_rulecheck($udom,$uname,$id,$item,$$curr_rules{$udom}{$item});
8993: foreach my $rule (@{$$curr_rules{$udom}{$item}}) {
8994: if ($rule_check{$rule}) {
8995: $$rulematch{$user}{$item} = $rule;
8996: if ($inst_response eq 'ok') {
1.615 raeburn 8997: if (ref($inst_results) eq 'HASH') {
8998: if (ref($inst_results->{$user}) eq 'HASH') {
8999: if (keys(%{$inst_results->{$user}}) == 0) {
9000: $$alerts{$item}{$udom}{$uname} = 1;
9001: }
1.612 raeburn 9002: }
9003: }
1.615 raeburn 9004: }
9005: last;
1.585 raeburn 9006: }
9007: }
9008: }
9009: }
9010: }
9011: }
9012: }
9013: }
1.612 raeburn 9014: return;
9015: }
9016:
9017: sub user_rule_formats {
9018: my ($domain,$domdesc,$curr_rules,$check) = @_;
9019: my %text = (
9020: 'username' => 'Usernames',
9021: 'id' => 'IDs',
9022: );
9023: my $output;
9024: my ($rules,$ruleorder) = &Apache::lonnet::inst_userrules($domain,$check);
9025: if ((ref($rules) eq 'HASH') && (ref($ruleorder) eq 'ARRAY')) {
9026: if (@{$ruleorder} > 0) {
1.1102 raeburn 9027: $output = '<br />'.
9028: &mt($text{$check}.' with the following format(s) may [_1]only[_2] be used for verified users at [_3]:',
9029: '<span class="LC_cusr_emph">','</span>',$domdesc).
9030: ' <ul>';
1.612 raeburn 9031: foreach my $rule (@{$ruleorder}) {
9032: if (ref($curr_rules) eq 'ARRAY') {
9033: if (grep(/^\Q$rule\E$/,@{$curr_rules})) {
9034: if (ref($rules->{$rule}) eq 'HASH') {
9035: $output .= '<li>'.$rules->{$rule}{'name'}.': '.
9036: $rules->{$rule}{'desc'}.'</li>';
9037: }
9038: }
9039: }
9040: }
9041: $output .= '</ul>';
9042: }
9043: }
9044: return $output;
9045: }
9046:
9047: sub instrule_disallow_msg {
1.615 raeburn 9048: my ($checkitem,$domdesc,$count,$mode) = @_;
1.612 raeburn 9049: my $response;
9050: my %text = (
9051: item => 'username',
9052: items => 'usernames',
9053: match => 'matches',
9054: do => 'does',
9055: action => 'a username',
9056: one => 'one',
9057: );
9058: if ($count > 1) {
9059: $text{'item'} = 'usernames';
9060: $text{'match'} ='match';
9061: $text{'do'} = 'do';
9062: $text{'action'} = 'usernames',
9063: $text{'one'} = 'ones';
9064: }
9065: if ($checkitem eq 'id') {
9066: $text{'items'} = 'IDs';
9067: $text{'item'} = 'ID';
9068: $text{'action'} = 'an ID';
1.615 raeburn 9069: if ($count > 1) {
9070: $text{'item'} = 'IDs';
9071: $text{'action'} = 'IDs';
9072: }
1.612 raeburn 9073: }
1.674 bisitz 9074: $response = &mt("The $text{'item'} you chose $text{'match'} the format of $text{'items'} defined for [_1], but the $text{'item'} $text{'do'} not exist in the institutional directory.",'<span class="LC_cusr_emph">'.$domdesc.'</span>').'<br />';
1.615 raeburn 9075: if ($mode eq 'upload') {
9076: if ($checkitem eq 'username') {
9077: $response .= &mt("You will need to modify your upload file so it will include $text{'action'} with a different format -- $text{'one'} that will not conflict with 'official' institutional $text{'items'}.");
9078: } elsif ($checkitem eq 'id') {
1.674 bisitz 9079: $response .= &mt("Either upload a file which includes $text{'action'} with a different format -- $text{'one'} that will not conflict with 'official' institutional $text{'items'}, or when associating fields with data columns, omit an association for the Student/Employee ID field.");
1.615 raeburn 9080: }
1.669 raeburn 9081: } elsif ($mode eq 'selfcreate') {
9082: if ($checkitem eq 'id') {
9083: $response .= &mt("You must either choose $text{'action'} with a different format -- $text{'one'} that will not conflict with 'official' institutional $text{'items'}, or leave the ID field blank.");
9084: }
1.615 raeburn 9085: } else {
9086: if ($checkitem eq 'username') {
9087: $response .= &mt("You must choose $text{'action'} with a different format -- $text{'one'} that will not conflict with 'official' institutional $text{'items'}.");
9088: } elsif ($checkitem eq 'id') {
9089: $response .= &mt("You must either choose $text{'action'} with a different format -- $text{'one'} that will not conflict with 'official' institutional $text{'items'}, or leave the ID field blank.");
9090: }
1.612 raeburn 9091: }
9092: return $response;
1.585 raeburn 9093: }
9094:
1.624 raeburn 9095: sub personal_data_fieldtitles {
9096: my %fieldtitles = &Apache::lonlocal::texthash (
9097: id => 'Student/Employee ID',
9098: permanentemail => 'E-mail address',
9099: lastname => 'Last Name',
9100: firstname => 'First Name',
9101: middlename => 'Middle Name',
9102: generation => 'Generation',
9103: gen => 'Generation',
1.765 raeburn 9104: inststatus => 'Affiliation',
1.624 raeburn 9105: );
9106: return %fieldtitles;
9107: }
9108:
1.642 raeburn 9109: sub sorted_inst_types {
9110: my ($dom) = @_;
9111: my ($usertypes,$order) = &Apache::lonnet::retrieve_inst_usertypes($dom);
9112: my $othertitle = &mt('All users');
9113: if ($env{'request.course.id'}) {
1.668 raeburn 9114: $othertitle = &mt('Any users');
1.642 raeburn 9115: }
9116: my @types;
9117: if (ref($order) eq 'ARRAY') {
9118: @types = @{$order};
9119: }
9120: if (@types == 0) {
9121: if (ref($usertypes) eq 'HASH') {
9122: @types = sort(keys(%{$usertypes}));
9123: }
9124: }
9125: if (keys(%{$usertypes}) > 0) {
9126: $othertitle = &mt('Other users');
9127: }
9128: return ($othertitle,$usertypes,\@types);
9129: }
9130:
1.645 raeburn 9131: sub get_institutional_codes {
9132: my ($settings,$allcourses,$LC_code) = @_;
9133: # Get complete list of course sections to update
9134: my @currsections = ();
9135: my @currxlists = ();
9136: my $coursecode = $$settings{'internal.coursecode'};
9137:
9138: if ($$settings{'internal.sectionnums'} ne '') {
9139: @currsections = split(/,/,$$settings{'internal.sectionnums'});
9140: }
9141:
9142: if ($$settings{'internal.crosslistings'} ne '') {
9143: @currxlists = split(/,/,$$settings{'internal.crosslistings'});
9144: }
9145:
9146: if (@currxlists > 0) {
9147: foreach (@currxlists) {
9148: if (m/^([^:]+):(\w*)$/) {
9149: unless (grep/^$1$/,@{$allcourses}) {
9150: push @{$allcourses},$1;
9151: $$LC_code{$1} = $2;
9152: }
9153: }
9154: }
9155: }
9156:
9157: if (@currsections > 0) {
9158: foreach (@currsections) {
9159: if (m/^(\w+):(\w*)$/) {
9160: my $sec = $coursecode.$1;
9161: my $lc_sec = $2;
9162: unless (grep/^$sec$/,@{$allcourses}) {
9163: push @{$allcourses},$sec;
9164: $$LC_code{$sec} = $lc_sec;
9165: }
9166: }
9167: }
9168: }
9169: return;
9170: }
9171:
1.971 raeburn 9172: sub get_standard_codeitems {
9173: return ('Year','Semester','Department','Number','Section');
9174: }
9175:
1.112 bowersj2 9176: =pod
9177:
1.780 raeburn 9178: =head1 Slot Helpers
9179:
9180: =over 4
9181:
9182: =item * sorted_slots()
9183:
1.1040 raeburn 9184: Sorts an array of slot names in order of an optional sort key,
9185: default sort is by slot start time (earliest first).
1.780 raeburn 9186:
9187: Inputs:
9188:
9189: =over 4
9190:
9191: slotsarr - Reference to array of unsorted slot names.
9192:
9193: slots - Reference to hash of hash, where outer hash keys are slot names.
9194:
1.1040 raeburn 9195: sortkey - Name of key in inner hash to be sorted on (e.g., starttime).
9196:
1.549 albertel 9197: =back
9198:
1.780 raeburn 9199: Returns:
9200:
9201: =over 4
9202:
1.1040 raeburn 9203: sorted - An array of slot names sorted by a specified sort key
9204: (default sort key is start time of the slot).
1.780 raeburn 9205:
9206: =back
9207:
9208: =cut
9209:
9210:
9211: sub sorted_slots {
1.1040 raeburn 9212: my ($slotsarr,$slots,$sortkey) = @_;
9213: if ($sortkey eq '') {
9214: $sortkey = 'starttime';
9215: }
1.780 raeburn 9216: my @sorted;
9217: if ((ref($slotsarr) eq 'ARRAY') && (ref($slots) eq 'HASH')) {
9218: @sorted =
9219: sort {
9220: if (ref($slots->{$a}) && ref($slots->{$b})) {
1.1040 raeburn 9221: return $slots->{$a}{$sortkey} <=> $slots->{$b}{$sortkey}
1.780 raeburn 9222: }
9223: if (ref($slots->{$a})) { return -1;}
9224: if (ref($slots->{$b})) { return 1;}
9225: return 0;
9226: } @{$slotsarr};
9227: }
9228: return @sorted;
9229: }
9230:
1.1040 raeburn 9231: =pod
9232:
9233: =item * get_future_slots()
9234:
9235: Inputs:
9236:
9237: =over 4
9238:
9239: cnum - course number
9240:
9241: cdom - course domain
9242:
9243: now - current UNIX time
9244:
9245: symb - optional symb
9246:
9247: =back
9248:
9249: Returns:
9250:
9251: =over 4
9252:
9253: sorted_reservable - ref to array of student_schedulable slots currently
9254: reservable, ordered by end date of reservation period.
9255:
9256: reservable_now - ref to hash of student_schedulable slots currently
9257: reservable.
9258:
9259: Keys in inner hash are:
9260: (a) symb: either blank or symb to which slot use is restricted.
9261: (b) endreserve: end date of reservation period.
9262:
9263: sorted_future - ref to array of student_schedulable slots reservable in
9264: the future, ordered by start date of reservation period.
9265:
9266: future_reservable - ref to hash of student_schedulable slots reservable
9267: in the future.
9268:
9269: Keys in inner hash are:
9270: (a) symb: either blank or symb to which slot use is restricted.
9271: (b) startreserve: start date of reservation period.
9272:
9273: =back
9274:
9275: =cut
9276:
9277: sub get_future_slots {
9278: my ($cnum,$cdom,$now,$symb) = @_;
9279: my (%reservable_now,%future_reservable,@sorted_reservable,@sorted_future);
9280: my %slots = &Apache::lonnet::get_course_slots($cnum,$cdom);
9281: foreach my $slot (keys(%slots)) {
9282: next unless($slots{$slot}->{'type'} eq 'schedulable_student');
9283: if ($symb) {
9284: next if (($slots{$slot}->{'symb'} ne '') &&
9285: ($slots{$slot}->{'symb'} ne $symb));
9286: }
9287: if (($slots{$slot}->{'starttime'} > $now) &&
9288: ($slots{$slot}->{'endtime'} > $now)) {
9289: if (($slots{$slot}->{'allowedsections'}) || ($slots{$slot}->{'allowedusers'})) {
9290: my $userallowed = 0;
9291: if ($slots{$slot}->{'allowedsections'}) {
9292: my @allowed_sec = split(',',$slots{$slot}->{'allowedsections'});
9293: if (!defined($env{'request.role.sec'})
9294: && grep(/^No section assigned$/,@allowed_sec)) {
9295: $userallowed=1;
9296: } else {
9297: if (grep(/^\Q$env{'request.role.sec'}\E$/,@allowed_sec)) {
9298: $userallowed=1;
9299: }
9300: }
9301: unless ($userallowed) {
9302: if (defined($env{'request.course.groups'})) {
9303: my @groups = split(/:/,$env{'request.course.groups'});
9304: foreach my $group (@groups) {
9305: if (grep(/^\Q$group\E$/,@allowed_sec)) {
9306: $userallowed=1;
9307: last;
9308: }
9309: }
9310: }
9311: }
9312: }
9313: if ($slots{$slot}->{'allowedusers'}) {
9314: my @allowed_users = split(',',$slots{$slot}->{'allowedusers'});
9315: my $user = $env{'user.name'}.':'.$env{'user.domain'};
9316: if (grep(/^\Q$user\E$/,@allowed_users)) {
9317: $userallowed = 1;
9318: }
9319: }
9320: next unless($userallowed);
9321: }
9322: my $startreserve = $slots{$slot}->{'startreserve'};
9323: my $endreserve = $slots{$slot}->{'endreserve'};
9324: my $symb = $slots{$slot}->{'symb'};
9325: if (($startreserve < $now) &&
9326: (!$endreserve || $endreserve > $now)) {
9327: my $lastres = $endreserve;
9328: if (!$lastres) {
9329: $lastres = $slots{$slot}->{'starttime'};
9330: }
9331: $reservable_now{$slot} = {
9332: symb => $symb,
9333: endreserve => $lastres
9334: };
9335: } elsif (($startreserve > $now) &&
9336: (!$endreserve || $endreserve > $startreserve)) {
9337: $future_reservable{$slot} = {
9338: symb => $symb,
9339: startreserve => $startreserve
9340: };
9341: }
9342: }
9343: }
9344: my @unsorted_reservable = keys(%reservable_now);
9345: if (@unsorted_reservable > 0) {
9346: @sorted_reservable =
9347: &sorted_slots(\@unsorted_reservable,\%reservable_now,'endreserve');
9348: }
9349: my @unsorted_future = keys(%future_reservable);
9350: if (@unsorted_future > 0) {
9351: @sorted_future =
9352: &sorted_slots(\@unsorted_future,\%future_reservable,'startreserve');
9353: }
9354: return (\@sorted_reservable,\%reservable_now,\@sorted_future,\%future_reservable);
9355: }
1.780 raeburn 9356:
9357: =pod
9358:
1.1057 foxr 9359: =back
9360:
1.549 albertel 9361: =head1 HTTP Helpers
9362:
9363: =over 4
9364:
1.648 raeburn 9365: =item * &get_unprocessed_cgi($query,$possible_names)
1.112 bowersj2 9366:
1.258 albertel 9367: Modify the %env hash to contain unprocessed CGI form parameters held in
1.112 bowersj2 9368: $query. The parameters listed in $possible_names (an array reference),
1.258 albertel 9369: will be set in $env{'form.name'} if they do not already exist.
1.112 bowersj2 9370:
9371: Typically called with $ENV{'QUERY_STRING'} as the first parameter.
9372: $possible_names is an ref to an array of form element names. As an example:
9373: get_unprocessed_cgi($ENV{'QUERY_STRING'},['uname','udom']);
1.258 albertel 9374: will result in $env{'form.uname'} and $env{'form.udom'} being set.
1.112 bowersj2 9375:
9376: =cut
1.1 albertel 9377:
1.6 albertel 9378: sub get_unprocessed_cgi {
1.25 albertel 9379: my ($query,$possible_names)= @_;
1.26 matthew 9380: # $Apache::lonxml::debug=1;
1.356 albertel 9381: foreach my $pair (split(/&/,$query)) {
9382: my ($name, $value) = split(/=/,$pair);
1.369 www 9383: $name = &unescape($name);
1.25 albertel 9384: if (!defined($possible_names) || (grep {$_ eq $name} @$possible_names)) {
9385: $value =~ tr/+/ /;
9386: $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
1.258 albertel 9387: unless (defined($env{'form.'.$name})) { &add_to_env('form.'.$name,$value) };
1.25 albertel 9388: }
1.16 harris41 9389: }
1.6 albertel 9390: }
9391:
1.112 bowersj2 9392: =pod
9393:
1.648 raeburn 9394: =item * &cacheheader()
1.112 bowersj2 9395:
9396: returns cache-controlling header code
9397:
9398: =cut
9399:
1.7 albertel 9400: sub cacheheader {
1.258 albertel 9401: unless ($env{'request.method'} eq 'GET') { return ''; }
1.216 albertel 9402: my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime);
9403: my $output .='<meta HTTP-EQUIV="Expires" CONTENT="'.$date.'" />
1.7 albertel 9404: <meta HTTP-EQUIV="Cache-control" CONTENT="no-cache" />
9405: <meta HTTP-EQUIV="Pragma" CONTENT="no-cache" />';
1.216 albertel 9406: return $output;
1.7 albertel 9407: }
9408:
1.112 bowersj2 9409: =pod
9410:
1.648 raeburn 9411: =item * &no_cache($r)
1.112 bowersj2 9412:
9413: specifies header code to not have cache
9414:
9415: =cut
9416:
1.9 albertel 9417: sub no_cache {
1.216 albertel 9418: my ($r) = @_;
9419: if ($ENV{'REQUEST_METHOD'} ne 'GET' &&
1.258 albertel 9420: $env{'request.method'} ne 'GET') { return ''; }
1.216 albertel 9421: my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime(time));
9422: $r->no_cache(1);
9423: $r->header_out("Expires" => $date);
9424: $r->header_out("Pragma" => "no-cache");
1.123 www 9425: }
9426:
9427: sub content_type {
1.181 albertel 9428: my ($r,$type,$charset) = @_;
1.299 foxr 9429: if ($r) {
9430: # Note that printout.pl calls this with undef for $r.
9431: &no_cache($r);
9432: }
1.258 albertel 9433: if ($env{'browser.mathml'} && $type eq 'text/html') { $type='text/xml'; }
1.181 albertel 9434: unless ($charset) {
9435: $charset=&Apache::lonlocal::current_encoding;
9436: }
9437: if ($charset) { $type.='; charset='.$charset; }
9438: if ($r) {
9439: $r->content_type($type);
9440: } else {
9441: print("Content-type: $type\n\n");
9442: }
1.9 albertel 9443: }
1.25 albertel 9444:
1.112 bowersj2 9445: =pod
9446:
1.648 raeburn 9447: =item * &add_to_env($name,$value)
1.112 bowersj2 9448:
1.258 albertel 9449: adds $name to the %env hash with value
1.112 bowersj2 9450: $value, if $name already exists, the entry is converted to an array
9451: reference and $value is added to the array.
9452:
9453: =cut
9454:
1.25 albertel 9455: sub add_to_env {
9456: my ($name,$value)=@_;
1.258 albertel 9457: if (defined($env{$name})) {
9458: if (ref($env{$name})) {
1.25 albertel 9459: #already have multiple values
1.258 albertel 9460: push(@{ $env{$name} },$value);
1.25 albertel 9461: } else {
9462: #first time seeing multiple values, convert hash entry to an arrayref
1.258 albertel 9463: my $first=$env{$name};
9464: undef($env{$name});
9465: push(@{ $env{$name} },$first,$value);
1.25 albertel 9466: }
9467: } else {
1.258 albertel 9468: $env{$name}=$value;
1.25 albertel 9469: }
1.31 albertel 9470: }
1.149 albertel 9471:
9472: =pod
9473:
1.648 raeburn 9474: =item * &get_env_multiple($name)
1.149 albertel 9475:
1.258 albertel 9476: gets $name from the %env hash, it seemlessly handles the cases where multiple
1.149 albertel 9477: values may be defined and end up as an array ref.
9478:
9479: returns an array of values
9480:
9481: =cut
9482:
9483: sub get_env_multiple {
9484: my ($name) = @_;
9485: my @values;
1.258 albertel 9486: if (defined($env{$name})) {
1.149 albertel 9487: # exists is it an array
1.258 albertel 9488: if (ref($env{$name})) {
9489: @values=@{ $env{$name} };
1.149 albertel 9490: } else {
1.258 albertel 9491: $values[0]=$env{$name};
1.149 albertel 9492: }
9493: }
9494: return(@values);
9495: }
9496:
1.660 raeburn 9497: sub ask_for_embedded_content {
9498: my ($actionurl,$state,$allfiles,$codebase,$args)=@_;
1.1071 raeburn 9499: my (%subdependencies,%dependencies,%mapping,%existing,%newfiles,%pathchanges,
1.1085 raeburn 9500: %currsubfile,%unused,$rem);
1.1071 raeburn 9501: my $counter = 0;
9502: my $numnew = 0;
1.987 raeburn 9503: my $numremref = 0;
9504: my $numinvalid = 0;
9505: my $numpathchg = 0;
9506: my $numexisting = 0;
1.1071 raeburn 9507: my $numunused = 0;
9508: my ($output,$upload_output,$toplevel,$url,$udom,$uname,$getpropath,$cdom,$cnum,
9509: $fileloc,$filename,$delete_output,$modify_output,$title,$symb,$path);
9510: my $heading = &mt('Upload embedded files');
9511: my $buttontext = &mt('Upload');
9512:
1.1085 raeburn 9513: my $navmap;
9514: if ($env{'request.course.id'}) {
9515: $navmap = Apache::lonnavmaps::navmap->new();
9516: }
1.984 raeburn 9517: if (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
9518: my $current_path='/';
9519: if ($env{'form.currentpath'}) {
9520: $current_path = $env{'form.currentpath'};
9521: }
9522: if ($actionurl eq '/adm/coursegrp_portfolio') {
9523: $udom = $env{'course.'.$env{'request.course.id'}.'.domain'};
9524: $uname = $env{'course.'.$env{'request.course.id'}.'.num'};
9525: $url = '/userfiles/groups/'.$env{'form.group'}.'/portfolio';
9526: } else {
9527: $udom = $env{'user.domain'};
9528: $uname = $env{'user.name'};
9529: $url = '/userfiles/portfolio';
9530: }
1.987 raeburn 9531: $toplevel = $url.'/';
1.984 raeburn 9532: $url .= $current_path;
9533: $getpropath = 1;
1.987 raeburn 9534: } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank') ||
9535: ($actionurl eq '/adm/imsimport')) {
1.1022 www 9536: my ($udom,$uname,$rest) = ($args->{'current_path'} =~ m{/priv/($match_domain)/($match_username)/?(.*)$});
1.1026 raeburn 9537: $url = $Apache::lonnet::perlvar{'lonDocRoot'}."/priv/$udom/$uname/";
1.987 raeburn 9538: $toplevel = $url;
1.984 raeburn 9539: if ($rest ne '') {
1.987 raeburn 9540: $url .= $rest;
9541: }
9542: } elsif ($actionurl eq '/adm/coursedocs') {
9543: if (ref($args) eq 'HASH') {
1.1071 raeburn 9544: $url = $args->{'docs_url'};
9545: $toplevel = $url;
1.1084 raeburn 9546: if ($args->{'context'} eq 'paste') {
9547: ($cdom,$cnum) = ($url =~ m{^\Q/uploaded/\E($match_domain)/($match_courseid)/});
9548: ($path) =
9549: ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/\E(?:docs|supplemental)/(?:default|\d+)/\d+)/});
9550: $fileloc = &Apache::lonnet::filelocation('',$toplevel);
9551: $fileloc =~ s{^/}{};
9552: }
1.1071 raeburn 9553: }
1.1084 raeburn 9554: } elsif ($actionurl eq '/adm/dependencies') {
1.1071 raeburn 9555: if ($env{'request.course.id'} ne '') {
9556: $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
9557: $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
9558: if (ref($args) eq 'HASH') {
9559: $url = $args->{'docs_url'};
9560: $title = $args->{'docs_title'};
9561: $toplevel = "/$url";
1.1085 raeburn 9562: ($rem) = ($toplevel =~ m{^(.+/)[^/]+$});
1.1071 raeburn 9563: ($path) =
9564: ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/\E(?:docs|supplemental)/(?:default|\d+)/\d+)/});
9565: $fileloc = &Apache::lonnet::filelocation('',$toplevel);
9566: $fileloc =~ s{^/}{};
9567: ($filename) = ($fileloc =~ m{.+/([^/]+)$});
9568: $heading = &mt('Status of dependencies in [_1]',"$title ($filename)");
9569: }
1.987 raeburn 9570: }
9571: }
9572: my $now = time();
9573: foreach my $embed_file (keys(%{$allfiles})) {
9574: my $absolutepath;
9575: if ($embed_file =~ m{^\w+://}) {
9576: $newfiles{$embed_file} = 1;
9577: $mapping{$embed_file} = $embed_file;
9578: } else {
9579: if ($embed_file =~ m{^/}) {
9580: $absolutepath = $embed_file;
9581: $embed_file =~ s{^(/+)}{};
9582: }
9583: if ($embed_file =~ m{/}) {
9584: my ($path,$fname) = ($embed_file =~ m{^(.+)/([^/]*)$});
9585: $path = &check_for_traversal($path,$url,$toplevel);
9586: my $item = $fname;
9587: if ($path ne '') {
9588: $item = $path.'/'.$fname;
9589: $subdependencies{$path}{$fname} = 1;
9590: } else {
9591: $dependencies{$item} = 1;
9592: }
9593: if ($absolutepath) {
9594: $mapping{$item} = $absolutepath;
9595: } else {
9596: $mapping{$item} = $embed_file;
9597: }
9598: } else {
9599: $dependencies{$embed_file} = 1;
9600: if ($absolutepath) {
9601: $mapping{$embed_file} = $absolutepath;
9602: } else {
9603: $mapping{$embed_file} = $embed_file;
9604: }
9605: }
1.984 raeburn 9606: }
9607: }
1.1071 raeburn 9608: my $dirptr = 16384;
1.984 raeburn 9609: foreach my $path (keys(%subdependencies)) {
1.1071 raeburn 9610: $currsubfile{$path} = {};
1.984 raeburn 9611: if (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
1.1021 raeburn 9612: my ($sublistref,$listerror) =
9613: &Apache::lonnet::dirlist($url.$path,$udom,$uname,$getpropath);
9614: if (ref($sublistref) eq 'ARRAY') {
9615: foreach my $line (@{$sublistref}) {
9616: my ($file_name,$rest) = split(/\&/,$line,2);
1.1071 raeburn 9617: $currsubfile{$path}{$file_name} = 1;
1.1021 raeburn 9618: }
1.984 raeburn 9619: }
1.987 raeburn 9620: } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
1.984 raeburn 9621: if (opendir(my $dir,$url.'/'.$path)) {
9622: my @subdir_list = grep(!/^\./,readdir($dir));
1.1071 raeburn 9623: map {$currsubfile{$path}{$_} = 1;} @subdir_list;
9624: }
1.1084 raeburn 9625: } elsif (($actionurl eq '/adm/dependencies') ||
9626: (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
9627: ($args->{'context'} eq 'paste'))) {
1.1071 raeburn 9628: if ($env{'request.course.id'} ne '') {
9629: my ($dir) = ($fileloc =~ m{^(.+/)[^/]+$});
9630: if ($dir ne '') {
9631: my ($sublistref,$listerror) =
9632: &Apache::lonnet::dirlist($dir.$path,$cdom,$cnum,$getpropath,undef,'/');
9633: if (ref($sublistref) eq 'ARRAY') {
9634: foreach my $line (@{$sublistref}) {
9635: my ($file_name,$dom,undef,$testdir,undef,undef,undef,undef,$size,
9636: undef,$mtime)=split(/\&/,$line,12);
9637: unless (($testdir&$dirptr) ||
9638: ($file_name =~ /^\.\.?$/)) {
9639: $currsubfile{$path}{$file_name} = [$size,$mtime];
9640: }
9641: }
9642: }
9643: }
1.984 raeburn 9644: }
9645: }
9646: foreach my $file (keys(%{$subdependencies{$path}})) {
1.1071 raeburn 9647: if (exists($currsubfile{$path}{$file})) {
1.987 raeburn 9648: my $item = $path.'/'.$file;
9649: unless ($mapping{$item} eq $item) {
9650: $pathchanges{$item} = 1;
9651: }
9652: $existing{$item} = 1;
9653: $numexisting ++;
9654: } else {
9655: $newfiles{$path.'/'.$file} = 1;
1.984 raeburn 9656: }
9657: }
1.1071 raeburn 9658: if ($actionurl eq '/adm/dependencies') {
9659: foreach my $path (keys(%currsubfile)) {
9660: if (ref($currsubfile{$path}) eq 'HASH') {
9661: foreach my $file (keys(%{$currsubfile{$path}})) {
9662: unless ($subdependencies{$path}{$file}) {
1.1085 raeburn 9663: next if (($rem ne '') &&
9664: (($env{"httpref.$rem"."$path/$file"} ne '') ||
9665: (ref($navmap) &&
9666: (($navmap->getResourceByUrl($rem."$path/$file") ne '') ||
9667: (($file =~ /^(.*\.s?html?)\.bak$/i) &&
9668: ($navmap->getResourceByUrl($rem."$path/$1")))))));
1.1071 raeburn 9669: $unused{$path.'/'.$file} = 1;
9670: }
9671: }
9672: }
9673: }
9674: }
1.984 raeburn 9675: }
1.987 raeburn 9676: my %currfile;
1.984 raeburn 9677: if (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
1.1021 raeburn 9678: my ($dirlistref,$listerror) =
9679: &Apache::lonnet::dirlist($url,$udom,$uname,$getpropath);
9680: if (ref($dirlistref) eq 'ARRAY') {
9681: foreach my $line (@{$dirlistref}) {
9682: my ($file_name,$rest) = split(/\&/,$line,2);
9683: $currfile{$file_name} = 1;
9684: }
1.984 raeburn 9685: }
1.987 raeburn 9686: } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
1.984 raeburn 9687: if (opendir(my $dir,$url)) {
1.987 raeburn 9688: my @dir_list = grep(!/^\./,readdir($dir));
1.984 raeburn 9689: map {$currfile{$_} = 1;} @dir_list;
9690: }
1.1084 raeburn 9691: } elsif (($actionurl eq '/adm/dependencies') ||
9692: (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
9693: ($args->{'context'} eq 'paste'))) {
1.1071 raeburn 9694: if ($env{'request.course.id'} ne '') {
9695: my ($dir) = ($fileloc =~ m{^(.+/)[^/]+$});
9696: if ($dir ne '') {
9697: my ($dirlistref,$listerror) =
9698: &Apache::lonnet::dirlist($dir,$cdom,$cnum,$getpropath,undef,'/');
9699: if (ref($dirlistref) eq 'ARRAY') {
9700: foreach my $line (@{$dirlistref}) {
9701: my ($file_name,$dom,undef,$testdir,undef,undef,undef,undef,
9702: $size,undef,$mtime)=split(/\&/,$line,12);
9703: unless (($testdir&$dirptr) ||
9704: ($file_name =~ /^\.\.?$/)) {
9705: $currfile{$file_name} = [$size,$mtime];
9706: }
9707: }
9708: }
9709: }
9710: }
1.984 raeburn 9711: }
9712: foreach my $file (keys(%dependencies)) {
1.1071 raeburn 9713: if (exists($currfile{$file})) {
1.987 raeburn 9714: unless ($mapping{$file} eq $file) {
9715: $pathchanges{$file} = 1;
9716: }
9717: $existing{$file} = 1;
9718: $numexisting ++;
9719: } else {
1.984 raeburn 9720: $newfiles{$file} = 1;
9721: }
9722: }
1.1071 raeburn 9723: foreach my $file (keys(%currfile)) {
9724: unless (($file eq $filename) ||
9725: ($file eq $filename.'.bak') ||
9726: ($dependencies{$file})) {
1.1085 raeburn 9727: if ($actionurl eq '/adm/dependencies') {
9728: next if (($rem ne '') &&
9729: (($env{"httpref.$rem".$file} ne '') ||
9730: (ref($navmap) &&
9731: (($navmap->getResourceByUrl($rem.$file) ne '') ||
9732: (($file =~ /^(.*\.s?html?)\.bak$/i) &&
9733: ($navmap->getResourceByUrl($rem.$1)))))));
9734: }
1.1071 raeburn 9735: $unused{$file} = 1;
9736: }
9737: }
1.1084 raeburn 9738: if (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
9739: ($args->{'context'} eq 'paste')) {
9740: $counter = scalar(keys(%existing));
9741: $numpathchg = scalar(keys(%pathchanges));
9742: return ($output,$counter,$numpathchg,\%existing);
9743: }
1.984 raeburn 9744: foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%newfiles)) {
1.1071 raeburn 9745: if ($actionurl eq '/adm/dependencies') {
9746: next if ($embed_file =~ m{^\w+://});
9747: }
1.660 raeburn 9748: $upload_output .= &start_data_table_row().
1.1071 raeburn 9749: '<td><img src="'.&icon($embed_file).'" /> '.
9750: '<span class="LC_filename">'.$embed_file.'</span>';
1.987 raeburn 9751: unless ($mapping{$embed_file} eq $embed_file) {
9752: $upload_output .= '<br /><span class="LC_info" style="font-size:smaller;">'.&mt('changed from: [_1]',$mapping{$embed_file}).'</span>';
9753: }
9754: $upload_output .= '</td><td>';
1.1071 raeburn 9755: if ($args->{'ignore_remote_references'} && $embed_file =~ m{^\w+://}) {
1.660 raeburn 9756: $upload_output.='<span class="LC_warning">'.&mt("URL points to other server.").'</span>';
1.987 raeburn 9757: $numremref++;
1.660 raeburn 9758: } elsif ($args->{'error_on_invalid_names'}
9759: && $embed_file ne &Apache::lonnet::clean_filename($embed_file,{'keep_path' => 1,})) {
1.987 raeburn 9760: $upload_output.='<span class="LC_warning">'.&mt('Invalid characters').'</span>';
9761: $numinvalid++;
1.660 raeburn 9762: } else {
1.1071 raeburn 9763: $upload_output .= &embedded_file_element('upload_embedded',$counter,
1.987 raeburn 9764: $embed_file,\%mapping,
1.1071 raeburn 9765: $allfiles,$codebase,'upload');
9766: $counter ++;
9767: $numnew ++;
1.987 raeburn 9768: }
9769: $upload_output .= '</td>'.&Apache::loncommon::end_data_table_row()."\n";
9770: }
9771: foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%existing)) {
1.1071 raeburn 9772: if ($actionurl eq '/adm/dependencies') {
9773: my ($size,$mtime) = &get_dependency_details(\%currfile,\%currsubfile,$embed_file);
9774: $modify_output .= &start_data_table_row().
9775: '<td><a href="'.$path.'/'.$embed_file.'" style="text-decoration:none;">'.
9776: '<img src="'.&icon($embed_file).'" border="0" />'.
9777: ' <span class="LC_filename">'.$embed_file.'</span></a></td>'.
9778: '<td>'.$size.'</td>'.
9779: '<td>'.$mtime.'</td>'.
9780: '<td><label><input type="checkbox" name="mod_upload_dep" '.
9781: 'onclick="toggleBrowse('."'$counter'".')" id="mod_upload_dep_'.
9782: $counter.'" value="'.$counter.'" />'.&mt('Yes').'</label>'.
9783: '<div id="moduploaddep_'.$counter.'" style="display:none;">'.
9784: &embedded_file_element('upload_embedded',$counter,
9785: $embed_file,\%mapping,
9786: $allfiles,$codebase,'modify').
9787: '</div></td>'.
9788: &end_data_table_row()."\n";
9789: $counter ++;
9790: } else {
9791: $upload_output .= &start_data_table_row().
9792: '<td><span class="LC_filename">'.$embed_file.'</span></td>';
9793: '<td><span class="LC_warning">'.&mt('Already exists').'</span></td>'.
9794: &Apache::loncommon::end_data_table_row()."\n";
9795: }
9796: }
9797: my $delidx = $counter;
9798: foreach my $oldfile (sort {lc($a) cmp lc($b)} keys(%unused)) {
9799: my ($size,$mtime) = &get_dependency_details(\%currfile,\%currsubfile,$oldfile);
9800: $delete_output .= &start_data_table_row().
9801: '<td><img src="'.&icon($oldfile).'" />'.
9802: ' <span class="LC_filename">'.$oldfile.'</span></td>'.
9803: '<td>'.$size.'</td>'.
9804: '<td>'.$mtime.'</td>'.
9805: '<td><label><input type="checkbox" name="del_upload_dep" '.
9806: ' value="'.$delidx.'" />'.&mt('Yes').'</label>'.
9807: &embedded_file_element('upload_embedded',$delidx,
9808: $oldfile,\%mapping,$allfiles,
9809: $codebase,'delete').'</td>'.
9810: &end_data_table_row()."\n";
9811: $numunused ++;
9812: $delidx ++;
1.987 raeburn 9813: }
9814: if ($upload_output) {
9815: $upload_output = &start_data_table().
9816: $upload_output.
9817: &end_data_table()."\n";
9818: }
1.1071 raeburn 9819: if ($modify_output) {
9820: $modify_output = &start_data_table().
9821: &start_data_table_header_row().
9822: '<th>'.&mt('File').'</th>'.
9823: '<th>'.&mt('Size (KB)').'</th>'.
9824: '<th>'.&mt('Modified').'</th>'.
9825: '<th>'.&mt('Upload replacement?').'</th>'.
9826: &end_data_table_header_row().
9827: $modify_output.
9828: &end_data_table()."\n";
9829: }
9830: if ($delete_output) {
9831: $delete_output = &start_data_table().
9832: &start_data_table_header_row().
9833: '<th>'.&mt('File').'</th>'.
9834: '<th>'.&mt('Size (KB)').'</th>'.
9835: '<th>'.&mt('Modified').'</th>'.
9836: '<th>'.&mt('Delete?').'</th>'.
9837: &end_data_table_header_row().
9838: $delete_output.
9839: &end_data_table()."\n";
9840: }
1.987 raeburn 9841: my $applies = 0;
9842: if ($numremref) {
9843: $applies ++;
9844: }
9845: if ($numinvalid) {
9846: $applies ++;
9847: }
9848: if ($numexisting) {
9849: $applies ++;
9850: }
1.1071 raeburn 9851: if ($counter || $numunused) {
1.987 raeburn 9852: $output = '<form name="upload_embedded" action="'.$actionurl.'"'.
9853: ' method="post" enctype="multipart/form-data">'."\n".
1.1071 raeburn 9854: $state.'<h3>'.$heading.'</h3>';
9855: if ($actionurl eq '/adm/dependencies') {
9856: if ($numnew) {
9857: $output .= '<h4>'.&mt('Missing dependencies').'</h4>'.
9858: '<p>'.&mt('The following files need to be uploaded.').'</p>'."\n".
9859: $upload_output.'<br />'."\n";
9860: }
9861: if ($numexisting) {
9862: $output .= '<h4>'.&mt('Uploaded dependencies (in use)').'</h4>'.
9863: '<p>'.&mt('Upload a new file to replace the one currently in use.').'</p>'."\n".
9864: $modify_output.'<br />'."\n";
9865: $buttontext = &mt('Save changes');
9866: }
9867: if ($numunused) {
9868: $output .= '<h4>'.&mt('Unused files').'</h4>'.
9869: '<p>'.&mt('The following uploaded files are no longer used.').'</p>'."\n".
9870: $delete_output.'<br />'."\n";
9871: $buttontext = &mt('Save changes');
9872: }
9873: } else {
9874: $output .= $upload_output.'<br />'."\n";
9875: }
9876: $output .= '<input type ="hidden" name="number_embedded_items" value="'.
9877: $counter.'" />'."\n";
9878: if ($actionurl eq '/adm/dependencies') {
9879: $output .= '<input type ="hidden" name="number_newemb_items" value="'.
9880: $numnew.'" />'."\n";
9881: } elsif ($actionurl eq '') {
1.987 raeburn 9882: $output .= '<input type="hidden" name="phase" value="three" />';
9883: }
9884: } elsif ($applies) {
9885: $output = '<b>'.&mt('Referenced files').'</b>:<br />';
9886: if ($applies > 1) {
9887: $output .=
9888: &mt('No files need to be uploaded, as one of the following applies to each reference:').'<ul>';
9889: if ($numremref) {
9890: $output .= '<li>'.&mt('reference is to a URL which points to another server').'</li>'."\n";
9891: }
9892: if ($numinvalid) {
9893: $output .= '<li>'.&mt('reference is to file with a name containing invalid characters').'</li>'."\n";
9894: }
9895: if ($numexisting) {
9896: $output .= '<li>'.&mt('reference is to an existing file at the specified location').'</li>'."\n";
9897: }
9898: $output .= '</ul><br />';
9899: } elsif ($numremref) {
9900: $output .= '<p>'.&mt('None to upload, as all references are to URLs pointing to another server.').'</p>';
9901: } elsif ($numinvalid) {
9902: $output .= '<p>'.&mt('None to upload, as all references are to files with names containing invalid characters.').'</p>';
9903: } elsif ($numexisting) {
9904: $output .= '<p>'.&mt('None to upload, as all references are to existing files.').'</p>';
9905: }
9906: $output .= $upload_output.'<br />';
9907: }
9908: my ($pathchange_output,$chgcount);
1.1071 raeburn 9909: $chgcount = $counter;
1.987 raeburn 9910: if (keys(%pathchanges) > 0) {
9911: foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%pathchanges)) {
1.1071 raeburn 9912: if ($counter) {
1.987 raeburn 9913: $output .= &embedded_file_element('pathchange',$chgcount,
9914: $embed_file,\%mapping,
1.1071 raeburn 9915: $allfiles,$codebase,'change');
1.987 raeburn 9916: } else {
9917: $pathchange_output .=
9918: &start_data_table_row().
9919: '<td><input type ="checkbox" name="namechange" value="'.
9920: $chgcount.'" checked="checked" /></td>'.
9921: '<td>'.$mapping{$embed_file}.'</td>'.
9922: '<td>'.$embed_file.
9923: &embedded_file_element('pathchange',$numpathchg,$embed_file,
1.1071 raeburn 9924: \%mapping,$allfiles,$codebase,'change').
1.987 raeburn 9925: '</td>'.&end_data_table_row();
1.660 raeburn 9926: }
1.987 raeburn 9927: $numpathchg ++;
9928: $chgcount ++;
1.660 raeburn 9929: }
9930: }
1.1071 raeburn 9931: if ($counter) {
1.987 raeburn 9932: if ($numpathchg) {
9933: $output .= '<input type ="hidden" name="number_pathchange_items" value="'.
9934: $numpathchg.'" />'."\n";
9935: }
9936: if (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank') ||
9937: ($actionurl eq '/adm/imsimport')) {
9938: $output .= '<input type="hidden" name="phase" value="three" />'."\n";
9939: } elsif ($actionurl eq '/adm/portfolio' || $actionurl eq '/adm/coursegrp_portfolio') {
9940: $output .= '<input type="hidden" name="action" value="upload_embedded" />';
1.1071 raeburn 9941: } elsif ($actionurl eq '/adm/dependencies') {
9942: $output .= '<input type="hidden" name="action" value="process_changes" />';
1.987 raeburn 9943: }
1.1071 raeburn 9944: $output .= '<input type ="submit" value="'.$buttontext.'" />'."\n".'</form>'."\n";
1.987 raeburn 9945: } elsif ($numpathchg) {
9946: my %pathchange = ();
9947: $output .= &modify_html_form('pathchange',$actionurl,$state,\%pathchange,$pathchange_output);
9948: if (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
9949: $output .= '<p>'.&mt('or').'</p>';
9950: }
9951: }
1.1071 raeburn 9952: return ($output,$counter,$numpathchg);
1.987 raeburn 9953: }
9954:
9955: sub embedded_file_element {
1.1071 raeburn 9956: my ($context,$num,$embed_file,$mapping,$allfiles,$codebase,$type) = @_;
1.987 raeburn 9957: return unless ((ref($mapping) eq 'HASH') && (ref($allfiles) eq 'HASH') &&
9958: (ref($codebase) eq 'HASH'));
9959: my $output;
1.1071 raeburn 9960: if (($context eq 'upload_embedded') && ($type ne 'delete')) {
1.987 raeburn 9961: $output = '<input name="embedded_item_'.$num.'" type="file" value="" />'."\n";
9962: }
9963: $output .= '<input name="embedded_orig_'.$num.'" type="hidden" value="'.
9964: &escape($embed_file).'" />';
9965: unless (($context eq 'upload_embedded') &&
9966: ($mapping->{$embed_file} eq $embed_file)) {
9967: $output .='
9968: <input name="embedded_ref_'.$num.'" type="hidden" value="'.&escape($mapping->{$embed_file}).'" />';
9969: }
9970: my $attrib;
9971: if (ref($allfiles->{$mapping->{$embed_file}}) eq 'ARRAY') {
9972: $attrib = &escape(join(':',@{$allfiles->{$mapping->{$embed_file}}}));
9973: }
9974: $output .=
9975: "\n\t\t".
9976: '<input name="embedded_attrib_'.$num.'" type="hidden" value="'.
9977: $attrib.'" />';
9978: if (exists($codebase->{$mapping->{$embed_file}})) {
9979: $output .=
9980: "\n\t\t".
9981: '<input name="codebase_'.$num.'" type="hidden" value="'.
9982: &escape($codebase->{$mapping->{$embed_file}}).'" />';
1.984 raeburn 9983: }
1.987 raeburn 9984: return $output;
1.660 raeburn 9985: }
9986:
1.1071 raeburn 9987: sub get_dependency_details {
9988: my ($currfile,$currsubfile,$embed_file) = @_;
9989: my ($size,$mtime,$showsize,$showmtime);
9990: if ((ref($currfile) eq 'HASH') && (ref($currsubfile))) {
9991: if ($embed_file =~ m{/}) {
9992: my ($path,$fname) = split(/\//,$embed_file);
9993: if (ref($currsubfile->{$path}{$fname}) eq 'ARRAY') {
9994: ($size,$mtime) = @{$currsubfile->{$path}{$fname}};
9995: }
9996: } else {
9997: if (ref($currfile->{$embed_file}) eq 'ARRAY') {
9998: ($size,$mtime) = @{$currfile->{$embed_file}};
9999: }
10000: }
10001: $showsize = $size/1024.0;
10002: $showsize = sprintf("%.1f",$showsize);
10003: if ($mtime > 0) {
10004: $showmtime = &Apache::lonlocal::locallocaltime($mtime);
10005: }
10006: }
10007: return ($showsize,$showmtime);
10008: }
10009:
10010: sub ask_embedded_js {
10011: return <<"END";
10012: <script type="text/javascript"">
10013: // <![CDATA[
10014: function toggleBrowse(counter) {
10015: var chkboxid = document.getElementById('mod_upload_dep_'+counter);
10016: var fileid = document.getElementById('embedded_item_'+counter);
10017: var uploaddivid = document.getElementById('moduploaddep_'+counter);
10018: if (chkboxid.checked == true) {
10019: uploaddivid.style.display='block';
10020: } else {
10021: uploaddivid.style.display='none';
10022: fileid.value = '';
10023: }
10024: }
10025: // ]]>
10026: </script>
10027:
10028: END
10029: }
10030:
1.661 raeburn 10031: sub upload_embedded {
10032: my ($context,$dirpath,$uname,$udom,$dir_root,$url_root,$group,$disk_quota,
1.987 raeburn 10033: $current_disk_usage,$hiddenstate,$actionurl) = @_;
10034: my (%pathchange,$output,$modifyform,$footer,$returnflag);
1.661 raeburn 10035: for (my $i=0; $i<$env{'form.number_embedded_items'}; $i++) {
10036: next if (!exists($env{'form.embedded_item_'.$i.'.filename'}));
10037: my $orig_uploaded_filename =
10038: $env{'form.embedded_item_'.$i.'.filename'};
1.987 raeburn 10039: foreach my $type ('orig','ref','attrib','codebase') {
10040: if ($env{'form.embedded_'.$type.'_'.$i} ne '') {
10041: $env{'form.embedded_'.$type.'_'.$i} =
10042: &unescape($env{'form.embedded_'.$type.'_'.$i});
10043: }
10044: }
1.661 raeburn 10045: my ($path,$fname) =
10046: ($env{'form.embedded_orig_'.$i} =~ m{(.*/)([^/]*)});
10047: # no path, whole string is fname
10048: if (!$fname) { $fname = $env{'form.embedded_orig_'.$i} };
10049: $fname = &Apache::lonnet::clean_filename($fname);
10050: # See if there is anything left
10051: next if ($fname eq '');
10052:
10053: # Check if file already exists as a file or directory.
10054: my ($state,$msg);
10055: if ($context eq 'portfolio') {
10056: my $port_path = $dirpath;
10057: if ($group ne '') {
10058: $port_path = "groups/$group/$port_path";
10059: }
1.987 raeburn 10060: ($state,$msg) = &check_for_upload($env{'form.currentpath'}.$path,
10061: $fname,$group,'embedded_item_'.$i,
1.661 raeburn 10062: $dir_root,$port_path,$disk_quota,
10063: $current_disk_usage,$uname,$udom);
10064: if ($state eq 'will_exceed_quota'
1.984 raeburn 10065: || $state eq 'file_locked') {
1.661 raeburn 10066: $output .= $msg;
10067: next;
10068: }
10069: } elsif (($context eq 'author') || ($context eq 'testbank')) {
10070: ($state,$msg) = &check_for_existing($path,$fname,'embedded_item_'.$i);
10071: if ($state eq 'exists') {
10072: $output .= $msg;
10073: next;
10074: }
10075: }
10076: # Check if extension is valid
10077: if (($fname =~ /\.(\w+)$/) &&
10078: (&Apache::loncommon::fileembstyle($1) eq 'hdn')) {
1.987 raeburn 10079: $output .= &mt('Invalid file extension ([_1]) - reserved for LONCAPA use - rename the file with a different extension and re-upload. ',$1).'<br />';
1.661 raeburn 10080: next;
10081: } elsif (($fname =~ /\.(\w+)$/) &&
10082: (!defined(&Apache::loncommon::fileembstyle($1)))) {
1.987 raeburn 10083: $output .= &mt('Unrecognized file extension ([_1]) - rename the file with a proper extension and re-upload.',$1).'<br />';
1.661 raeburn 10084: next;
10085: } elsif ($fname=~/\.(\d+)\.(\w+)$/) {
1.987 raeburn 10086: $output .= &mt('File name not allowed - rename the file to remove the number immediately before the file extension([_1]) and re-upload.',$2).'<br />';
1.661 raeburn 10087: next;
10088: }
10089: $env{'form.embedded_item_'.$i.'.filename'}=$fname;
10090: if ($context eq 'portfolio') {
1.984 raeburn 10091: my $result;
10092: if ($state eq 'existingfile') {
10093: $result=
10094: &Apache::lonnet::userfileupload('embedded_item_'.$i,'existingfile',
1.987 raeburn 10095: $dirpath.$env{'form.currentpath'}.$path);
1.661 raeburn 10096: } else {
1.984 raeburn 10097: $result=
10098: &Apache::lonnet::userfileupload('embedded_item_'.$i,'',
1.987 raeburn 10099: $dirpath.
10100: $env{'form.currentpath'}.$path);
1.984 raeburn 10101: if ($result !~ m|^/uploaded/|) {
10102: $output .= '<span class="LC_error">'
10103: .&mt('An error occurred ([_1]) while trying to upload [_2] for embedded element [_3].'
10104: ,$result,$orig_uploaded_filename,$env{'form.embedded_orig_'.$i})
10105: .'</span><br />';
10106: next;
10107: } else {
1.987 raeburn 10108: $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
10109: $path.$fname.'</span>').'<br />';
1.984 raeburn 10110: }
1.661 raeburn 10111: }
1.987 raeburn 10112: } elsif ($context eq 'coursedoc') {
10113: my $result =
10114: &Apache::lonnet::userfileupload('embedded_item_'.$i,'coursedoc',
10115: $dirpath.'/'.$path);
10116: if ($result !~ m|^/uploaded/|) {
10117: $output .= '<span class="LC_error">'
10118: .&mt('An error occurred ([_1]) while trying to upload [_2] for embedded element [_3].'
10119: ,$result,$orig_uploaded_filename,$env{'form.embedded_orig_'.$i})
10120: .'</span><br />';
10121: next;
10122: } else {
10123: $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
10124: $path.$fname.'</span>').'<br />';
10125: }
1.661 raeburn 10126: } else {
10127: # Save the file
10128: my $target = $env{'form.embedded_item_'.$i};
10129: my $fullpath = $dir_root.$dirpath.'/'.$path;
10130: my $dest = $fullpath.$fname;
10131: my $url = $url_root.$dirpath.'/'.$path.$fname;
1.1027 raeburn 10132: my @parts=split(/\//,"$dirpath/$path");
1.661 raeburn 10133: my $count;
10134: my $filepath = $dir_root;
1.1027 raeburn 10135: foreach my $subdir (@parts) {
10136: $filepath .= "/$subdir";
10137: if (!-e $filepath) {
1.661 raeburn 10138: mkdir($filepath,0770);
10139: }
10140: }
10141: my $fh;
10142: if (!open($fh,'>'.$dest)) {
10143: &Apache::lonnet::logthis('Failed to create '.$dest);
10144: $output .= '<span class="LC_error">'.
1.1071 raeburn 10145: &mt('An error occurred while trying to upload [_1] for embedded element [_2].',
10146: $orig_uploaded_filename,$env{'form.embedded_orig_'.$i}).
1.661 raeburn 10147: '</span><br />';
10148: } else {
10149: if (!print $fh $env{'form.embedded_item_'.$i}) {
10150: &Apache::lonnet::logthis('Failed to write to '.$dest);
10151: $output .= '<span class="LC_error">'.
1.1071 raeburn 10152: &mt('An error occurred while writing the file [_1] for embedded element [_2].',
10153: $orig_uploaded_filename,$env{'form.embedded_orig_'.$i}).
1.661 raeburn 10154: '</span><br />';
10155: } else {
1.987 raeburn 10156: $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
10157: $url.'</span>').'<br />';
10158: unless ($context eq 'testbank') {
10159: $footer .= &mt('View embedded file: [_1]',
10160: '<a href="'.$url.'">'.$fname.'</a>').'<br />';
10161: }
10162: }
10163: close($fh);
10164: }
10165: }
10166: if ($env{'form.embedded_ref_'.$i}) {
10167: $pathchange{$i} = 1;
10168: }
10169: }
10170: if ($output) {
10171: $output = '<p>'.$output.'</p>';
10172: }
10173: $output .= &modify_html_form('upload_embedded',$actionurl,$hiddenstate,\%pathchange);
10174: $returnflag = 'ok';
1.1071 raeburn 10175: my $numpathchgs = scalar(keys(%pathchange));
10176: if ($numpathchgs > 0) {
1.987 raeburn 10177: if ($context eq 'portfolio') {
10178: $output .= '<p>'.&mt('or').'</p>';
10179: } elsif ($context eq 'testbank') {
1.1071 raeburn 10180: $output .= '<p>'.&mt('Or [_1]continue[_2] the testbank import without modifying the reference(s).',
10181: '<a href="javascript:document.testbankForm.submit();">','</a>').'</p>';
1.987 raeburn 10182: $returnflag = 'modify_orightml';
10183: }
10184: }
1.1071 raeburn 10185: return ($output.$footer,$returnflag,$numpathchgs);
1.987 raeburn 10186: }
10187:
10188: sub modify_html_form {
10189: my ($context,$actionurl,$hiddenstate,$pathchange,$pathchgtable) = @_;
10190: my $end = 0;
10191: my $modifyform;
10192: if ($context eq 'upload_embedded') {
10193: return unless (ref($pathchange) eq 'HASH');
10194: if ($env{'form.number_embedded_items'}) {
10195: $end += $env{'form.number_embedded_items'};
10196: }
10197: if ($env{'form.number_pathchange_items'}) {
10198: $end += $env{'form.number_pathchange_items'};
10199: }
10200: if ($end) {
10201: for (my $i=0; $i<$end; $i++) {
10202: if ($i < $env{'form.number_embedded_items'}) {
10203: next unless($pathchange->{$i});
10204: }
10205: $modifyform .=
10206: &start_data_table_row().
10207: '<td><input type ="checkbox" name="namechange" value="'.$i.'" '.
10208: 'checked="checked" /></td>'.
10209: '<td>'.$env{'form.embedded_ref_'.$i}.
10210: '<input type="hidden" name="embedded_ref_'.$i.'" value="'.
10211: &escape($env{'form.embedded_ref_'.$i}).'" />'.
10212: '<input type="hidden" name="embedded_codebase_'.$i.'" value="'.
10213: &escape($env{'form.embedded_codebase_'.$i}).'" />'.
10214: '<input type="hidden" name="embedded_attrib_'.$i.'" value="'.
10215: &escape($env{'form.embedded_attrib_'.$i}).'" /></td>'.
10216: '<td>'.$env{'form.embedded_orig_'.$i}.
10217: '<input type="hidden" name="embedded_orig_'.$i.'" value="'.
10218: &escape($env{'form.embedded_orig_'.$i}).'" /></td>'.
10219: &end_data_table_row();
1.1071 raeburn 10220: }
1.987 raeburn 10221: }
10222: } else {
10223: $modifyform = $pathchgtable;
10224: if (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
10225: $hiddenstate .= '<input type="hidden" name="phase" value="four" />';
10226: } elsif (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
10227: $hiddenstate .= '<input type="hidden" name="action" value="modify_orightml" />';
10228: }
10229: }
10230: if ($modifyform) {
1.1071 raeburn 10231: if ($actionurl eq '/adm/dependencies') {
10232: $hiddenstate .= '<input type="hidden" name="action" value="modifyhrefs" />';
10233: }
1.987 raeburn 10234: return '<h3>'.&mt('Changes in content of HTML file required').'</h3>'."\n".
10235: '<p>'.&mt('Changes need to be made to the reference(s) used for one or more of the dependencies, if your HTML file is to work correctly:').'<ol>'."\n".
10236: '<li>'.&mt('For consistency between the reference(s) and the location of the corresponding stored file within LON-CAPA.').'</li>'."\n".
10237: '<li>'.&mt('To change absolute paths to relative paths, or replace directory traversal via "../" within the original reference.').'</li>'."\n".
10238: '</ol></p>'."\n".'<p>'.
10239: &mt('LON-CAPA can make the required changes to your HTML file.').'</p>'."\n".
10240: '<form method="post" name="refchanger" action="'.$actionurl.'">'.
10241: &start_data_table()."\n".
10242: &start_data_table_header_row().
10243: '<th>'.&mt('Change?').'</th>'.
10244: '<th>'.&mt('Current reference').'</th>'.
10245: '<th>'.&mt('Required reference').'</th>'.
10246: &end_data_table_header_row()."\n".
10247: $modifyform.
10248: &end_data_table().'<br />'."\n".$hiddenstate.
10249: '<input type="submit" name="pathchanges" value="'.&mt('Modify HTML file').'" />'.
10250: '</form>'."\n";
10251: }
10252: return;
10253: }
10254:
10255: sub modify_html_refs {
10256: my ($context,$dirpath,$uname,$udom,$dir_root) = @_;
10257: my $container;
10258: if ($context eq 'portfolio') {
10259: $container = $env{'form.container'};
10260: } elsif ($context eq 'coursedoc') {
10261: $container = $env{'form.primaryurl'};
1.1071 raeburn 10262: } elsif ($context eq 'manage_dependencies') {
10263: (undef,undef,$container) = &Apache::lonnet::decode_symb($env{'form.symb'});
10264: $container = "/$container";
1.987 raeburn 10265: } else {
1.1027 raeburn 10266: $container = $Apache::lonnet::perlvar{'lonDocRoot'}.$env{'form.filename'};
1.987 raeburn 10267: }
10268: my (%allfiles,%codebase,$output,$content);
10269: my @changes = &get_env_multiple('form.namechange');
1.1071 raeburn 10270: unless (@changes > 0) {
10271: if (wantarray) {
10272: return ('',0,0);
10273: } else {
10274: return;
10275: }
10276: }
10277: if (($context eq 'portfolio') || ($context eq 'coursedoc') ||
10278: ($context eq 'manage_dependencies')) {
10279: unless ($container =~ m{^/uploaded/\Q$udom\E/\Q$uname\E/}) {
10280: if (wantarray) {
10281: return ('',0,0);
10282: } else {
10283: return;
10284: }
10285: }
1.987 raeburn 10286: $content = &Apache::lonnet::getfile($container);
1.1071 raeburn 10287: if ($content eq '-1') {
10288: if (wantarray) {
10289: return ('',0,0);
10290: } else {
10291: return;
10292: }
10293: }
1.987 raeburn 10294: } else {
1.1071 raeburn 10295: unless ($container =~ /^\Q$dir_root\E/) {
10296: if (wantarray) {
10297: return ('',0,0);
10298: } else {
10299: return;
10300: }
10301: }
1.987 raeburn 10302: if (open(my $fh,"<$container")) {
10303: $content = join('', <$fh>);
10304: close($fh);
10305: } else {
1.1071 raeburn 10306: if (wantarray) {
10307: return ('',0,0);
10308: } else {
10309: return;
10310: }
1.987 raeburn 10311: }
10312: }
10313: my ($count,$codebasecount) = (0,0);
10314: my $mm = new File::MMagic;
10315: my $mime_type = $mm->checktype_contents($content);
10316: if ($mime_type eq 'text/html') {
10317: my $parse_result =
10318: &Apache::lonnet::extract_embedded_items($container,\%allfiles,
10319: \%codebase,\$content);
10320: if ($parse_result eq 'ok') {
10321: foreach my $i (@changes) {
10322: my $orig = &unescape($env{'form.embedded_orig_'.$i});
10323: my $ref = &unescape($env{'form.embedded_ref_'.$i});
10324: if ($allfiles{$ref}) {
10325: my $newname = $orig;
10326: my ($attrib_regexp,$codebase);
1.1006 raeburn 10327: $attrib_regexp = &unescape($env{'form.embedded_attrib_'.$i});
1.987 raeburn 10328: if ($attrib_regexp =~ /:/) {
10329: $attrib_regexp =~ s/\:/|/g;
10330: }
10331: if ($content =~ m{($attrib_regexp\s*=\s*['"]?)\Q$ref\E(['"]?)}) {
10332: my $numchg = ($content =~ s{($attrib_regexp\s*=\s*['"]?)\Q$ref\E(['"]?)}{$1$newname$2}gi);
10333: $count += $numchg;
10334: }
10335: if ($env{'form.embedded_codebase_'.$i} ne '') {
1.1006 raeburn 10336: $codebase = &unescape($env{'form.embedded_codebase_'.$i});
1.987 raeburn 10337: my $numchg = ($content =~ s/(codebase\s*=\s*["']?)\Q$codebase\E(["']?)/$1.$2/i); #' stupid emacs
10338: $codebasecount ++;
10339: }
10340: }
10341: }
10342: if ($count || $codebasecount) {
10343: my $saveresult;
1.1071 raeburn 10344: if (($context eq 'portfolio') || ($context eq 'coursedoc') ||
10345: ($context eq 'manage_dependencies')) {
1.987 raeburn 10346: my $url = &Apache::lonnet::store_edited_file($container,$content,$udom,$uname,\$saveresult);
10347: if ($url eq $container) {
10348: my ($fname) = ($container =~ m{/([^/]+)$});
10349: $output = '<p>'.&mt('Updated [quant,_1,reference] in [_2].',
10350: $count,'<span class="LC_filename">'.
1.1071 raeburn 10351: $fname.'</span>').'</p>';
1.987 raeburn 10352: } else {
10353: $output = '<p class="LC_error">'.
10354: &mt('Error: update failed for: [_1].',
10355: '<span class="LC_filename">'.
10356: $container.'</span>').'</p>';
10357: }
10358: } else {
10359: if (open(my $fh,">$container")) {
10360: print $fh $content;
10361: close($fh);
10362: $output = '<p>'.&mt('Updated [quant,_1,reference] in [_2].',
10363: $count,'<span class="LC_filename">'.
10364: $container.'</span>').'</p>';
1.661 raeburn 10365: } else {
1.987 raeburn 10366: $output = '<p class="LC_error">'.
10367: &mt('Error: could not update [_1].',
10368: '<span class="LC_filename">'.
10369: $container.'</span>').'</p>';
1.661 raeburn 10370: }
10371: }
10372: }
1.987 raeburn 10373: } else {
10374: &logthis('Failed to parse '.$container.
10375: ' to modify references: '.$parse_result);
1.661 raeburn 10376: }
10377: }
1.1071 raeburn 10378: if (wantarray) {
10379: return ($output,$count,$codebasecount);
10380: } else {
10381: return $output;
10382: }
1.661 raeburn 10383: }
10384:
10385: sub check_for_existing {
10386: my ($path,$fname,$element) = @_;
10387: my ($state,$msg);
10388: if (-d $path.'/'.$fname) {
10389: $state = 'exists';
10390: $msg = &mt('Unable to upload [_1]. A directory by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$path);
10391: } elsif (-e $path.'/'.$fname) {
10392: $state = 'exists';
10393: $msg = &mt('Unable to upload [_1]. A file by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$path);
10394: }
10395: if ($state eq 'exists') {
10396: $msg = '<span class="LC_error">'.$msg.'</span><br />';
10397: }
10398: return ($state,$msg);
10399: }
10400:
10401: sub check_for_upload {
10402: my ($path,$fname,$group,$element,$portfolio_root,$port_path,
10403: $disk_quota,$current_disk_usage,$uname,$udom) = @_;
1.985 raeburn 10404: my $filesize = length($env{'form.'.$element});
10405: if (!$filesize) {
10406: my $msg = '<span class="LC_error">'.
10407: &mt('Unable to upload [_1]. (size = [_2] bytes)',
10408: '<span class="LC_filename">'.$fname.'</span>',
10409: $filesize).'<br />'.
1.1007 raeburn 10410: &mt('Either the file you attempted to upload was empty, or your web browser was unable to read its contents.').'<br />'.
1.985 raeburn 10411: '</span>';
10412: return ('zero_bytes',$msg);
10413: }
10414: $filesize = $filesize/1000; #express in k (1024?)
1.661 raeburn 10415: my $getpropath = 1;
1.1021 raeburn 10416: my ($dirlistref,$listerror) =
10417: &Apache::lonnet::dirlist($portfolio_root.$path,$udom,$uname,$getpropath);
1.661 raeburn 10418: my $found_file = 0;
10419: my $locked_file = 0;
1.991 raeburn 10420: my @lockers;
10421: my $navmap;
10422: if ($env{'request.course.id'}) {
10423: $navmap = Apache::lonnavmaps::navmap->new();
10424: }
1.1021 raeburn 10425: if (ref($dirlistref) eq 'ARRAY') {
10426: foreach my $line (@{$dirlistref}) {
10427: my ($file_name,$rest)=split(/\&/,$line,2);
10428: if ($file_name eq $fname){
10429: $file_name = $path.$file_name;
10430: if ($group ne '') {
10431: $file_name = $group.$file_name;
10432: }
10433: $found_file = 1;
10434: if (&Apache::lonnet::is_locked($file_name,$udom,$uname,\@lockers) eq 'true') {
10435: foreach my $lock (@lockers) {
10436: if (ref($lock) eq 'ARRAY') {
10437: my ($symb,$crsid) = @{$lock};
10438: if ($crsid eq $env{'request.course.id'}) {
10439: if (ref($navmap)) {
10440: my $res = $navmap->getBySymb($symb);
10441: foreach my $part (@{$res->parts()}) {
10442: my ($slot_status,$slot_time,$slot_name)=$res->check_for_slot($part);
10443: unless (($slot_status == $res->RESERVED) ||
10444: ($slot_status == $res->RESERVED_LOCATION)) {
10445: $locked_file = 1;
10446: }
1.991 raeburn 10447: }
1.1021 raeburn 10448: } else {
10449: $locked_file = 1;
1.991 raeburn 10450: }
10451: } else {
10452: $locked_file = 1;
10453: }
10454: }
1.1021 raeburn 10455: }
10456: } else {
10457: my @info = split(/\&/,$rest);
10458: my $currsize = $info[6]/1000;
10459: if ($currsize < $filesize) {
10460: my $extra = $filesize - $currsize;
10461: if (($current_disk_usage + $extra) > $disk_quota) {
10462: my $msg = '<span class="LC_error">'.
10463: &mt('Unable to upload [_1]. (size = [_2] kilobytes). Disk quota will be exceeded if existing (smaller) file with same name (size = [_3] kilobytes) is replaced.',
10464: '<span class="LC_filename">'.$fname.'</span>',$filesize,$currsize).'</span>'.
10465: '<br />'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',
10466: $disk_quota,$current_disk_usage);
10467: return ('will_exceed_quota',$msg);
10468: }
1.984 raeburn 10469: }
10470: }
1.661 raeburn 10471: }
10472: }
10473: }
10474: if (($current_disk_usage + $filesize) > $disk_quota){
10475: my $msg = '<span class="LC_error">'.
10476: &mt('Unable to upload [_1]. (size = [_2] kilobytes). Disk quota will be exceeded.','<span class="LC_filename">'.$fname.'</span>',$filesize).'</span>'.
10477: '<br />'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',$disk_quota,$current_disk_usage);
10478: return ('will_exceed_quota',$msg);
10479: } elsif ($found_file) {
10480: if ($locked_file) {
10481: my $msg = '<span class="LC_error">';
10482: $msg .= &mt('Unable to upload [_1]. A locked file by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>','<span class="LC_filename">'.$port_path.$env{'form.currentpath'}.'</span>');
10483: $msg .= '</span><br />';
10484: $msg .= &mt('You will be able to rename or delete existing [_1] after a grade has been assigned.','<span class="LC_filename">'.$fname.'</span>');
10485: return ('file_locked',$msg);
10486: } else {
10487: my $msg = '<span class="LC_error">';
1.984 raeburn 10488: $msg .= &mt(' A file by that name: [_1] was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$port_path.$env{'form.currentpath'});
1.661 raeburn 10489: $msg .= '</span>';
1.984 raeburn 10490: return ('existingfile',$msg);
1.661 raeburn 10491: }
10492: }
10493: }
10494:
1.987 raeburn 10495: sub check_for_traversal {
10496: my ($path,$url,$toplevel) = @_;
10497: my @parts=split(/\//,$path);
10498: my $cleanpath;
10499: my $fullpath = $url;
10500: for (my $i=0;$i<@parts;$i++) {
10501: next if ($parts[$i] eq '.');
10502: if ($parts[$i] eq '..') {
10503: $fullpath =~ s{([^/]+/)$}{};
10504: } else {
10505: $fullpath .= $parts[$i].'/';
10506: }
10507: }
10508: if ($fullpath =~ /^\Q$url\E(.*)$/) {
10509: $cleanpath = $1;
10510: } elsif ($fullpath =~ /^\Q$toplevel\E(.*)$/) {
10511: my $curr_toprel = $1;
10512: my @parts = split(/\//,$curr_toprel);
10513: my ($url_toprel) = ($url =~ /^\Q$toplevel\E(.*)$/);
10514: my @urlparts = split(/\//,$url_toprel);
10515: my $doubledots;
10516: my $startdiff = -1;
10517: for (my $i=0; $i<@urlparts; $i++) {
10518: if ($startdiff == -1) {
10519: unless ($urlparts[$i] eq $parts[$i]) {
10520: $startdiff = $i;
10521: $doubledots .= '../';
10522: }
10523: } else {
10524: $doubledots .= '../';
10525: }
10526: }
10527: if ($startdiff > -1) {
10528: $cleanpath = $doubledots;
10529: for (my $i=$startdiff; $i<@parts; $i++) {
10530: $cleanpath .= $parts[$i].'/';
10531: }
10532: }
10533: }
10534: $cleanpath =~ s{(/)$}{};
10535: return $cleanpath;
10536: }
1.31 albertel 10537:
1.1053 raeburn 10538: sub is_archive_file {
10539: my ($mimetype) = @_;
10540: if (($mimetype eq 'application/octet-stream') ||
10541: ($mimetype eq 'application/x-stuffit') ||
10542: ($mimetype =~ m{^application/(x\-)?(compressed|tar|zip|tgz|gz|gtar|gzip|gunzip|bz|bz2|bzip2)})) {
10543: return 1;
10544: }
10545: return;
10546: }
10547:
10548: sub decompress_form {
1.1065 raeburn 10549: my ($mimetype,$archiveurl,$action,$noextract,$hiddenelements,$dirlist) = @_;
1.1053 raeburn 10550: my %lt = &Apache::lonlocal::texthash (
10551: this => 'This file is an archive file.',
1.1067 raeburn 10552: camt => 'This file is a Camtasia archive file.',
1.1065 raeburn 10553: itsc => 'Its contents are as follows:',
1.1053 raeburn 10554: youm => 'You may wish to extract its contents.',
10555: extr => 'Extract contents',
1.1067 raeburn 10556: auto => 'LON-CAPA can process the files automatically, or you can decide how each should be handled.',
10557: proa => 'Process automatically?',
1.1053 raeburn 10558: yes => 'Yes',
10559: no => 'No',
1.1067 raeburn 10560: fold => 'Title for folder containing movie',
10561: movi => 'Title for page containing embedded movie',
1.1053 raeburn 10562: );
1.1065 raeburn 10563: my $fileloc = &Apache::lonnet::filelocation(undef,$archiveurl);
1.1067 raeburn 10564: my ($is_camtasia,$topdir,%toplevel,@paths);
1.1065 raeburn 10565: my $info = &list_archive_contents($fileloc,\@paths);
10566: if (@paths) {
10567: foreach my $path (@paths) {
10568: $path =~ s{^/}{};
1.1067 raeburn 10569: if ($path =~ m{^([^/]+)/$}) {
10570: $topdir = $1;
10571: }
1.1065 raeburn 10572: if ($path =~ m{^([^/]+)/}) {
10573: $toplevel{$1} = $path;
10574: } else {
10575: $toplevel{$path} = $path;
10576: }
10577: }
10578: }
1.1067 raeburn 10579: if ($mimetype =~ m{^application/(x\-)?(compressed|zip)}) {
10580: my @camtasia = ("$topdir/","$topdir/index.html",
10581: "$topdir/media/",
10582: "$topdir/media/$topdir.mp4",
10583: "$topdir/media/FirstFrame.png",
10584: "$topdir/media/player.swf",
10585: "$topdir/media/swfobject.js",
10586: "$topdir/media/expressInstall.swf");
10587: my @diffs = &compare_arrays(\@paths,\@camtasia);
10588: if (@diffs == 0) {
10589: $is_camtasia = 1;
10590: }
10591: }
10592: my $output;
10593: if ($is_camtasia) {
10594: $output = <<"ENDCAM";
10595: <script type="text/javascript" language="Javascript">
10596: // <![CDATA[
10597:
10598: function camtasiaToggle() {
10599: for (var i=0; i<document.uploaded_decompress.autoextract_camtasia.length; i++) {
10600: if (document.uploaded_decompress.autoextract_camtasia[i].checked) {
10601: if (document.uploaded_decompress.autoextract_camtasia[i].value == 1) {
10602:
10603: document.getElementById('camtasia_titles').style.display='block';
10604: } else {
10605: document.getElementById('camtasia_titles').style.display='none';
10606: }
10607: }
10608: }
10609: return;
10610: }
10611:
10612: // ]]>
10613: </script>
10614: <p>$lt{'camt'}</p>
10615: ENDCAM
1.1065 raeburn 10616: } else {
1.1067 raeburn 10617: $output = '<p>'.$lt{'this'};
10618: if ($info eq '') {
10619: $output .= ' '.$lt{'youm'}.'</p>'."\n";
10620: } else {
10621: $output .= ' '.$lt{'itsc'}.'</p>'."\n".
10622: '<div><pre>'.$info.'</pre></div>';
10623: }
1.1065 raeburn 10624: }
1.1067 raeburn 10625: $output .= '<form name="uploaded_decompress" action="'.$action.'" method="post">'."\n";
1.1065 raeburn 10626: my $duplicates;
10627: my $num = 0;
10628: if (ref($dirlist) eq 'ARRAY') {
10629: foreach my $item (@{$dirlist}) {
10630: if (ref($item) eq 'ARRAY') {
10631: if (exists($toplevel{$item->[0]})) {
10632: $duplicates .=
10633: &start_data_table_row().
10634: '<td><label><input type="radio" name="archive_overwrite_'.$num.'" '.
10635: 'value="0" checked="checked" />'.&mt('No').'</label>'.
10636: ' <label><input type="radio" name="archive_overwrite_'.$num.'" '.
10637: 'value="1" />'.&mt('Yes').'</label>'.
10638: '<input type="hidden" name="archive_overwrite_name_'.$num.'" value="'.$item->[0].'" /></td>'."\n".
10639: '<td>'.$item->[0].'</td>';
10640: if ($item->[2]) {
10641: $duplicates .= '<td>'.&mt('Directory').'</td>';
10642: } else {
10643: $duplicates .= '<td>'.&mt('File').'</td>';
10644: }
10645: $duplicates .= '<td>'.$item->[3].'</td>'.
10646: '<td>'.
10647: &Apache::lonlocal::locallocaltime($item->[4]).
10648: '</td>'.
10649: &end_data_table_row();
10650: $num ++;
10651: }
10652: }
10653: }
10654: }
10655: my $itemcount;
10656: if (@paths > 0) {
10657: $itemcount = scalar(@paths);
10658: } else {
10659: $itemcount = 1;
10660: }
1.1067 raeburn 10661: if ($is_camtasia) {
10662: $output .= $lt{'auto'}.'<br />'.
10663: '<span class="LC_nobreak">'.$lt{'proa'}.'<label>'.
10664: '<input type="radio" name="autoextract_camtasia" value="1" onclick="javascript:camtasiaToggle();" checked="checked" />'.
10665: $lt{'yes'}.'</label> <label>'.
10666: '<input type="radio" name="autoextract_camtasia" value="0" onclick="javascript:camtasiaToggle();" />'.
10667: $lt{'no'}.'</label></span><br />'.
10668: '<div id="camtasia_titles" style="display:block">'.
10669: &Apache::lonhtmlcommon::start_pick_box().
10670: &Apache::lonhtmlcommon::row_title($lt{'fold'}).
10671: '<input type="textbox" name="camtasia_foldername" value="'.$env{'form.comment'}.'" />'."\n".
10672: &Apache::lonhtmlcommon::row_closure().
10673: &Apache::lonhtmlcommon::row_title($lt{'movi'}).
10674: '<input type="textbox" name="camtasia_moviename" value="" />'."\n".
10675: &Apache::lonhtmlcommon::row_closure(1).
10676: &Apache::lonhtmlcommon::end_pick_box().
10677: '</div>';
10678: }
1.1065 raeburn 10679: $output .=
10680: '<input type="hidden" name="archive_overwrite_total" value="'.$num.'" />'.
1.1067 raeburn 10681: '<input type="hidden" name="archive_itemcount" value="'.$itemcount.'" />'.
10682: "\n";
1.1065 raeburn 10683: if ($duplicates ne '') {
10684: $output .= '<p><span class="LC_warning">'.
10685: &mt('Warning: decompression of the archive will overwrite the following items which already exist:').'</span><br />'.
10686: &start_data_table().
10687: &start_data_table_header_row().
10688: '<th>'.&mt('Overwrite?').'</th>'.
10689: '<th>'.&mt('Name').'</th>'.
10690: '<th>'.&mt('Type').'</th>'.
10691: '<th>'.&mt('Size').'</th>'.
10692: '<th>'.&mt('Last modified').'</th>'.
10693: &end_data_table_header_row().
10694: $duplicates.
10695: &end_data_table().
10696: '</p>';
10697: }
1.1067 raeburn 10698: $output .= '<input type="hidden" name="archiveurl" value="'.$archiveurl.'" />'."\n";
1.1053 raeburn 10699: if (ref($hiddenelements) eq 'HASH') {
10700: foreach my $hidden (sort(keys(%{$hiddenelements}))) {
10701: $output .= '<input type="hidden" name="'.$hidden.'" value="'.$hiddenelements->{$hidden}.'" />'."\n";
10702: }
10703: }
10704: $output .= <<"END";
1.1067 raeburn 10705: <br />
1.1053 raeburn 10706: <input type="submit" name="decompress" value="$lt{'extr'}" />
10707: </form>
10708: $noextract
10709: END
10710: return $output;
10711: }
10712:
1.1065 raeburn 10713: sub decompression_utility {
10714: my ($program) = @_;
10715: my @utilities = ('tar','gunzip','bunzip2','unzip');
10716: my $location;
10717: if (grep(/^\Q$program\E$/,@utilities)) {
10718: foreach my $dir ('/bin/','/usr/bin/','/usr/local/bin/','/sbin/',
10719: '/usr/sbin/') {
10720: if (-x $dir.$program) {
10721: $location = $dir.$program;
10722: last;
10723: }
10724: }
10725: }
10726: return $location;
10727: }
10728:
10729: sub list_archive_contents {
10730: my ($file,$pathsref) = @_;
10731: my (@cmd,$output);
10732: my $needsregexp;
10733: if ($file =~ /\.zip$/) {
10734: @cmd = (&decompression_utility('unzip'),"-l");
10735: $needsregexp = 1;
10736: } elsif (($file =~ m/\.tar\.gz$/) ||
10737: ($file =~ /\.tgz$/)) {
10738: @cmd = (&decompression_utility('tar'),"-ztf");
10739: } elsif ($file =~ /\.tar\.bz2$/) {
10740: @cmd = (&decompression_utility('tar'),"-jtf");
10741: } elsif ($file =~ m|\.tar$|) {
10742: @cmd = (&decompression_utility('tar'),"-tf");
10743: }
10744: if (@cmd) {
10745: undef($!);
10746: undef($@);
10747: if (open(my $fh,"-|", @cmd, $file)) {
10748: while (my $line = <$fh>) {
10749: $output .= $line;
10750: chomp($line);
10751: my $item;
10752: if ($needsregexp) {
10753: ($item) = ($line =~ /^\s*\d+\s+[\d\-]+\s+[\d:]+\s*(.+)$/);
10754: } else {
10755: $item = $line;
10756: }
10757: if ($item ne '') {
10758: unless (grep(/^\Q$item\E$/,@{$pathsref})) {
10759: push(@{$pathsref},$item);
10760: }
10761: }
10762: }
10763: close($fh);
10764: }
10765: }
10766: return $output;
10767: }
10768:
1.1053 raeburn 10769: sub decompress_uploaded_file {
10770: my ($file,$dir) = @_;
10771: &Apache::lonnet::appenv({'cgi.file' => $file});
10772: &Apache::lonnet::appenv({'cgi.dir' => $dir});
10773: my $result = &Apache::lonnet::ssi_body('/cgi-bin/decompress.pl');
10774: my ($handle) = ($env{'user.environment'} =~m{/([^/]+)\.id$});
10775: my $lonidsdir = $Apache::lonnet::perlvar{'lonIDsDir'};
10776: &Apache::lonnet::transfer_profile_to_env($lonidsdir,$handle,1);
10777: my $decompressed = $env{'cgi.decompressed'};
10778: &Apache::lonnet::delenv('cgi.file');
10779: &Apache::lonnet::delenv('cgi.dir');
10780: &Apache::lonnet::delenv('cgi.decompressed');
10781: return ($decompressed,$result);
10782: }
10783:
1.1055 raeburn 10784: sub process_decompression {
10785: my ($docudom,$docuname,$file,$destination,$dir_root,$hiddenelem) = @_;
10786: my ($dir,$error,$warning,$output);
10787: if ($file !~ /\.(zip|tar|bz2|gz|tar.gz|tar.bz2|tgz)$/) {
10788: $error = &mt('File name not a supported archive file type.').
10789: '<br />'.&mt('File name should end with one of: [_1].',
10790: '.zip, .tar, .bz2, .gz, .tar.gz, .tar.bz2, .tgz');
10791: } else {
10792: my $docuhome = &Apache::lonnet::homeserver($docuname,$docudom);
10793: if ($docuhome eq 'no_host') {
10794: $error = &mt('Could not determine home server for course.');
10795: } else {
10796: my @ids=&Apache::lonnet::current_machine_ids();
10797: my $currdir = "$dir_root/$destination";
10798: if (grep(/^\Q$docuhome\E$/,@ids)) {
10799: $dir = &LONCAPA::propath($docudom,$docuname).
10800: "$dir_root/$destination";
10801: } else {
10802: $dir = $Apache::lonnet::perlvar{'lonDocRoot'}.
10803: "$dir_root/$docudom/$docuname/$destination";
10804: unless (&Apache::lonnet::repcopy_userfile("$dir/$file") eq 'ok') {
10805: $error = &mt('Archive file not found.');
10806: }
10807: }
1.1065 raeburn 10808: my (@to_overwrite,@to_skip);
10809: if ($env{'form.archive_overwrite_total'} > 0) {
10810: my $total = $env{'form.archive_overwrite_total'};
10811: for (my $i=0; $i<$total; $i++) {
10812: if ($env{'form.archive_overwrite_'.$i} == 1) {
10813: push(@to_overwrite,$env{'form.archive_overwrite_name_'.$i});
10814: } elsif ($env{'form.archive_overwrite_'.$i} == 0) {
10815: push(@to_skip,$env{'form.archive_overwrite_name_'.$i});
10816: }
10817: }
10818: }
10819: my $numskip = scalar(@to_skip);
10820: if (($numskip > 0) &&
10821: ($numskip == $env{'form.archive_itemcount'})) {
10822: $warning = &mt('All items in the archive file already exist, and no overwriting of existing files has been requested.');
10823: } elsif ($dir eq '') {
1.1055 raeburn 10824: $error = &mt('Directory containing archive file unavailable.');
10825: } elsif (!$error) {
1.1065 raeburn 10826: my ($decompressed,$display);
10827: if ($numskip > 0) {
10828: my $tempdir = time.'_'.$$.int(rand(10000));
10829: mkdir("$dir/$tempdir",0755);
10830: system("mv $dir/$file $dir/$tempdir/$file");
10831: ($decompressed,$display) =
10832: &decompress_uploaded_file($file,"$dir/$tempdir");
10833: foreach my $item (@to_skip) {
10834: if (($item ne '') && ($item !~ /\.\./)) {
10835: if (-f "$dir/$tempdir/$item") {
10836: unlink("$dir/$tempdir/$item");
10837: } elsif (-d "$dir/$tempdir/$item") {
10838: system("rm -rf $dir/$tempdir/$item");
10839: }
10840: }
10841: }
10842: system("mv $dir/$tempdir/* $dir");
10843: rmdir("$dir/$tempdir");
10844: } else {
10845: ($decompressed,$display) =
10846: &decompress_uploaded_file($file,$dir);
10847: }
1.1055 raeburn 10848: if ($decompressed eq 'ok') {
1.1065 raeburn 10849: $output = '<p class="LC_info">'.
10850: &mt('Files extracted successfully from archive.').
10851: '</p>'."\n";
1.1055 raeburn 10852: my ($warning,$result,@contents);
10853: my ($newdirlistref,$newlisterror) =
10854: &Apache::lonnet::dirlist($currdir,$docudom,
10855: $docuname,1);
10856: my (%is_dir,%changes,@newitems);
10857: my $dirptr = 16384;
1.1065 raeburn 10858: if (ref($newdirlistref) eq 'ARRAY') {
1.1055 raeburn 10859: foreach my $dir_line (@{$newdirlistref}) {
10860: my ($item,undef,undef,$testdir)=split(/\&/,$dir_line,5);
1.1065 raeburn 10861: unless (($item =~ /^\.+$/) || ($item eq $file) ||
10862: ((@to_skip > 0) && (grep(/^\Q$item\E$/,@to_skip)))) {
1.1055 raeburn 10863: push(@newitems,$item);
10864: if ($dirptr&$testdir) {
10865: $is_dir{$item} = 1;
10866: }
10867: $changes{$item} = 1;
10868: }
10869: }
10870: }
10871: if (keys(%changes) > 0) {
10872: foreach my $item (sort(@newitems)) {
10873: if ($changes{$item}) {
10874: push(@contents,$item);
10875: }
10876: }
10877: }
10878: if (@contents > 0) {
1.1067 raeburn 10879: my $wantform;
10880: unless ($env{'form.autoextract_camtasia'}) {
10881: $wantform = 1;
10882: }
1.1056 raeburn 10883: my (%children,%parent,%dirorder,%titles);
1.1055 raeburn 10884: my ($count,$datatable) = &get_extracted($docudom,$docuname,
10885: $currdir,\%is_dir,
10886: \%children,\%parent,
1.1056 raeburn 10887: \@contents,\%dirorder,
10888: \%titles,$wantform);
1.1055 raeburn 10889: if ($datatable ne '') {
10890: $output .= &archive_options_form('decompressed',$datatable,
10891: $count,$hiddenelem);
1.1065 raeburn 10892: my $startcount = 6;
1.1055 raeburn 10893: $output .= &archive_javascript($startcount,$count,
1.1056 raeburn 10894: \%titles,\%children);
1.1055 raeburn 10895: }
1.1067 raeburn 10896: if ($env{'form.autoextract_camtasia'}) {
10897: my %displayed;
10898: my $total = 1;
10899: $env{'form.archive_directory'} = [];
10900: foreach my $i (sort { $a <=> $b } keys(%dirorder)) {
10901: my $path = join('/',map { $titles{$_}; } @{$dirorder{$i}});
10902: $path =~ s{/$}{};
10903: my $item;
10904: if ($path ne '') {
10905: $item = "$path/$titles{$i}";
10906: } else {
10907: $item = $titles{$i};
10908: }
10909: $env{'form.archive_content_'.$i} = "$dir_root/$destination/$item";
10910: if ($item eq $contents[0]) {
10911: push(@{$env{'form.archive_directory'}},$i);
10912: $env{'form.archive_'.$i} = 'display';
10913: $env{'form.archive_title_'.$i} = $env{'form.camtasia_foldername'};
10914: $displayed{'folder'} = $i;
10915: } elsif ($item eq "$contents[0]/index.html") {
10916: $env{'form.archive_'.$i} = 'display';
10917: $env{'form.archive_title_'.$i} = $env{'form.camtasia_moviename'};
10918: $displayed{'web'} = $i;
10919: } else {
10920: if ($item eq "$contents[0]/media") {
10921: push(@{$env{'form.archive_directory'}},$i);
10922: }
10923: $env{'form.archive_'.$i} = 'dependency';
10924: }
10925: $total ++;
10926: }
10927: for (my $i=1; $i<$total; $i++) {
10928: next if ($i == $displayed{'web'});
10929: next if ($i == $displayed{'folder'});
10930: $env{'form.archive_dependent_on_'.$i} = $displayed{'web'};
10931: }
10932: $env{'form.phase'} = 'decompress_cleanup';
10933: $env{'form.archivedelete'} = 1;
10934: $env{'form.archive_count'} = $total-1;
10935: $output .=
10936: &process_extracted_files('coursedocs',$docudom,
10937: $docuname,$destination,
10938: $dir_root,$hiddenelem);
10939: }
1.1055 raeburn 10940: } else {
10941: $warning = &mt('No new items extracted from archive file.');
10942: }
10943: } else {
10944: $output = $display;
10945: $error = &mt('An error occurred during extraction from the archive file.');
10946: }
10947: }
10948: }
10949: }
10950: if ($error) {
10951: $output .= '<p class="LC_error">'.&mt('Not extracted.').'<br />'.
10952: $error.'</p>'."\n";
10953: }
10954: if ($warning) {
10955: $output .= '<p class="LC_warning">'.$warning.'</p>'."\n";
10956: }
10957: return $output;
10958: }
10959:
10960: sub get_extracted {
1.1056 raeburn 10961: my ($docudom,$docuname,$currdir,$is_dir,$children,$parent,$contents,$dirorder,
10962: $titles,$wantform) = @_;
1.1055 raeburn 10963: my $count = 0;
10964: my $depth = 0;
10965: my $datatable;
1.1056 raeburn 10966: my @hierarchy;
1.1055 raeburn 10967: return unless ((ref($is_dir) eq 'HASH') && (ref($children) eq 'HASH') &&
1.1056 raeburn 10968: (ref($parent) eq 'HASH') && (ref($contents) eq 'ARRAY') &&
10969: (ref($dirorder) eq 'HASH') && (ref($titles) eq 'HASH'));
1.1055 raeburn 10970: foreach my $item (@{$contents}) {
10971: $count ++;
1.1056 raeburn 10972: @{$dirorder->{$count}} = @hierarchy;
10973: $titles->{$count} = $item;
1.1055 raeburn 10974: &archive_hierarchy($depth,$count,$parent,$children);
10975: if ($wantform) {
10976: $datatable .= &archive_row($is_dir->{$item},$item,
10977: $currdir,$depth,$count);
10978: }
10979: if ($is_dir->{$item}) {
10980: $depth ++;
1.1056 raeburn 10981: push(@hierarchy,$count);
10982: $parent->{$depth} = $count;
1.1055 raeburn 10983: $datatable .=
10984: &recurse_extracted_archive("$currdir/$item",$docudom,$docuname,
1.1056 raeburn 10985: \$depth,\$count,\@hierarchy,$dirorder,
10986: $children,$parent,$titles,$wantform);
1.1055 raeburn 10987: $depth --;
1.1056 raeburn 10988: pop(@hierarchy);
1.1055 raeburn 10989: }
10990: }
10991: return ($count,$datatable);
10992: }
10993:
10994: sub recurse_extracted_archive {
1.1056 raeburn 10995: my ($currdir,$docudom,$docuname,$depth,$count,$hierarchy,$dirorder,
10996: $children,$parent,$titles,$wantform) = @_;
1.1055 raeburn 10997: my $result='';
1.1056 raeburn 10998: unless ((ref($depth)) && (ref($count)) && (ref($hierarchy) eq 'ARRAY') &&
10999: (ref($children) eq 'HASH') && (ref($parent) eq 'HASH') &&
11000: (ref($dirorder) eq 'HASH')) {
1.1055 raeburn 11001: return $result;
11002: }
11003: my $dirptr = 16384;
11004: my ($newdirlistref,$newlisterror) =
11005: &Apache::lonnet::dirlist($currdir,$docudom,$docuname,1);
11006: if (ref($newdirlistref) eq 'ARRAY') {
11007: foreach my $dir_line (@{$newdirlistref}) {
11008: my ($item,undef,undef,$testdir)=split(/\&/,$dir_line,5);
11009: unless ($item =~ /^\.+$/) {
11010: $$count ++;
1.1056 raeburn 11011: @{$dirorder->{$$count}} = @{$hierarchy};
11012: $titles->{$$count} = $item;
1.1055 raeburn 11013: &archive_hierarchy($$depth,$$count,$parent,$children);
1.1056 raeburn 11014:
1.1055 raeburn 11015: my $is_dir;
11016: if ($dirptr&$testdir) {
11017: $is_dir = 1;
11018: }
11019: if ($wantform) {
11020: $result .= &archive_row($is_dir,$item,$currdir,$$depth,$$count);
11021: }
11022: if ($is_dir) {
11023: $$depth ++;
1.1056 raeburn 11024: push(@{$hierarchy},$$count);
11025: $parent->{$$depth} = $$count;
1.1055 raeburn 11026: $result .=
11027: &recurse_extracted_archive("$currdir/$item",$docudom,
11028: $docuname,$depth,$count,
1.1056 raeburn 11029: $hierarchy,$dirorder,$children,
11030: $parent,$titles,$wantform);
1.1055 raeburn 11031: $$depth --;
1.1056 raeburn 11032: pop(@{$hierarchy});
1.1055 raeburn 11033: }
11034: }
11035: }
11036: }
11037: return $result;
11038: }
11039:
11040: sub archive_hierarchy {
11041: my ($depth,$count,$parent,$children) =@_;
11042: if ((ref($parent) eq 'HASH') && (ref($children) eq 'HASH')) {
11043: if (exists($parent->{$depth})) {
11044: $children->{$parent->{$depth}} .= $count.':';
11045: }
11046: }
11047: return;
11048: }
11049:
11050: sub archive_row {
11051: my ($is_dir,$item,$currdir,$depth,$count) = @_;
11052: my ($name) = ($item =~ m{([^/]+)$});
11053: my %choices = &Apache::lonlocal::texthash (
1.1059 raeburn 11054: 'display' => 'Add as file',
1.1055 raeburn 11055: 'dependency' => 'Include as dependency',
11056: 'discard' => 'Discard',
11057: );
11058: if ($is_dir) {
1.1059 raeburn 11059: $choices{'display'} = &mt('Add as folder');
1.1055 raeburn 11060: }
1.1056 raeburn 11061: my $output = &start_data_table_row().'<td align="right">'.$count.'</td>'."\n";
11062: my $offset = 0;
1.1055 raeburn 11063: foreach my $action ('display','dependency','discard') {
1.1056 raeburn 11064: $offset ++;
1.1065 raeburn 11065: if ($action ne 'display') {
11066: $offset ++;
11067: }
1.1055 raeburn 11068: $output .= '<td><span class="LC_nobreak">'.
11069: '<label><input type="radio" name="archive_'.$count.
11070: '" id="archive_'.$action.'_'.$count.'" value="'.$action.'"';
11071: my $text = $choices{$action};
11072: if ($is_dir) {
11073: $output .= ' onclick="javascript:propagateCheck(this.form,'."'$count'".');"';
11074: if ($action eq 'display') {
1.1059 raeburn 11075: $text = &mt('Add as folder');
1.1055 raeburn 11076: }
1.1056 raeburn 11077: } else {
11078: $output .= ' onclick="javascript:dependencyCheck(this.form,'."$count,$offset".');"';
11079:
11080: }
11081: $output .= ' /> '.$choices{$action}.'</label></span>';
11082: if ($action eq 'dependency') {
11083: $output .= '<div id="arc_depon_'.$count.'" style="display:none;">'."\n".
11084: &mt('Used by:').' <select name="archive_dependent_on_'.$count.'" '.
11085: 'onchange="propagateSelect(this.form,'."$count,$offset".')">'."\n".
11086: '<option value=""></option>'."\n".
11087: '</select>'."\n".
11088: '</div>';
1.1059 raeburn 11089: } elsif ($action eq 'display') {
11090: $output .= '<div id="arc_title_'.$count.'" style="display:none;">'."\n".
11091: &mt('Title:').' <input type="text" name="archive_title_'.$count.'" id="archive_title_'.$count.'" />'."\n".
11092: '</div>';
1.1055 raeburn 11093: }
1.1056 raeburn 11094: $output .= '</td>';
1.1055 raeburn 11095: }
11096: $output .= '<td><input type="hidden" name="archive_content_'.$count.'" value="'.
11097: &HTML::Entities::encode("$currdir/$item",'"<>&').'" />'.(' ' x 2);
11098: for (my $i=0; $i<$depth; $i++) {
11099: $output .= ('<img src="/adm/lonIcons/whitespace1.gif" class="LC_docs_spacer" alt="" />' x2)."\n";
11100: }
11101: if ($is_dir) {
11102: $output .= '<img src="/adm/lonIcons/navmap.folder.open.gif" alt="" /> '."\n".
11103: '<input type="hidden" name="archive_directory" value="'.$count.'" />'."\n";
11104: } else {
11105: $output .= '<input type="hidden" name="archive_file" value="'.$count.'" />'."\n";
11106: }
11107: $output .= ' '.$name.'</td>'."\n".
11108: &end_data_table_row();
11109: return $output;
11110: }
11111:
11112: sub archive_options_form {
1.1065 raeburn 11113: my ($form,$display,$count,$hiddenelem) = @_;
11114: my %lt = &Apache::lonlocal::texthash(
11115: perm => 'Permanently remove archive file?',
11116: hows => 'How should each extracted item be incorporated in the course?',
11117: cont => 'Content actions for all',
11118: addf => 'Add as folder/file',
11119: incd => 'Include as dependency for a displayed file',
11120: disc => 'Discard',
11121: no => 'No',
11122: yes => 'Yes',
11123: save => 'Save',
11124: );
11125: my $output = <<"END";
11126: <form name="$form" method="post" action="">
11127: <p><span class="LC_nobreak">$lt{'perm'}
11128: <label>
11129: <input type="radio" name="archivedelete" value="0" checked="checked" />$lt{'no'}
11130: </label>
11131:
11132: <label>
11133: <input type="radio" name="archivedelete" value="1" />$lt{'yes'}</label>
11134: </span>
11135: </p>
11136: <input type="hidden" name="phase" value="decompress_cleanup" />
11137: <br />$lt{'hows'}
11138: <div class="LC_columnSection">
11139: <fieldset>
11140: <legend>$lt{'cont'}</legend>
11141: <input type="button" value="$lt{'addf'}" onclick="javascript:checkAll(document.$form,'display');" />
11142: <input type="button" value="$lt{'incd'}" onclick="javascript:checkAll(document.$form,'dependency');" />
11143: <input type="button" value="$lt{'disc'}" onclick="javascript:checkAll(document.$form,'discard');" />
11144: </fieldset>
11145: </div>
11146: END
11147: return $output.
1.1055 raeburn 11148: &start_data_table()."\n".
1.1065 raeburn 11149: $display."\n".
1.1055 raeburn 11150: &end_data_table()."\n".
11151: '<input type="hidden" name="archive_count" value="'.$count.'" />'.
11152: $hiddenelem.
1.1065 raeburn 11153: '<br /><input type="submit" name="archive_submit" value="'.$lt{'save'}.'" />'.
1.1055 raeburn 11154: '</form>';
11155: }
11156:
11157: sub archive_javascript {
1.1056 raeburn 11158: my ($startcount,$numitems,$titles,$children) = @_;
11159: return unless ((ref($titles) eq 'HASH') && (ref($children) eq 'HASH'));
1.1059 raeburn 11160: my $maintitle = $env{'form.comment'};
1.1055 raeburn 11161: my $scripttag = <<START;
11162: <script type="text/javascript">
11163: // <![CDATA[
11164:
11165: function checkAll(form,prefix) {
11166: var idstr = new RegExp("^archive_"+prefix+"_\\\\d+\$");
11167: for (var i=0; i < form.elements.length; i++) {
11168: var id = form.elements[i].id;
11169: if ((id != '') && (id != undefined)) {
11170: if (idstr.test(id)) {
11171: if (form.elements[i].type == 'radio') {
11172: form.elements[i].checked = true;
1.1056 raeburn 11173: var nostart = i-$startcount;
1.1059 raeburn 11174: var offset = nostart%7;
11175: var count = (nostart-offset)/7;
1.1056 raeburn 11176: dependencyCheck(form,count,offset);
1.1055 raeburn 11177: }
11178: }
11179: }
11180: }
11181: }
11182:
11183: function propagateCheck(form,count) {
11184: if (count > 0) {
1.1059 raeburn 11185: var startelement = $startcount + ((count-1) * 7);
11186: for (var j=1; j<6; j++) {
11187: if ((j != 2) && (j != 4)) {
1.1056 raeburn 11188: var item = startelement + j;
11189: if (form.elements[item].type == 'radio') {
11190: if (form.elements[item].checked) {
11191: containerCheck(form,count,j);
11192: break;
11193: }
1.1055 raeburn 11194: }
11195: }
11196: }
11197: }
11198: }
11199:
11200: numitems = $numitems
1.1056 raeburn 11201: var titles = new Array(numitems);
11202: var parents = new Array(numitems);
1.1055 raeburn 11203: for (var i=0; i<numitems; i++) {
1.1056 raeburn 11204: parents[i] = new Array;
1.1055 raeburn 11205: }
1.1059 raeburn 11206: var maintitle = '$maintitle';
1.1055 raeburn 11207:
11208: START
11209:
1.1056 raeburn 11210: foreach my $container (sort { $a <=> $b } (keys(%{$children}))) {
11211: my @contents = split(/:/,$children->{$container});
1.1055 raeburn 11212: for (my $i=0; $i<@contents; $i ++) {
11213: $scripttag .= 'parents['.$container.']['.$i.'] = '.$contents[$i]."\n";
11214: }
11215: }
11216:
1.1056 raeburn 11217: foreach my $key (sort { $a <=> $b } (keys(%{$titles}))) {
11218: $scripttag .= "titles[$key] = '".$titles->{$key}."';\n";
11219: }
11220:
1.1055 raeburn 11221: $scripttag .= <<END;
11222:
11223: function containerCheck(form,count,offset) {
11224: if (count > 0) {
1.1056 raeburn 11225: dependencyCheck(form,count,offset);
1.1059 raeburn 11226: var item = (offset+$startcount)+7*(count-1);
1.1055 raeburn 11227: form.elements[item].checked = true;
11228: if(Object.prototype.toString.call(parents[count]) === '[object Array]') {
11229: if (parents[count].length > 0) {
11230: for (var j=0; j<parents[count].length; j++) {
1.1056 raeburn 11231: containerCheck(form,parents[count][j],offset);
11232: }
11233: }
11234: }
11235: }
11236: }
11237:
11238: function dependencyCheck(form,count,offset) {
11239: if (count > 0) {
1.1059 raeburn 11240: var chosen = (offset+$startcount)+7*(count-1);
11241: var depitem = $startcount + ((count-1) * 7) + 4;
1.1056 raeburn 11242: var currtype = form.elements[depitem].type;
11243: if (form.elements[chosen].value == 'dependency') {
11244: document.getElementById('arc_depon_'+count).style.display='block';
11245: form.elements[depitem].options.length = 0;
11246: form.elements[depitem].options[0] = new Option('Select','',true,true);
1.1085 raeburn 11247: for (var i=1; i<=numitems; i++) {
11248: if (i == count) {
11249: continue;
11250: }
1.1059 raeburn 11251: var startelement = $startcount + (i-1) * 7;
11252: for (var j=1; j<6; j++) {
11253: if ((j != 2) && (j!= 4)) {
1.1056 raeburn 11254: var item = startelement + j;
11255: if (form.elements[item].type == 'radio') {
11256: if (form.elements[item].checked) {
11257: if (form.elements[item].value == 'display') {
11258: var n = form.elements[depitem].options.length;
11259: form.elements[depitem].options[n] = new Option(titles[i],i,false,false);
11260: }
11261: }
11262: }
11263: }
11264: }
11265: }
11266: } else {
11267: document.getElementById('arc_depon_'+count).style.display='none';
11268: form.elements[depitem].options.length = 0;
11269: form.elements[depitem].options[0] = new Option('Select','',true,true);
11270: }
1.1059 raeburn 11271: titleCheck(form,count,offset);
1.1056 raeburn 11272: }
11273: }
11274:
11275: function propagateSelect(form,count,offset) {
11276: if (count > 0) {
1.1065 raeburn 11277: var item = (1+offset+$startcount)+7*(count-1);
1.1056 raeburn 11278: var picked = form.elements[item].options[form.elements[item].selectedIndex].value;
11279: if (Object.prototype.toString.call(parents[count]) === '[object Array]') {
11280: if (parents[count].length > 0) {
11281: for (var j=0; j<parents[count].length; j++) {
11282: containerSelect(form,parents[count][j],offset,picked);
1.1055 raeburn 11283: }
11284: }
11285: }
11286: }
11287: }
1.1056 raeburn 11288:
11289: function containerSelect(form,count,offset,picked) {
11290: if (count > 0) {
1.1065 raeburn 11291: var item = (offset+$startcount)+7*(count-1);
1.1056 raeburn 11292: if (form.elements[item].type == 'radio') {
11293: if (form.elements[item].value == 'dependency') {
11294: if (form.elements[item+1].type == 'select-one') {
11295: for (var i=0; i<form.elements[item+1].options.length; i++) {
11296: if (form.elements[item+1].options[i].value == picked) {
11297: form.elements[item+1].selectedIndex = i;
11298: break;
11299: }
11300: }
11301: }
11302: if (Object.prototype.toString.call(parents[count]) === '[object Array]') {
11303: if (parents[count].length > 0) {
11304: for (var j=0; j<parents[count].length; j++) {
11305: containerSelect(form,parents[count][j],offset,picked);
11306: }
11307: }
11308: }
11309: }
11310: }
11311: }
11312: }
11313:
1.1059 raeburn 11314: function titleCheck(form,count,offset) {
11315: if (count > 0) {
11316: var chosen = (offset+$startcount)+7*(count-1);
11317: var depitem = $startcount + ((count-1) * 7) + 2;
11318: var currtype = form.elements[depitem].type;
11319: if (form.elements[chosen].value == 'display') {
11320: document.getElementById('arc_title_'+count).style.display='block';
11321: if ((count==1) && ((parents[count].length > 0) || (numitems == 1))) {
11322: document.getElementById('archive_title_'+count).value=maintitle;
11323: }
11324: } else {
11325: document.getElementById('arc_title_'+count).style.display='none';
11326: if (currtype == 'text') {
11327: document.getElementById('archive_title_'+count).value='';
11328: }
11329: }
11330: }
11331: return;
11332: }
11333:
1.1055 raeburn 11334: // ]]>
11335: </script>
11336: END
11337: return $scripttag;
11338: }
11339:
11340: sub process_extracted_files {
1.1067 raeburn 11341: my ($context,$docudom,$docuname,$destination,$dir_root,$hiddenelem) = @_;
1.1055 raeburn 11342: my $numitems = $env{'form.archive_count'};
11343: return unless ($numitems);
11344: my @ids=&Apache::lonnet::current_machine_ids();
11345: my ($prefix,$pathtocheck,$dir,$ishome,$error,$warning,%toplevelitems,%is_dir,
1.1067 raeburn 11346: %folders,%containers,%mapinner,%prompttofetch);
1.1055 raeburn 11347: my $docuhome = &Apache::lonnet::homeserver($docuname,$docudom);
11348: if (grep(/^\Q$docuhome\E$/,@ids)) {
11349: $prefix = &LONCAPA::propath($docudom,$docuname);
11350: $pathtocheck = "$dir_root/$destination";
11351: $dir = $dir_root;
11352: $ishome = 1;
11353: } else {
11354: $prefix = $Apache::lonnet::perlvar{'lonDocRoot'};
11355: $pathtocheck = "$dir_root/$docudom/$docuname/$destination";
11356: $dir = "$dir_root/$docudom/$docuname";
11357: }
11358: my $currdir = "$dir_root/$destination";
11359: (my $docstype,$mapinner{'0'}) = ($destination =~ m{^(docs|supplemental)/(\w+)/});
11360: if ($env{'form.folderpath'}) {
11361: my @items = split('&',$env{'form.folderpath'});
11362: $folders{'0'} = $items[-2];
1.1099 raeburn 11363: if ($env{'form.folderpath'} =~ /\:1$/) {
11364: $containers{'0'}='page';
11365: } else {
11366: $containers{'0'}='sequence';
11367: }
1.1055 raeburn 11368: }
11369: my @archdirs = &get_env_multiple('form.archive_directory');
11370: if ($numitems) {
11371: for (my $i=1; $i<=$numitems; $i++) {
11372: my $path = $env{'form.archive_content_'.$i};
11373: if ($path =~ m{^\Q$pathtocheck\E/([^/]+)$}) {
11374: my $item = $1;
11375: $toplevelitems{$item} = $i;
11376: if (grep(/^\Q$i\E$/,@archdirs)) {
11377: $is_dir{$item} = 1;
11378: }
11379: }
11380: }
11381: }
1.1067 raeburn 11382: my ($output,%children,%parent,%titles,%dirorder,$result);
1.1055 raeburn 11383: if (keys(%toplevelitems) > 0) {
11384: my @contents = sort(keys(%toplevelitems));
1.1056 raeburn 11385: (my $count,undef) = &get_extracted($docudom,$docuname,$currdir,\%is_dir,\%children,
11386: \%parent,\@contents,\%dirorder,\%titles);
1.1055 raeburn 11387: }
1.1066 raeburn 11388: my (%referrer,%orphaned,%todelete,%todeletedir,%newdest,%newseqid);
1.1055 raeburn 11389: if ($numitems) {
11390: for (my $i=1; $i<=$numitems; $i++) {
1.1086 raeburn 11391: next if ($env{'form.archive_'.$i} eq 'dependency');
1.1055 raeburn 11392: my $path = $env{'form.archive_content_'.$i};
11393: if ($path =~ /^\Q$pathtocheck\E/) {
11394: if ($env{'form.archive_'.$i} eq 'discard') {
11395: if ($prefix ne '' && $path ne '') {
11396: if (-e $prefix.$path) {
1.1066 raeburn 11397: if ((@archdirs > 0) &&
11398: (grep(/^\Q$i\E$/,@archdirs))) {
11399: $todeletedir{$prefix.$path} = 1;
11400: } else {
11401: $todelete{$prefix.$path} = 1;
11402: }
1.1055 raeburn 11403: }
11404: }
11405: } elsif ($env{'form.archive_'.$i} eq 'display') {
1.1059 raeburn 11406: my ($docstitle,$title,$url,$outer);
1.1055 raeburn 11407: ($title) = ($path =~ m{/([^/]+)$});
1.1059 raeburn 11408: $docstitle = $env{'form.archive_title_'.$i};
11409: if ($docstitle eq '') {
11410: $docstitle = $title;
11411: }
1.1055 raeburn 11412: $outer = 0;
1.1056 raeburn 11413: if (ref($dirorder{$i}) eq 'ARRAY') {
11414: if (@{$dirorder{$i}} > 0) {
11415: foreach my $item (reverse(@{$dirorder{$i}})) {
1.1055 raeburn 11416: if ($env{'form.archive_'.$item} eq 'display') {
11417: $outer = $item;
11418: last;
11419: }
11420: }
11421: }
11422: }
11423: my ($errtext,$fatal) =
11424: &LONCAPA::map::mapread('/uploaded/'.$docudom.'/'.$docuname.
11425: '/'.$folders{$outer}.'.'.
11426: $containers{$outer});
11427: next if ($fatal);
11428: if ((@archdirs > 0) && (grep(/^\Q$i\E$/,@archdirs))) {
11429: if ($context eq 'coursedocs') {
1.1056 raeburn 11430: $mapinner{$i} = time;
1.1055 raeburn 11431: $folders{$i} = 'default_'.$mapinner{$i};
11432: $containers{$i} = 'sequence';
11433: my $url = '/uploaded/'.$docudom.'/'.$docuname.'/'.
11434: $folders{$i}.'.'.$containers{$i};
11435: my $newidx = &LONCAPA::map::getresidx();
11436: $LONCAPA::map::resources[$newidx]=
1.1059 raeburn 11437: $docstitle.':'.$url.':false:normal:res';
1.1055 raeburn 11438: push(@LONCAPA::map::order,$newidx);
11439: my ($outtext,$errtext) =
11440: &LONCAPA::map::storemap('/uploaded/'.$docudom.'/'.
11441: $docuname.'/'.$folders{$outer}.
1.1087 raeburn 11442: '.'.$containers{$outer},1,1);
1.1056 raeburn 11443: $newseqid{$i} = $newidx;
1.1067 raeburn 11444: unless ($errtext) {
11445: $result .= '<li>'.&mt('Folder: [_1] added to course',$docstitle).'</li>'."\n";
11446: }
1.1055 raeburn 11447: }
11448: } else {
11449: if ($context eq 'coursedocs') {
11450: my $newidx=&LONCAPA::map::getresidx();
11451: my $url = '/uploaded/'.$docudom.'/'.$docuname.'/'.
11452: $docstype.'/'.$mapinner{$outer}.'/'.$newidx.'/'.
11453: $title;
11454: if (!-e "$prefix$dir/$docstype/$mapinner{$outer}") {
11455: mkdir("$prefix$dir/$docstype/$mapinner{$outer}",0755);
11456: }
11457: if (!-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx") {
11458: mkdir("$prefix$dir/$docstype/$mapinner{$outer}/$newidx");
11459: }
11460: if (-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx") {
11461: system("mv $prefix$path $prefix$dir/$docstype/$mapinner{$outer}/$newidx/$title");
1.1056 raeburn 11462: $newdest{$i} = "$prefix$dir/$docstype/$mapinner{$outer}/$newidx";
1.1067 raeburn 11463: unless ($ishome) {
11464: my $fetch = "$newdest{$i}/$title";
11465: $fetch =~ s/^\Q$prefix$dir\E//;
11466: $prompttofetch{$fetch} = 1;
11467: }
1.1055 raeburn 11468: }
11469: $LONCAPA::map::resources[$newidx]=
1.1059 raeburn 11470: $docstitle.':'.$url.':false:normal:res';
1.1055 raeburn 11471: push(@LONCAPA::map::order, $newidx);
11472: my ($outtext,$errtext)=
11473: &LONCAPA::map::storemap('/uploaded/'.$docudom.'/'.
11474: $docuname.'/'.$folders{$outer}.
1.1087 raeburn 11475: '.'.$containers{$outer},1,1);
1.1067 raeburn 11476: unless ($errtext) {
11477: if (-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx/$title") {
11478: $result .= '<li>'.&mt('File: [_1] added to course',$docstitle).'</li>'."\n";
11479: }
11480: }
1.1055 raeburn 11481: }
11482: }
1.1086 raeburn 11483: }
11484: } else {
11485: $warning .= &mt('Item extracted from archive: [_1] has unexpected path.',$path).'<br />';
11486: }
11487: }
11488: for (my $i=1; $i<=$numitems; $i++) {
11489: next unless ($env{'form.archive_'.$i} eq 'dependency');
11490: my $path = $env{'form.archive_content_'.$i};
11491: if ($path =~ /^\Q$pathtocheck\E/) {
11492: my ($title) = ($path =~ m{/([^/]+)$});
11493: $referrer{$i} = $env{'form.archive_dependent_on_'.$i};
11494: if ($env{'form.archive_'.$referrer{$i}} eq 'display') {
11495: if (ref($dirorder{$i}) eq 'ARRAY') {
11496: my ($itemidx,$fullpath,$relpath);
11497: if (ref($dirorder{$referrer{$i}}) eq 'ARRAY') {
11498: my $container = $dirorder{$referrer{$i}}->[-1];
1.1056 raeburn 11499: for (my $j=0; $j<@{$dirorder{$i}}; $j++) {
1.1086 raeburn 11500: if ($dirorder{$i}->[$j] eq $container) {
11501: $itemidx = $j;
1.1056 raeburn 11502: }
11503: }
1.1086 raeburn 11504: }
11505: if ($itemidx eq '') {
11506: $itemidx = 0;
11507: }
11508: if (grep(/^\Q$referrer{$i}\E$/,@archdirs)) {
11509: if ($mapinner{$referrer{$i}}) {
11510: $fullpath = "$prefix$dir/$docstype/$mapinner{$referrer{$i}}";
11511: for (my $j=$itemidx; $j<@{$dirorder{$i}}; $j++) {
11512: if (grep(/^\Q$dirorder{$i}->[$j]\E$/,@archdirs)) {
11513: unless (defined($newseqid{$dirorder{$i}->[$j]})) {
11514: $fullpath .= '/'.$titles{$dirorder{$i}->[$j]};
11515: $relpath .= '/'.$titles{$dirorder{$i}->[$j]};
11516: if (!-e $fullpath) {
11517: mkdir($fullpath,0755);
1.1056 raeburn 11518: }
11519: }
1.1086 raeburn 11520: } else {
11521: last;
1.1056 raeburn 11522: }
1.1086 raeburn 11523: }
11524: }
11525: } elsif ($newdest{$referrer{$i}}) {
11526: $fullpath = $newdest{$referrer{$i}};
11527: for (my $j=$itemidx; $j<@{$dirorder{$i}}; $j++) {
11528: if ($env{'form.archive_'.$dirorder{$i}->[$j]} eq 'discard') {
11529: $orphaned{$i} = $env{'form.archive_'.$dirorder{$i}->[$j]};
11530: last;
11531: } elsif (grep(/^\Q$dirorder{$i}->[$j]\E$/,@archdirs)) {
11532: unless (defined($newseqid{$dirorder{$i}->[$j]})) {
11533: $fullpath .= '/'.$titles{$dirorder{$i}->[$j]};
11534: $relpath .= '/'.$titles{$dirorder{$i}->[$j]};
11535: if (!-e $fullpath) {
11536: mkdir($fullpath,0755);
1.1056 raeburn 11537: }
11538: }
1.1086 raeburn 11539: } else {
11540: last;
1.1056 raeburn 11541: }
1.1055 raeburn 11542: }
11543: }
1.1086 raeburn 11544: if ($fullpath ne '') {
11545: if (-e "$prefix$path") {
11546: system("mv $prefix$path $fullpath/$title");
11547: }
11548: if (-e "$fullpath/$title") {
11549: my $showpath;
11550: if ($relpath ne '') {
11551: $showpath = "$relpath/$title";
11552: } else {
11553: $showpath = "/$title";
11554: }
11555: $result .= '<li>'.&mt('[_1] included as a dependency',$showpath).'</li>'."\n";
11556: }
11557: unless ($ishome) {
11558: my $fetch = "$fullpath/$title";
11559: $fetch =~ s/^\Q$prefix$dir\E//;
11560: $prompttofetch{$fetch} = 1;
11561: }
11562: }
1.1055 raeburn 11563: }
1.1086 raeburn 11564: } elsif ($env{'form.archive_'.$referrer{$i}} eq 'discard') {
11565: $warning .= &mt('[_1] is a dependency of [_2], which was discarded.',
11566: $path,$env{'form.archive_content_'.$referrer{$i}}).'<br />';
1.1055 raeburn 11567: }
11568: } else {
11569: $warning .= &mt('Item extracted from archive: [_1] has unexpected path.',$path).'<br />';
11570: }
11571: }
11572: if (keys(%todelete)) {
11573: foreach my $key (keys(%todelete)) {
11574: unlink($key);
1.1066 raeburn 11575: }
11576: }
11577: if (keys(%todeletedir)) {
11578: foreach my $key (keys(%todeletedir)) {
11579: rmdir($key);
11580: }
11581: }
11582: foreach my $dir (sort(keys(%is_dir))) {
11583: if (($pathtocheck ne '') && ($dir ne '')) {
11584: &cleanup_empty_dirs($prefix."$pathtocheck/$dir");
1.1055 raeburn 11585: }
11586: }
1.1067 raeburn 11587: if ($result ne '') {
11588: $output .= '<ul>'."\n".
11589: $result."\n".
11590: '</ul>';
11591: }
11592: unless ($ishome) {
11593: my $replicationfail;
11594: foreach my $item (keys(%prompttofetch)) {
11595: my $fetchresult= &Apache::lonnet::reply('fetchuserfile:'.$item,$docuhome);
11596: unless ($fetchresult eq 'ok') {
11597: $replicationfail .= '<li>'.$item.'</li>'."\n";
11598: }
11599: }
11600: if ($replicationfail) {
11601: $output .= '<p class="LC_error">'.
11602: &mt('Course home server failed to retrieve:').'<ul>'.
11603: $replicationfail.
11604: '</ul></p>';
11605: }
11606: }
1.1055 raeburn 11607: } else {
11608: $warning = &mt('No items found in archive.');
11609: }
11610: if ($error) {
11611: $output .= '<p class="LC_error">'.&mt('Not extracted.').'<br />'.
11612: $error.'</p>'."\n";
11613: }
11614: if ($warning) {
11615: $output .= '<p class="LC_warning">'.$warning.'</p>'."\n";
11616: }
11617: return $output;
11618: }
11619:
1.1066 raeburn 11620: sub cleanup_empty_dirs {
11621: my ($path) = @_;
11622: if (($path ne '') && (-d $path)) {
11623: if (opendir(my $dirh,$path)) {
11624: my @dircontents = grep(!/^\./,readdir($dirh));
11625: my $numitems = 0;
11626: foreach my $item (@dircontents) {
11627: if (-d "$path/$item") {
11628: &recurse_dirs("$path/$item");
11629: if (-e "$path/$item") {
11630: $numitems ++;
11631: }
11632: } else {
11633: $numitems ++;
11634: }
11635: }
11636: if ($numitems == 0) {
11637: rmdir($path);
11638: }
11639: closedir($dirh);
11640: }
11641: }
11642: return;
11643: }
11644:
1.41 ng 11645: =pod
1.45 matthew 11646:
1.1068 raeburn 11647: =item &get_folder_hierarchy()
11648:
11649: Provides hierarchy of names of folders/sub-folders containing the current
11650: item,
11651:
11652: Inputs: 3
11653: - $navmap - navmaps object
11654:
11655: - $map - url for map (either the trigger itself, or map containing
11656: the resource, which is the trigger).
11657:
11658: - $showitem - 1 => show title for map itself; 0 => do not show.
11659:
11660: Outputs: 1 @pathitems - array of folder/subfolder names.
11661:
11662: =cut
11663:
11664: sub get_folder_hierarchy {
11665: my ($navmap,$map,$showitem) = @_;
11666: my @pathitems;
11667: if (ref($navmap)) {
11668: my $mapres = $navmap->getResourceByUrl($map);
11669: if (ref($mapres)) {
11670: my $pcslist = $mapres->map_hierarchy();
11671: if ($pcslist ne '') {
11672: my @pcs = split(/,/,$pcslist);
11673: foreach my $pc (@pcs) {
11674: if ($pc == 1) {
11675: push(@pathitems,&mt('Main Course Documents'));
11676: } else {
11677: my $res = $navmap->getByMapPc($pc);
11678: if (ref($res)) {
11679: my $title = $res->compTitle();
11680: $title =~ s/\W+/_/g;
11681: if ($title ne '') {
11682: push(@pathitems,$title);
11683: }
11684: }
11685: }
11686: }
11687: }
1.1071 raeburn 11688: if ($showitem) {
11689: if ($mapres->{ID} eq '0.0') {
11690: push(@pathitems,&mt('Main Course Documents'));
11691: } else {
11692: my $maptitle = $mapres->compTitle();
11693: $maptitle =~ s/\W+/_/g;
11694: if ($maptitle ne '') {
11695: push(@pathitems,$maptitle);
11696: }
1.1068 raeburn 11697: }
11698: }
11699: }
11700: }
11701: return @pathitems;
11702: }
11703:
11704: =pod
11705:
1.1015 raeburn 11706: =item * &get_turnedin_filepath()
11707:
11708: Determines path in a user's portfolio file for storage of files uploaded
11709: to a specific essayresponse or dropbox item.
11710:
11711: Inputs: 3 required + 1 optional.
11712: $symb is symb for resource, $uname and $udom are for current user (required).
11713: $caller is optional (can be "submission", if routine is called when storing
11714: an upoaded file when "Submit Answer" button was pressed).
11715:
11716: Returns array containing $path and $multiresp.
11717: $path is path in portfolio. $multiresp is 1 if this resource contains more
11718: than one file upload item. Callers of routine should append partid as a
11719: subdirectory to $path in cases where $multiresp is 1.
11720:
11721: Called by: homework/essayresponse.pm and homework/structuretags.pm
11722:
11723: =cut
11724:
11725: sub get_turnedin_filepath {
11726: my ($symb,$uname,$udom,$caller) = @_;
11727: my ($map,$resid,$resurl)=&Apache::lonnet::decode_symb($symb);
11728: my $turnindir;
11729: my %userhash = &Apache::lonnet::userenvironment($udom,$uname,'turnindir');
11730: $turnindir = $userhash{'turnindir'};
11731: my ($path,$multiresp);
11732: if ($turnindir eq '') {
11733: if ($caller eq 'submission') {
11734: $turnindir = &mt('turned in');
11735: $turnindir =~ s/\W+/_/g;
11736: my %newhash = (
11737: 'turnindir' => $turnindir,
11738: );
11739: &Apache::lonnet::put('environment',\%newhash,$udom,$uname);
11740: }
11741: }
11742: if ($turnindir ne '') {
11743: $path = '/'.$turnindir.'/';
11744: my ($multipart,$turnin,@pathitems);
11745: my $navmap = Apache::lonnavmaps::navmap->new();
11746: if (defined($navmap)) {
11747: my $mapres = $navmap->getResourceByUrl($map);
11748: if (ref($mapres)) {
11749: my $pcslist = $mapres->map_hierarchy();
11750: if ($pcslist ne '') {
11751: foreach my $pc (split(/,/,$pcslist)) {
11752: my $res = $navmap->getByMapPc($pc);
11753: if (ref($res)) {
11754: my $title = $res->compTitle();
11755: $title =~ s/\W+/_/g;
11756: if ($title ne '') {
11757: push(@pathitems,$title);
11758: }
11759: }
11760: }
11761: }
11762: my $maptitle = $mapres->compTitle();
11763: $maptitle =~ s/\W+/_/g;
11764: if ($maptitle ne '') {
11765: push(@pathitems,$maptitle);
11766: }
11767: unless ($env{'request.state'} eq 'construct') {
11768: my $res = $navmap->getBySymb($symb);
11769: if (ref($res)) {
11770: my $partlist = $res->parts();
11771: my $totaluploads = 0;
11772: if (ref($partlist) eq 'ARRAY') {
11773: foreach my $part (@{$partlist}) {
11774: my @types = $res->responseType($part);
11775: my @ids = $res->responseIds($part);
11776: for (my $i=0; $i < scalar(@ids); $i++) {
11777: if ($types[$i] eq 'essay') {
11778: my $partid = $part.'_'.$ids[$i];
11779: if (&Apache::lonnet::EXT("resource.$partid.uploadedfiletypes") ne '') {
11780: $totaluploads ++;
11781: }
11782: }
11783: }
11784: }
11785: if ($totaluploads > 1) {
11786: $multiresp = 1;
11787: }
11788: }
11789: }
11790: }
11791: } else {
11792: return;
11793: }
11794: } else {
11795: return;
11796: }
11797: my $restitle=&Apache::lonnet::gettitle($symb);
11798: $restitle =~ s/\W+/_/g;
11799: if ($restitle eq '') {
11800: $restitle = ($resurl =~ m{/[^/]+$});
11801: if ($restitle eq '') {
11802: $restitle = time;
11803: }
11804: }
11805: push(@pathitems,$restitle);
11806: $path .= join('/',@pathitems);
11807: }
11808: return ($path,$multiresp);
11809: }
11810:
11811: =pod
11812:
1.464 albertel 11813: =back
1.41 ng 11814:
1.112 bowersj2 11815: =head1 CSV Upload/Handling functions
1.38 albertel 11816:
1.41 ng 11817: =over 4
11818:
1.648 raeburn 11819: =item * &upfile_store($r)
1.41 ng 11820:
11821: Store uploaded file, $r should be the HTTP Request object,
1.258 albertel 11822: needs $env{'form.upfile'}
1.41 ng 11823: returns $datatoken to be put into hidden field
11824:
11825: =cut
1.31 albertel 11826:
11827: sub upfile_store {
11828: my $r=shift;
1.258 albertel 11829: $env{'form.upfile'}=~s/\r/\n/gs;
11830: $env{'form.upfile'}=~s/\f/\n/gs;
11831: $env{'form.upfile'}=~s/\n+/\n/gs;
11832: $env{'form.upfile'}=~s/\n+$//gs;
1.31 albertel 11833:
1.258 albertel 11834: my $datatoken=$env{'user.name'}.'_'.$env{'user.domain'}.
11835: '_enroll_'.$env{'request.course.id'}.'_'.time.'_'.$$;
1.31 albertel 11836: {
1.158 raeburn 11837: my $datafile = $r->dir_config('lonDaemons').
11838: '/tmp/'.$datatoken.'.tmp';
11839: if ( open(my $fh,">$datafile") ) {
1.258 albertel 11840: print $fh $env{'form.upfile'};
1.158 raeburn 11841: close($fh);
11842: }
1.31 albertel 11843: }
11844: return $datatoken;
11845: }
11846:
1.56 matthew 11847: =pod
11848:
1.648 raeburn 11849: =item * &load_tmp_file($r)
1.41 ng 11850:
11851: Load uploaded file from tmp, $r should be the HTTP Request object,
1.258 albertel 11852: needs $env{'form.datatoken'},
11853: sets $env{'form.upfile'} to the contents of the file
1.41 ng 11854:
11855: =cut
1.31 albertel 11856:
11857: sub load_tmp_file {
11858: my $r=shift;
11859: my @studentdata=();
11860: {
1.158 raeburn 11861: my $studentfile = $r->dir_config('lonDaemons').
1.258 albertel 11862: '/tmp/'.$env{'form.datatoken'}.'.tmp';
1.158 raeburn 11863: if ( open(my $fh,"<$studentfile") ) {
11864: @studentdata=<$fh>;
11865: close($fh);
11866: }
1.31 albertel 11867: }
1.258 albertel 11868: $env{'form.upfile'}=join('',@studentdata);
1.31 albertel 11869: }
11870:
1.56 matthew 11871: =pod
11872:
1.648 raeburn 11873: =item * &upfile_record_sep()
1.41 ng 11874:
11875: Separate uploaded file into records
11876: returns array of records,
1.258 albertel 11877: needs $env{'form.upfile'} and $env{'form.upfiletype'}
1.41 ng 11878:
11879: =cut
1.31 albertel 11880:
11881: sub upfile_record_sep {
1.258 albertel 11882: if ($env{'form.upfiletype'} eq 'xml') {
1.31 albertel 11883: } else {
1.248 albertel 11884: my @records;
1.258 albertel 11885: foreach my $line (split(/\n/,$env{'form.upfile'})) {
1.248 albertel 11886: if ($line=~/^\s*$/) { next; }
11887: push(@records,$line);
11888: }
11889: return @records;
1.31 albertel 11890: }
11891: }
11892:
1.56 matthew 11893: =pod
11894:
1.648 raeburn 11895: =item * &record_sep($record)
1.41 ng 11896:
1.258 albertel 11897: Separate a record into fields $record should be an item from the upfile_record_sep(), needs $env{'form.upfiletype'}
1.41 ng 11898:
11899: =cut
11900:
1.263 www 11901: sub takeleft {
11902: my $index=shift;
11903: return substr('0000'.$index,-4,4);
11904: }
11905:
1.31 albertel 11906: sub record_sep {
11907: my $record=shift;
11908: my %components=();
1.258 albertel 11909: if ($env{'form.upfiletype'} eq 'xml') {
11910: } elsif ($env{'form.upfiletype'} eq 'space') {
1.31 albertel 11911: my $i=0;
1.356 albertel 11912: foreach my $field (split(/\s+/,$record)) {
1.31 albertel 11913: $field=~s/^(\"|\')//;
11914: $field=~s/(\"|\')$//;
1.263 www 11915: $components{&takeleft($i)}=$field;
1.31 albertel 11916: $i++;
11917: }
1.258 albertel 11918: } elsif ($env{'form.upfiletype'} eq 'tab') {
1.31 albertel 11919: my $i=0;
1.356 albertel 11920: foreach my $field (split(/\t/,$record)) {
1.31 albertel 11921: $field=~s/^(\"|\')//;
11922: $field=~s/(\"|\')$//;
1.263 www 11923: $components{&takeleft($i)}=$field;
1.31 albertel 11924: $i++;
11925: }
11926: } else {
1.561 www 11927: my $separator=',';
1.480 banghart 11928: if ($env{'form.upfiletype'} eq 'semisv') {
1.561 www 11929: $separator=';';
1.480 banghart 11930: }
1.31 albertel 11931: my $i=0;
1.561 www 11932: # the character we are looking for to indicate the end of a quote or a record
11933: my $looking_for=$separator;
11934: # do not add the characters to the fields
11935: my $ignore=0;
11936: # we just encountered a separator (or the beginning of the record)
11937: my $just_found_separator=1;
11938: # store the field we are working on here
11939: my $field='';
11940: # work our way through all characters in record
11941: foreach my $character ($record=~/(.)/g) {
11942: if ($character eq $looking_for) {
11943: if ($character ne $separator) {
11944: # Found the end of a quote, again looking for separator
11945: $looking_for=$separator;
11946: $ignore=1;
11947: } else {
11948: # Found a separator, store away what we got
11949: $components{&takeleft($i)}=$field;
11950: $i++;
11951: $just_found_separator=1;
11952: $ignore=0;
11953: $field='';
11954: }
11955: next;
11956: }
11957: # single or double quotation marks after a separator indicate beginning of a quote
11958: # we are now looking for the end of the quote and need to ignore separators
11959: if ((($character eq '"') || ($character eq "'")) && ($just_found_separator)) {
11960: $looking_for=$character;
11961: next;
11962: }
11963: # ignore would be true after we reached the end of a quote
11964: if ($ignore) { next; }
11965: if (($just_found_separator) && ($character=~/\s/)) { next; }
11966: $field.=$character;
11967: $just_found_separator=0;
1.31 albertel 11968: }
1.561 www 11969: # catch the very last entry, since we never encountered the separator
11970: $components{&takeleft($i)}=$field;
1.31 albertel 11971: }
11972: return %components;
11973: }
11974:
1.144 matthew 11975: ######################################################
11976: ######################################################
11977:
1.56 matthew 11978: =pod
11979:
1.648 raeburn 11980: =item * &upfile_select_html()
1.41 ng 11981:
1.144 matthew 11982: Return HTML code to select a file from the users machine and specify
11983: the file type.
1.41 ng 11984:
11985: =cut
11986:
1.144 matthew 11987: ######################################################
11988: ######################################################
1.31 albertel 11989: sub upfile_select_html {
1.144 matthew 11990: my %Types = (
11991: csv => &mt('CSV (comma separated values, spreadsheet)'),
1.480 banghart 11992: semisv => &mt('Semicolon separated values'),
1.144 matthew 11993: space => &mt('Space separated'),
11994: tab => &mt('Tabulator separated'),
11995: # xml => &mt('HTML/XML'),
11996: );
11997: my $Str = '<input type="file" name="upfile" size="50" />'.
1.727 riegler 11998: '<br />'.&mt('Type').': <select name="upfiletype">';
1.144 matthew 11999: foreach my $type (sort(keys(%Types))) {
12000: $Str .= '<option value="'.$type.'" >'.$Types{$type}."</option>\n";
12001: }
12002: $Str .= "</select>\n";
12003: return $Str;
1.31 albertel 12004: }
12005:
1.301 albertel 12006: sub get_samples {
12007: my ($records,$toget) = @_;
12008: my @samples=({});
12009: my $got=0;
12010: foreach my $rec (@$records) {
12011: my %temp = &record_sep($rec);
12012: if (! grep(/\S/, values(%temp))) { next; }
12013: if (%temp) {
12014: $samples[$got]=\%temp;
12015: $got++;
12016: if ($got == $toget) { last; }
12017: }
12018: }
12019: return \@samples;
12020: }
12021:
1.144 matthew 12022: ######################################################
12023: ######################################################
12024:
1.56 matthew 12025: =pod
12026:
1.648 raeburn 12027: =item * &csv_print_samples($r,$records)
1.41 ng 12028:
12029: Prints a table of sample values from each column uploaded $r is an
12030: Apache Request ref, $records is an arrayref from
12031: &Apache::loncommon::upfile_record_sep
12032:
12033: =cut
12034:
1.144 matthew 12035: ######################################################
12036: ######################################################
1.31 albertel 12037: sub csv_print_samples {
12038: my ($r,$records) = @_;
1.662 bisitz 12039: my $samples = &get_samples($records,5);
1.301 albertel 12040:
1.594 raeburn 12041: $r->print(&mt('Samples').'<br />'.&start_data_table().
12042: &start_data_table_header_row());
1.356 albertel 12043: foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
1.845 bisitz 12044: $r->print('<th>'.&mt('Column [_1]',($sample+1)).'</th>'); }
1.594 raeburn 12045: $r->print(&end_data_table_header_row());
1.301 albertel 12046: foreach my $hash (@$samples) {
1.594 raeburn 12047: $r->print(&start_data_table_row());
1.356 albertel 12048: foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
1.31 albertel 12049: $r->print('<td>');
1.356 albertel 12050: if (defined($$hash{$sample})) { $r->print($$hash{$sample}); }
1.31 albertel 12051: $r->print('</td>');
12052: }
1.594 raeburn 12053: $r->print(&end_data_table_row());
1.31 albertel 12054: }
1.594 raeburn 12055: $r->print(&end_data_table().'<br />'."\n");
1.31 albertel 12056: }
12057:
1.144 matthew 12058: ######################################################
12059: ######################################################
12060:
1.56 matthew 12061: =pod
12062:
1.648 raeburn 12063: =item * &csv_print_select_table($r,$records,$d)
1.41 ng 12064:
12065: Prints a table to create associations between values and table columns.
1.144 matthew 12066:
1.41 ng 12067: $r is an Apache Request ref,
12068: $records is an arrayref from &Apache::loncommon::upfile_record_sep,
1.174 matthew 12069: $d is an array of 2 element arrays (internal name, displayed name,defaultcol)
1.41 ng 12070:
12071: =cut
12072:
1.144 matthew 12073: ######################################################
12074: ######################################################
1.31 albertel 12075: sub csv_print_select_table {
12076: my ($r,$records,$d) = @_;
1.301 albertel 12077: my $i=0;
12078: my $samples = &get_samples($records,1);
1.144 matthew 12079: $r->print(&mt('Associate columns with student attributes.')."\n".
1.594 raeburn 12080: &start_data_table().&start_data_table_header_row().
1.144 matthew 12081: '<th>'.&mt('Attribute').'</th>'.
1.594 raeburn 12082: '<th>'.&mt('Column').'</th>'.
12083: &end_data_table_header_row()."\n");
1.356 albertel 12084: foreach my $array_ref (@$d) {
12085: my ($value,$display,$defaultcol)=@{ $array_ref };
1.729 raeburn 12086: $r->print(&start_data_table_row().'<td>'.$display.'</td>');
1.31 albertel 12087:
1.875 bisitz 12088: $r->print('<td><select name="f'.$i.'"'.
1.32 matthew 12089: ' onchange="javascript:flip(this.form,'.$i.');">');
1.31 albertel 12090: $r->print('<option value="none"></option>');
1.356 albertel 12091: foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
12092: $r->print('<option value="'.$sample.'"'.
12093: ($sample eq $defaultcol ? ' selected="selected" ' : '').
1.662 bisitz 12094: '>'.&mt('Column [_1]',($sample+1)).'</option>');
1.31 albertel 12095: }
1.594 raeburn 12096: $r->print('</select></td>'.&end_data_table_row()."\n");
1.31 albertel 12097: $i++;
12098: }
1.594 raeburn 12099: $r->print(&end_data_table());
1.31 albertel 12100: $i--;
12101: return $i;
12102: }
1.56 matthew 12103:
1.144 matthew 12104: ######################################################
12105: ######################################################
12106:
1.56 matthew 12107: =pod
1.31 albertel 12108:
1.648 raeburn 12109: =item * &csv_samples_select_table($r,$records,$d)
1.41 ng 12110:
12111: Prints a table of sample values from the upload and can make associate samples to internal names.
12112:
12113: $r is an Apache Request ref,
12114: $records is an arrayref from &Apache::loncommon::upfile_record_sep,
12115: $d is an array of 2 element arrays (internal name, displayed name)
12116:
12117: =cut
12118:
1.144 matthew 12119: ######################################################
12120: ######################################################
1.31 albertel 12121: sub csv_samples_select_table {
12122: my ($r,$records,$d) = @_;
12123: my $i=0;
1.144 matthew 12124: #
1.662 bisitz 12125: my $max_samples = 5;
12126: my $samples = &get_samples($records,$max_samples);
1.594 raeburn 12127: $r->print(&start_data_table().
12128: &start_data_table_header_row().'<th>'.
12129: &mt('Field').'</th><th>'.&mt('Samples').'</th>'.
12130: &end_data_table_header_row());
1.301 albertel 12131:
12132: foreach my $key (sort(keys(%{ $samples->[0] }))) {
1.594 raeburn 12133: $r->print(&start_data_table_row().'<td><select name="f'.$i.'"'.
1.32 matthew 12134: ' onchange="javascript:flip(this.form,'.$i.');">');
1.301 albertel 12135: foreach my $option (@$d) {
12136: my ($value,$display,$defaultcol)=@{ $option };
1.174 matthew 12137: $r->print('<option value="'.$value.'"'.
1.253 albertel 12138: ($i eq $defaultcol ? ' selected="selected" ':'').'>'.
1.174 matthew 12139: $display.'</option>');
1.31 albertel 12140: }
12141: $r->print('</select></td><td>');
1.662 bisitz 12142: foreach my $line (0..($max_samples-1)) {
1.301 albertel 12143: if (defined($samples->[$line]{$key})) {
12144: $r->print($samples->[$line]{$key}."<br />\n");
12145: }
12146: }
1.594 raeburn 12147: $r->print('</td>'.&end_data_table_row());
1.31 albertel 12148: $i++;
12149: }
1.594 raeburn 12150: $r->print(&end_data_table());
1.31 albertel 12151: $i--;
12152: return($i);
1.115 matthew 12153: }
12154:
1.144 matthew 12155: ######################################################
12156: ######################################################
12157:
1.115 matthew 12158: =pod
12159:
1.648 raeburn 12160: =item * &clean_excel_name($name)
1.115 matthew 12161:
12162: Returns a replacement for $name which does not contain any illegal characters.
12163:
12164: =cut
12165:
1.144 matthew 12166: ######################################################
12167: ######################################################
1.115 matthew 12168: sub clean_excel_name {
12169: my ($name) = @_;
12170: $name =~ s/[:\*\?\/\\]//g;
12171: if (length($name) > 31) {
12172: $name = substr($name,0,31);
12173: }
12174: return $name;
1.25 albertel 12175: }
1.84 albertel 12176:
1.85 albertel 12177: =pod
12178:
1.648 raeburn 12179: =item * &check_if_partid_hidden($id,$symb,$udom,$uname)
1.85 albertel 12180:
12181: Returns either 1 or undef
12182:
12183: 1 if the part is to be hidden, undef if it is to be shown
12184:
12185: Arguments are:
12186:
12187: $id the id of the part to be checked
12188: $symb, optional the symb of the resource to check
12189: $udom, optional the domain of the user to check for
12190: $uname, optional the username of the user to check for
12191:
12192: =cut
1.84 albertel 12193:
12194: sub check_if_partid_hidden {
12195: my ($id,$symb,$udom,$uname) = @_;
1.133 albertel 12196: my $hiddenparts=&Apache::lonnet::EXT('resource.0.hiddenparts',
1.84 albertel 12197: $symb,$udom,$uname);
1.141 albertel 12198: my $truth=1;
12199: #if the string starts with !, then the list is the list to show not hide
12200: if ($hiddenparts=~s/^\s*!//) { $truth=undef; }
1.84 albertel 12201: my @hiddenlist=split(/,/,$hiddenparts);
12202: foreach my $checkid (@hiddenlist) {
1.141 albertel 12203: if ($checkid =~ /^\s*\Q$id\E\s*$/) { return $truth; }
1.84 albertel 12204: }
1.141 albertel 12205: return !$truth;
1.84 albertel 12206: }
1.127 matthew 12207:
1.138 matthew 12208:
12209: ############################################################
12210: ############################################################
12211:
12212: =pod
12213:
1.157 matthew 12214: =back
12215:
1.138 matthew 12216: =head1 cgi-bin script and graphing routines
12217:
1.157 matthew 12218: =over 4
12219:
1.648 raeburn 12220: =item * &get_cgi_id()
1.138 matthew 12221:
12222: Inputs: none
12223:
12224: Returns an id which can be used to pass environment variables
12225: to various cgi-bin scripts. These environment variables will
12226: be removed from the users environment after a given time by
12227: the routine &Apache::lonnet::transfer_profile_to_env.
12228:
12229: =cut
12230:
12231: ############################################################
12232: ############################################################
1.152 albertel 12233: my $uniq=0;
1.136 matthew 12234: sub get_cgi_id {
1.154 albertel 12235: $uniq=($uniq+1)%100000;
1.280 albertel 12236: return (time.'_'.$$.'_'.$uniq);
1.136 matthew 12237: }
12238:
1.127 matthew 12239: ############################################################
12240: ############################################################
12241:
12242: =pod
12243:
1.648 raeburn 12244: =item * &DrawBarGraph()
1.127 matthew 12245:
1.138 matthew 12246: Facilitates the plotting of data in a (stacked) bar graph.
12247: Puts plot definition data into the users environment in order for
12248: graph.png to plot it. Returns an <img> tag for the plot.
12249: The bars on the plot are labeled '1','2',...,'n'.
12250:
12251: Inputs:
12252:
12253: =over 4
12254:
12255: =item $Title: string, the title of the plot
12256:
12257: =item $xlabel: string, text describing the X-axis of the plot
12258:
12259: =item $ylabel: string, text describing the Y-axis of the plot
12260:
12261: =item $Max: scalar, the maximum Y value to use in the plot
12262: If $Max is < any data point, the graph will not be rendered.
12263:
1.140 matthew 12264: =item $colors: array ref holding the colors to be used for the data sets when
1.138 matthew 12265: they are plotted. If undefined, default values will be used.
12266:
1.178 matthew 12267: =item $labels: array ref holding the labels to use on the x-axis for the bars.
12268:
1.138 matthew 12269: =item @Values: An array of array references. Each array reference holds data
12270: to be plotted in a stacked bar chart.
12271:
1.239 matthew 12272: =item If the final element of @Values is a hash reference the key/value
12273: pairs will be added to the graph definition.
12274:
1.138 matthew 12275: =back
12276:
12277: Returns:
12278:
12279: An <img> tag which references graph.png and the appropriate identifying
12280: information for the plot.
12281:
1.127 matthew 12282: =cut
12283:
12284: ############################################################
12285: ############################################################
1.134 matthew 12286: sub DrawBarGraph {
1.178 matthew 12287: my ($Title,$xlabel,$ylabel,$Max,$colors,$labels,@Values)=@_;
1.134 matthew 12288: #
12289: if (! defined($colors)) {
12290: $colors = ['#33ff00',
12291: '#0033cc', '#990000', '#aaaa66', '#663399', '#ff9933',
12292: '#66ccff', '#ff9999', '#cccc33', '#660000', '#33cc66',
12293: ];
12294: }
1.228 matthew 12295: my $extra_settings = {};
12296: if (ref($Values[-1]) eq 'HASH') {
12297: $extra_settings = pop(@Values);
12298: }
1.127 matthew 12299: #
1.136 matthew 12300: my $identifier = &get_cgi_id();
12301: my $id = 'cgi.'.$identifier;
1.129 matthew 12302: if (! @Values || ref($Values[0]) ne 'ARRAY') {
1.127 matthew 12303: return '';
12304: }
1.225 matthew 12305: #
12306: my @Labels;
12307: if (defined($labels)) {
12308: @Labels = @$labels;
12309: } else {
12310: for (my $i=0;$i<@{$Values[0]};$i++) {
12311: push (@Labels,$i+1);
12312: }
12313: }
12314: #
1.129 matthew 12315: my $NumBars = scalar(@{$Values[0]});
1.225 matthew 12316: if ($NumBars < scalar(@Labels)) { $NumBars = scalar(@Labels); }
1.129 matthew 12317: my %ValuesHash;
12318: my $NumSets=1;
12319: foreach my $array (@Values) {
12320: next if (! ref($array));
1.136 matthew 12321: $ValuesHash{$id.'.data.'.$NumSets++} =
1.132 matthew 12322: join(',',@$array);
1.129 matthew 12323: }
1.127 matthew 12324: #
1.136 matthew 12325: my ($height,$width,$xskip,$bar_width) = (200,120,1,15);
1.225 matthew 12326: if ($NumBars < 3) {
12327: $width = 120+$NumBars*32;
1.220 matthew 12328: $xskip = 1;
1.225 matthew 12329: $bar_width = 30;
12330: } elsif ($NumBars < 5) {
12331: $width = 120+$NumBars*20;
12332: $xskip = 1;
12333: $bar_width = 20;
1.220 matthew 12334: } elsif ($NumBars < 10) {
1.136 matthew 12335: $width = 120+$NumBars*15;
12336: $xskip = 1;
12337: $bar_width = 15;
12338: } elsif ($NumBars <= 25) {
12339: $width = 120+$NumBars*11;
12340: $xskip = 5;
12341: $bar_width = 8;
12342: } elsif ($NumBars <= 50) {
12343: $width = 120+$NumBars*8;
12344: $xskip = 5;
12345: $bar_width = 4;
12346: } else {
12347: $width = 120+$NumBars*8;
12348: $xskip = 5;
12349: $bar_width = 4;
12350: }
12351: #
1.137 matthew 12352: $Max = 1 if ($Max < 1);
12353: if ( int($Max) < $Max ) {
12354: $Max++;
12355: $Max = int($Max);
12356: }
1.127 matthew 12357: $Title = '' if (! defined($Title));
12358: $xlabel = '' if (! defined($xlabel));
12359: $ylabel = '' if (! defined($ylabel));
1.369 www 12360: $ValuesHash{$id.'.title'} = &escape($Title);
12361: $ValuesHash{$id.'.xlabel'} = &escape($xlabel);
12362: $ValuesHash{$id.'.ylabel'} = &escape($ylabel);
1.137 matthew 12363: $ValuesHash{$id.'.y_max_value'} = $Max;
1.136 matthew 12364: $ValuesHash{$id.'.NumBars'} = $NumBars;
12365: $ValuesHash{$id.'.NumSets'} = $NumSets;
12366: $ValuesHash{$id.'.PlotType'} = 'bar';
12367: $ValuesHash{$id.'.Colors'} = join(',',@{$colors});
12368: $ValuesHash{$id.'.height'} = $height;
12369: $ValuesHash{$id.'.width'} = $width;
12370: $ValuesHash{$id.'.xskip'} = $xskip;
12371: $ValuesHash{$id.'.bar_width'} = $bar_width;
12372: $ValuesHash{$id.'.labels'} = join(',',@Labels);
1.127 matthew 12373: #
1.228 matthew 12374: # Deal with other parameters
12375: while (my ($key,$value) = each(%$extra_settings)) {
12376: $ValuesHash{$id.'.'.$key} = $value;
12377: }
12378: #
1.646 raeburn 12379: &Apache::lonnet::appenv(\%ValuesHash);
1.137 matthew 12380: return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
12381: }
12382:
12383: ############################################################
12384: ############################################################
12385:
12386: =pod
12387:
1.648 raeburn 12388: =item * &DrawXYGraph()
1.137 matthew 12389:
1.138 matthew 12390: Facilitates the plotting of data in an XY graph.
12391: Puts plot definition data into the users environment in order for
12392: graph.png to plot it. Returns an <img> tag for the plot.
12393:
12394: Inputs:
12395:
12396: =over 4
12397:
12398: =item $Title: string, the title of the plot
12399:
12400: =item $xlabel: string, text describing the X-axis of the plot
12401:
12402: =item $ylabel: string, text describing the Y-axis of the plot
12403:
12404: =item $Max: scalar, the maximum Y value to use in the plot
12405: If $Max is < any data point, the graph will not be rendered.
12406:
12407: =item $colors: Array ref containing the hex color codes for the data to be
12408: plotted in. If undefined, default values will be used.
12409:
12410: =item $Xlabels: Array ref containing the labels to be used for the X-axis.
12411:
12412: =item $Ydata: Array ref containing Array refs.
1.185 www 12413: Each of the contained arrays will be plotted as a separate curve.
1.138 matthew 12414:
12415: =item %Values: hash indicating or overriding any default values which are
12416: passed to graph.png.
12417: Possible values are: width, xskip, x_ticks, x_tick_offset, among others.
12418:
12419: =back
12420:
12421: Returns:
12422:
12423: An <img> tag which references graph.png and the appropriate identifying
12424: information for the plot.
12425:
1.137 matthew 12426: =cut
12427:
12428: ############################################################
12429: ############################################################
12430: sub DrawXYGraph {
12431: my ($Title,$xlabel,$ylabel,$Max,$colors,$Xlabels,$Ydata,%Values)=@_;
12432: #
12433: # Create the identifier for the graph
12434: my $identifier = &get_cgi_id();
12435: my $id = 'cgi.'.$identifier;
12436: #
12437: $Title = '' if (! defined($Title));
12438: $xlabel = '' if (! defined($xlabel));
12439: $ylabel = '' if (! defined($ylabel));
12440: my %ValuesHash =
12441: (
1.369 www 12442: $id.'.title' => &escape($Title),
12443: $id.'.xlabel' => &escape($xlabel),
12444: $id.'.ylabel' => &escape($ylabel),
1.137 matthew 12445: $id.'.y_max_value'=> $Max,
12446: $id.'.labels' => join(',',@$Xlabels),
12447: $id.'.PlotType' => 'XY',
12448: );
12449: #
12450: if (defined($colors) && ref($colors) eq 'ARRAY') {
12451: $ValuesHash{$id.'.Colors'} = join(',',@{$colors});
12452: }
12453: #
12454: if (! ref($Ydata) || ref($Ydata) ne 'ARRAY') {
12455: return '';
12456: }
12457: my $NumSets=1;
1.138 matthew 12458: foreach my $array (@{$Ydata}){
1.137 matthew 12459: next if (! ref($array));
12460: $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array);
12461: }
1.138 matthew 12462: $ValuesHash{$id.'.NumSets'} = $NumSets-1;
1.137 matthew 12463: #
12464: # Deal with other parameters
12465: while (my ($key,$value) = each(%Values)) {
12466: $ValuesHash{$id.'.'.$key} = $value;
1.127 matthew 12467: }
12468: #
1.646 raeburn 12469: &Apache::lonnet::appenv(\%ValuesHash);
1.136 matthew 12470: return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
12471: }
12472:
12473: ############################################################
12474: ############################################################
12475:
12476: =pod
12477:
1.648 raeburn 12478: =item * &DrawXYYGraph()
1.138 matthew 12479:
12480: Facilitates the plotting of data in an XY graph with two Y axes.
12481: Puts plot definition data into the users environment in order for
12482: graph.png to plot it. Returns an <img> tag for the plot.
12483:
12484: Inputs:
12485:
12486: =over 4
12487:
12488: =item $Title: string, the title of the plot
12489:
12490: =item $xlabel: string, text describing the X-axis of the plot
12491:
12492: =item $ylabel: string, text describing the Y-axis of the plot
12493:
12494: =item $colors: Array ref containing the hex color codes for the data to be
12495: plotted in. If undefined, default values will be used.
12496:
12497: =item $Xlabels: Array ref containing the labels to be used for the X-axis.
12498:
12499: =item $Ydata1: The first data set
12500:
12501: =item $Min1: The minimum value of the left Y-axis
12502:
12503: =item $Max1: The maximum value of the left Y-axis
12504:
12505: =item $Ydata2: The second data set
12506:
12507: =item $Min2: The minimum value of the right Y-axis
12508:
12509: =item $Max2: The maximum value of the left Y-axis
12510:
12511: =item %Values: hash indicating or overriding any default values which are
12512: passed to graph.png.
12513: Possible values are: width, xskip, x_ticks, x_tick_offset, among others.
12514:
12515: =back
12516:
12517: Returns:
12518:
12519: An <img> tag which references graph.png and the appropriate identifying
12520: information for the plot.
1.136 matthew 12521:
12522: =cut
12523:
12524: ############################################################
12525: ############################################################
1.137 matthew 12526: sub DrawXYYGraph {
12527: my ($Title,$xlabel,$ylabel,$colors,$Xlabels,$Ydata1,$Min1,$Max1,
12528: $Ydata2,$Min2,$Max2,%Values)=@_;
1.136 matthew 12529: #
12530: # Create the identifier for the graph
12531: my $identifier = &get_cgi_id();
12532: my $id = 'cgi.'.$identifier;
12533: #
12534: $Title = '' if (! defined($Title));
12535: $xlabel = '' if (! defined($xlabel));
12536: $ylabel = '' if (! defined($ylabel));
12537: my %ValuesHash =
12538: (
1.369 www 12539: $id.'.title' => &escape($Title),
12540: $id.'.xlabel' => &escape($xlabel),
12541: $id.'.ylabel' => &escape($ylabel),
1.136 matthew 12542: $id.'.labels' => join(',',@$Xlabels),
12543: $id.'.PlotType' => 'XY',
12544: $id.'.NumSets' => 2,
1.137 matthew 12545: $id.'.two_axes' => 1,
12546: $id.'.y1_max_value' => $Max1,
12547: $id.'.y1_min_value' => $Min1,
12548: $id.'.y2_max_value' => $Max2,
12549: $id.'.y2_min_value' => $Min2,
1.136 matthew 12550: );
12551: #
1.137 matthew 12552: if (defined($colors) && ref($colors) eq 'ARRAY') {
12553: $ValuesHash{$id.'.Colors'} = join(',',@{$colors});
12554: }
12555: #
12556: if (! ref($Ydata1) || ref($Ydata1) ne 'ARRAY' ||
12557: ! ref($Ydata2) || ref($Ydata2) ne 'ARRAY'){
1.136 matthew 12558: return '';
12559: }
12560: my $NumSets=1;
1.137 matthew 12561: foreach my $array ($Ydata1,$Ydata2){
1.136 matthew 12562: next if (! ref($array));
12563: $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array);
1.137 matthew 12564: }
12565: #
12566: # Deal with other parameters
12567: while (my ($key,$value) = each(%Values)) {
12568: $ValuesHash{$id.'.'.$key} = $value;
1.136 matthew 12569: }
12570: #
1.646 raeburn 12571: &Apache::lonnet::appenv(\%ValuesHash);
1.130 albertel 12572: return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
1.139 matthew 12573: }
12574:
12575: ############################################################
12576: ############################################################
12577:
12578: =pod
12579:
1.157 matthew 12580: =back
12581:
1.139 matthew 12582: =head1 Statistics helper routines?
12583:
12584: Bad place for them but what the hell.
12585:
1.157 matthew 12586: =over 4
12587:
1.648 raeburn 12588: =item * &chartlink()
1.139 matthew 12589:
12590: Returns a link to the chart for a specific student.
12591:
12592: Inputs:
12593:
12594: =over 4
12595:
12596: =item $linktext: The text of the link
12597:
12598: =item $sname: The students username
12599:
12600: =item $sdomain: The students domain
12601:
12602: =back
12603:
1.157 matthew 12604: =back
12605:
1.139 matthew 12606: =cut
12607:
12608: ############################################################
12609: ############################################################
12610: sub chartlink {
12611: my ($linktext, $sname, $sdomain) = @_;
12612: my $link = '<a href="/adm/statistics?reportSelected=student_assessment'.
1.369 www 12613: '&SelectedStudent='.&escape($sname.':'.$sdomain).
1.219 albertel 12614: '&chartoutputmode='.HTML::Entities::encode('html, with all links').
1.139 matthew 12615: '">'.$linktext.'</a>';
1.153 matthew 12616: }
12617:
12618: #######################################################
12619: #######################################################
12620:
12621: =pod
12622:
12623: =head1 Course Environment Routines
1.157 matthew 12624:
12625: =over 4
1.153 matthew 12626:
1.648 raeburn 12627: =item * &restore_course_settings()
1.153 matthew 12628:
1.648 raeburn 12629: =item * &store_course_settings()
1.153 matthew 12630:
12631: Restores/Store indicated form parameters from the course environment.
12632: Will not overwrite existing values of the form parameters.
12633:
12634: Inputs:
12635: a scalar describing the data (e.g. 'chart', 'problem_analysis')
12636:
12637: a hash ref describing the data to be stored. For example:
12638:
12639: %Save_Parameters = ('Status' => 'scalar',
12640: 'chartoutputmode' => 'scalar',
12641: 'chartoutputdata' => 'scalar',
12642: 'Section' => 'array',
1.373 raeburn 12643: 'Group' => 'array',
1.153 matthew 12644: 'StudentData' => 'array',
12645: 'Maps' => 'array');
12646:
12647: Returns: both routines return nothing
12648:
1.631 raeburn 12649: =back
12650:
1.153 matthew 12651: =cut
12652:
12653: #######################################################
12654: #######################################################
12655: sub store_course_settings {
1.496 albertel 12656: return &store_settings($env{'request.course.id'},@_);
12657: }
12658:
12659: sub store_settings {
1.153 matthew 12660: # save to the environment
12661: # appenv the same items, just to be safe
1.300 albertel 12662: my $udom = $env{'user.domain'};
12663: my $uname = $env{'user.name'};
1.496 albertel 12664: my ($context,$prefix,$Settings) = @_;
1.153 matthew 12665: my %SaveHash;
12666: my %AppHash;
12667: while (my ($setting,$type) = each(%$Settings)) {
1.496 albertel 12668: my $basename = join('.','internal',$context,$prefix,$setting);
1.300 albertel 12669: my $envname = 'environment.'.$basename;
1.258 albertel 12670: if (exists($env{'form.'.$setting})) {
1.153 matthew 12671: # Save this value away
12672: if ($type eq 'scalar' &&
1.258 albertel 12673: (! exists($env{$envname}) ||
12674: $env{$envname} ne $env{'form.'.$setting})) {
12675: $SaveHash{$basename} = $env{'form.'.$setting};
12676: $AppHash{$envname} = $env{'form.'.$setting};
1.153 matthew 12677: } elsif ($type eq 'array') {
12678: my $stored_form;
1.258 albertel 12679: if (ref($env{'form.'.$setting})) {
1.153 matthew 12680: $stored_form = join(',',
12681: map {
1.369 www 12682: &escape($_);
1.258 albertel 12683: } sort(@{$env{'form.'.$setting}}));
1.153 matthew 12684: } else {
12685: $stored_form =
1.369 www 12686: &escape($env{'form.'.$setting});
1.153 matthew 12687: }
12688: # Determine if the array contents are the same.
1.258 albertel 12689: if ($stored_form ne $env{$envname}) {
1.153 matthew 12690: $SaveHash{$basename} = $stored_form;
12691: $AppHash{$envname} = $stored_form;
12692: }
12693: }
12694: }
12695: }
12696: my $put_result = &Apache::lonnet::put('environment',\%SaveHash,
1.300 albertel 12697: $udom,$uname);
1.153 matthew 12698: if ($put_result !~ /^(ok|delayed)/) {
12699: &Apache::lonnet::logthis('unable to save form parameters, '.
12700: 'got error:'.$put_result);
12701: }
12702: # Make sure these settings stick around in this session, too
1.646 raeburn 12703: &Apache::lonnet::appenv(\%AppHash);
1.153 matthew 12704: return;
12705: }
12706:
12707: sub restore_course_settings {
1.499 albertel 12708: return &restore_settings($env{'request.course.id'},@_);
1.496 albertel 12709: }
12710:
12711: sub restore_settings {
12712: my ($context,$prefix,$Settings) = @_;
1.153 matthew 12713: while (my ($setting,$type) = each(%$Settings)) {
1.258 albertel 12714: next if (exists($env{'form.'.$setting}));
1.496 albertel 12715: my $envname = 'environment.internal.'.$context.'.'.$prefix.
1.153 matthew 12716: '.'.$setting;
1.258 albertel 12717: if (exists($env{$envname})) {
1.153 matthew 12718: if ($type eq 'scalar') {
1.258 albertel 12719: $env{'form.'.$setting} = $env{$envname};
1.153 matthew 12720: } elsif ($type eq 'array') {
1.258 albertel 12721: $env{'form.'.$setting} = [
1.153 matthew 12722: map {
1.369 www 12723: &unescape($_);
1.258 albertel 12724: } split(',',$env{$envname})
1.153 matthew 12725: ];
12726: }
12727: }
12728: }
1.127 matthew 12729: }
12730:
1.618 raeburn 12731: #######################################################
12732: #######################################################
12733:
12734: =pod
12735:
12736: =head1 Domain E-mail Routines
12737:
12738: =over 4
12739:
1.648 raeburn 12740: =item * &build_recipient_list()
1.618 raeburn 12741:
1.884 raeburn 12742: Build recipient lists for five types of e-mail:
1.766 raeburn 12743: (a) Error Reports, (b) Package Updates, (c) lonstatus warnings/errors
1.884 raeburn 12744: (d) Help requests, (e) Course requests needing approval, generated by
12745: lonerrorhandler.pm, CHECKRPMS, loncron, lonsupportreq.pm and
12746: loncoursequeueadmin.pm respectively.
1.618 raeburn 12747:
12748: Inputs:
1.619 raeburn 12749: defmail (scalar - email address of default recipient),
1.618 raeburn 12750: mailing type (scalar - errormail, packagesmail, or helpdeskmail),
1.619 raeburn 12751: defdom (domain for which to retrieve configuration settings),
12752: origmail (scalar - email address of recipient from loncapa.conf,
12753: i.e., predates configuration by DC via domainprefs.pm
1.618 raeburn 12754:
1.655 raeburn 12755: Returns: comma separated list of addresses to which to send e-mail.
12756:
12757: =back
1.618 raeburn 12758:
12759: =cut
12760:
12761: ############################################################
12762: ############################################################
12763: sub build_recipient_list {
1.619 raeburn 12764: my ($defmail,$mailing,$defdom,$origmail) = @_;
1.618 raeburn 12765: my @recipients;
12766: my $otheremails;
12767: my %domconfig =
12768: &Apache::lonnet::get_dom('configuration',['contacts'],$defdom);
12769: if (ref($domconfig{'contacts'}) eq 'HASH') {
1.766 raeburn 12770: if (exists($domconfig{'contacts'}{$mailing})) {
12771: if (ref($domconfig{'contacts'}{$mailing}) eq 'HASH') {
12772: my @contacts = ('adminemail','supportemail');
12773: foreach my $item (@contacts) {
12774: if ($domconfig{'contacts'}{$mailing}{$item}) {
12775: my $addr = $domconfig{'contacts'}{$item};
12776: if (!grep(/^\Q$addr\E$/,@recipients)) {
12777: push(@recipients,$addr);
12778: }
1.619 raeburn 12779: }
1.766 raeburn 12780: $otheremails = $domconfig{'contacts'}{$mailing}{'others'};
1.618 raeburn 12781: }
12782: }
1.766 raeburn 12783: } elsif ($origmail ne '') {
12784: push(@recipients,$origmail);
1.618 raeburn 12785: }
1.619 raeburn 12786: } elsif ($origmail ne '') {
12787: push(@recipients,$origmail);
1.618 raeburn 12788: }
1.688 raeburn 12789: if (defined($defmail)) {
12790: if ($defmail ne '') {
12791: push(@recipients,$defmail);
12792: }
1.618 raeburn 12793: }
12794: if ($otheremails) {
1.619 raeburn 12795: my @others;
12796: if ($otheremails =~ /,/) {
12797: @others = split(/,/,$otheremails);
1.618 raeburn 12798: } else {
1.619 raeburn 12799: push(@others,$otheremails);
12800: }
12801: foreach my $addr (@others) {
12802: if (!grep(/^\Q$addr\E$/,@recipients)) {
12803: push(@recipients,$addr);
12804: }
1.618 raeburn 12805: }
12806: }
1.619 raeburn 12807: my $recipientlist = join(',',@recipients);
1.618 raeburn 12808: return $recipientlist;
12809: }
12810:
1.127 matthew 12811: ############################################################
12812: ############################################################
1.154 albertel 12813:
1.655 raeburn 12814: =pod
12815:
12816: =head1 Course Catalog Routines
12817:
12818: =over 4
12819:
12820: =item * &gather_categories()
12821:
12822: Converts category definitions - keys of categories hash stored in
12823: coursecategories in configuration.db on the primary library server in a
12824: domain - to an array. Also generates javascript and idx hash used to
12825: generate Domain Coordinator interface for editing Course Categories.
12826:
12827: Inputs:
1.663 raeburn 12828:
1.655 raeburn 12829: categories (reference to hash of category definitions).
1.663 raeburn 12830:
1.655 raeburn 12831: cats (reference to array of arrays/hashes which encapsulates hierarchy of
12832: categories and subcategories).
1.663 raeburn 12833:
1.655 raeburn 12834: idx (reference to hash of counters used in Domain Coordinator interface for
12835: editing Course Categories).
1.663 raeburn 12836:
1.655 raeburn 12837: jsarray (reference to array of categories used to create Javascript arrays for
12838: Domain Coordinator interface for editing Course Categories).
12839:
12840: Returns: nothing
12841:
12842: Side effects: populates cats, idx and jsarray.
12843:
12844: =cut
12845:
12846: sub gather_categories {
12847: my ($categories,$cats,$idx,$jsarray) = @_;
12848: my %counters;
12849: my $num = 0;
12850: foreach my $item (keys(%{$categories})) {
12851: my ($cat,$container,$depth) = map { &unescape($_); } split(/:/,$item);
12852: if ($container eq '' && $depth == 0) {
12853: $cats->[$depth][$categories->{$item}] = $cat;
12854: } else {
12855: $cats->[$depth]{$container}[$categories->{$item}] = $cat;
12856: }
12857: my ($escitem,$tail) = split(/:/,$item,2);
12858: if ($counters{$tail} eq '') {
12859: $counters{$tail} = $num;
12860: $num ++;
12861: }
12862: if (ref($idx) eq 'HASH') {
12863: $idx->{$item} = $counters{$tail};
12864: }
12865: if (ref($jsarray) eq 'ARRAY') {
12866: push(@{$jsarray->[$counters{$tail}]},$item);
12867: }
12868: }
12869: return;
12870: }
12871:
12872: =pod
12873:
12874: =item * &extract_categories()
12875:
12876: Used to generate breadcrumb trails for course categories.
12877:
12878: Inputs:
1.663 raeburn 12879:
1.655 raeburn 12880: categories (reference to hash of category definitions).
1.663 raeburn 12881:
1.655 raeburn 12882: cats (reference to array of arrays/hashes which encapsulates hierarchy of
12883: categories and subcategories).
1.663 raeburn 12884:
1.655 raeburn 12885: trails (reference to array of breacrumb trails for each category).
1.663 raeburn 12886:
1.655 raeburn 12887: allitems (reference to hash - key is category key
12888: (format: escaped(name):escaped(parent category):depth in hierarchy).
1.663 raeburn 12889:
1.655 raeburn 12890: idx (reference to hash of counters used in Domain Coordinator interface for
12891: editing Course Categories).
1.663 raeburn 12892:
1.655 raeburn 12893: jsarray (reference to array of categories used to create Javascript arrays for
12894: Domain Coordinator interface for editing Course Categories).
12895:
1.665 raeburn 12896: subcats (reference to hash of arrays containing all subcategories within each
12897: category, -recursive)
12898:
1.655 raeburn 12899: Returns: nothing
12900:
12901: Side effects: populates trails and allitems hash references.
12902:
12903: =cut
12904:
12905: sub extract_categories {
1.665 raeburn 12906: my ($categories,$cats,$trails,$allitems,$idx,$jsarray,$subcats) = @_;
1.655 raeburn 12907: if (ref($categories) eq 'HASH') {
12908: &gather_categories($categories,$cats,$idx,$jsarray);
12909: if (ref($cats->[0]) eq 'ARRAY') {
12910: for (my $i=0; $i<@{$cats->[0]}; $i++) {
12911: my $name = $cats->[0][$i];
12912: my $item = &escape($name).'::0';
12913: my $trailstr;
12914: if ($name eq 'instcode') {
12915: $trailstr = &mt('Official courses (with institutional codes)');
1.919 raeburn 12916: } elsif ($name eq 'communities') {
12917: $trailstr = &mt('Communities');
1.655 raeburn 12918: } else {
12919: $trailstr = $name;
12920: }
12921: if ($allitems->{$item} eq '') {
12922: push(@{$trails},$trailstr);
12923: $allitems->{$item} = scalar(@{$trails})-1;
12924: }
12925: my @parents = ($name);
12926: if (ref($cats->[1]{$name}) eq 'ARRAY') {
12927: for (my $j=0; $j<@{$cats->[1]{$name}}; $j++) {
12928: my $category = $cats->[1]{$name}[$j];
1.665 raeburn 12929: if (ref($subcats) eq 'HASH') {
12930: push(@{$subcats->{$item}},&escape($category).':'.&escape($name).':1');
12931: }
12932: &recurse_categories($cats,2,$category,$trails,$allitems,\@parents,$subcats);
12933: }
12934: } else {
12935: if (ref($subcats) eq 'HASH') {
12936: $subcats->{$item} = [];
1.655 raeburn 12937: }
12938: }
12939: }
12940: }
12941: }
12942: return;
12943: }
12944:
12945: =pod
12946:
12947: =item *&recurse_categories()
12948:
12949: Recursively used to generate breadcrumb trails for course categories.
12950:
12951: Inputs:
1.663 raeburn 12952:
1.655 raeburn 12953: cats (reference to array of arrays/hashes which encapsulates hierarchy of
12954: categories and subcategories).
1.663 raeburn 12955:
1.655 raeburn 12956: depth (current depth in hierarchy of categories and sub-categories - 0 indexed).
1.663 raeburn 12957:
12958: category (current course category, for which breadcrumb trail is being generated).
12959:
12960: trails (reference to array of breadcrumb trails for each category).
12961:
1.655 raeburn 12962: allitems (reference to hash - key is category key
12963: (format: escaped(name):escaped(parent category):depth in hierarchy).
1.663 raeburn 12964:
1.655 raeburn 12965: parents (array containing containers directories for current category,
12966: back to top level).
12967:
12968: Returns: nothing
12969:
12970: Side effects: populates trails and allitems hash references
12971:
12972: =cut
12973:
12974: sub recurse_categories {
1.665 raeburn 12975: my ($cats,$depth,$category,$trails,$allitems,$parents,$subcats) = @_;
1.655 raeburn 12976: my $shallower = $depth - 1;
12977: if (ref($cats->[$depth]{$category}) eq 'ARRAY') {
12978: for (my $k=0; $k<@{$cats->[$depth]{$category}}; $k++) {
12979: my $name = $cats->[$depth]{$category}[$k];
12980: my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower;
12981: my $trailstr = join(' -> ',(@{$parents},$category));
12982: if ($allitems->{$item} eq '') {
12983: push(@{$trails},$trailstr);
12984: $allitems->{$item} = scalar(@{$trails})-1;
12985: }
12986: my $deeper = $depth+1;
12987: push(@{$parents},$category);
1.665 raeburn 12988: if (ref($subcats) eq 'HASH') {
12989: my $subcat = &escape($name).':'.$category.':'.$depth;
12990: for (my $j=@{$parents}; $j>=0; $j--) {
12991: my $higher;
12992: if ($j > 0) {
12993: $higher = &escape($parents->[$j]).':'.
12994: &escape($parents->[$j-1]).':'.$j;
12995: } else {
12996: $higher = &escape($parents->[$j]).'::'.$j;
12997: }
12998: push(@{$subcats->{$higher}},$subcat);
12999: }
13000: }
13001: &recurse_categories($cats,$deeper,$name,$trails,$allitems,$parents,
13002: $subcats);
1.655 raeburn 13003: pop(@{$parents});
13004: }
13005: } else {
13006: my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower;
13007: my $trailstr = join(' -> ',(@{$parents},$category));
13008: if ($allitems->{$item} eq '') {
13009: push(@{$trails},$trailstr);
13010: $allitems->{$item} = scalar(@{$trails})-1;
13011: }
13012: }
13013: return;
13014: }
13015:
1.663 raeburn 13016: =pod
13017:
13018: =item *&assign_categories_table()
13019:
13020: Create a datatable for display of hierarchical categories in a domain,
13021: with checkboxes to allow a course to be categorized.
13022:
13023: Inputs:
13024:
13025: cathash - reference to hash of categories defined for the domain (from
13026: configuration.db)
13027:
13028: currcat - scalar with an & separated list of categories assigned to a course.
13029:
1.919 raeburn 13030: type - scalar contains course type (Course or Community).
13031:
1.663 raeburn 13032: Returns: $output (markup to be displayed)
13033:
13034: =cut
13035:
13036: sub assign_categories_table {
1.919 raeburn 13037: my ($cathash,$currcat,$type) = @_;
1.663 raeburn 13038: my $output;
13039: if (ref($cathash) eq 'HASH') {
13040: my (@cats,@trails,%allitems,%idx,@jsarray,@path,$maxdepth);
13041: &extract_categories($cathash,\@cats,\@trails,\%allitems,\%idx,\@jsarray);
13042: $maxdepth = scalar(@cats);
13043: if (@cats > 0) {
13044: my $itemcount = 0;
13045: if (ref($cats[0]) eq 'ARRAY') {
13046: my @currcategories;
13047: if ($currcat ne '') {
13048: @currcategories = split('&',$currcat);
13049: }
1.919 raeburn 13050: my $table;
1.663 raeburn 13051: for (my $i=0; $i<@{$cats[0]}; $i++) {
13052: my $parent = $cats[0][$i];
1.919 raeburn 13053: next if ($parent eq 'instcode');
13054: if ($type eq 'Community') {
13055: next unless ($parent eq 'communities');
13056: } else {
13057: next if ($parent eq 'communities');
13058: }
1.663 raeburn 13059: my $css_class = $itemcount%2?' class="LC_odd_row"':'';
13060: my $item = &escape($parent).'::0';
13061: my $checked = '';
13062: if (@currcategories > 0) {
13063: if (grep(/^\Q$item\E$/,@currcategories)) {
1.772 bisitz 13064: $checked = ' checked="checked"';
1.663 raeburn 13065: }
13066: }
1.919 raeburn 13067: my $parent_title = $parent;
13068: if ($parent eq 'communities') {
13069: $parent_title = &mt('Communities');
13070: }
13071: $table .= '<tr '.$css_class.'><td><span class="LC_nobreak">'.
13072: '<input type="checkbox" name="usecategory" value="'.
13073: $item.'"'.$checked.' />'.$parent_title.'</span>'.
13074: '<input type="hidden" name="catname" value="'.$parent.'" /></td>';
1.663 raeburn 13075: my $depth = 1;
13076: push(@path,$parent);
1.919 raeburn 13077: $table .= &assign_category_rows($itemcount,\@cats,$depth,$parent,\@path,\@currcategories);
1.663 raeburn 13078: pop(@path);
1.919 raeburn 13079: $table .= '</tr><tr><td colspan="'.$maxdepth.'" class="LC_row_separator"></td></tr>';
1.663 raeburn 13080: $itemcount ++;
13081: }
1.919 raeburn 13082: if ($itemcount) {
13083: $output = &Apache::loncommon::start_data_table().
13084: $table.
13085: &Apache::loncommon::end_data_table();
13086: }
1.663 raeburn 13087: }
13088: }
13089: }
13090: return $output;
13091: }
13092:
13093: =pod
13094:
13095: =item *&assign_category_rows()
13096:
13097: Create a datatable row for display of nested categories in a domain,
13098: with checkboxes to allow a course to be categorized,called recursively.
13099:
13100: Inputs:
13101:
13102: itemcount - track row number for alternating colors
13103:
13104: cats - reference to array of arrays/hashes which encapsulates hierarchy of
13105: categories and subcategories.
13106:
13107: depth - current depth in hierarchy of categories and sub-categories - 0 indexed.
13108:
13109: parent - parent of current category item
13110:
13111: path - Array containing all categories back up through the hierarchy from the
13112: current category to the top level.
13113:
13114: currcategories - reference to array of current categories assigned to the course
13115:
13116: Returns: $output (markup to be displayed).
13117:
13118: =cut
13119:
13120: sub assign_category_rows {
13121: my ($itemcount,$cats,$depth,$parent,$path,$currcategories) = @_;
13122: my ($text,$name,$item,$chgstr);
13123: if (ref($cats) eq 'ARRAY') {
13124: my $maxdepth = scalar(@{$cats});
13125: if (ref($cats->[$depth]) eq 'HASH') {
13126: if (ref($cats->[$depth]{$parent}) eq 'ARRAY') {
13127: my $numchildren = @{$cats->[$depth]{$parent}};
13128: my $css_class = $itemcount%2?' class="LC_odd_row"':'';
13129: $text .= '<td><table class="LC_datatable">';
13130: for (my $j=0; $j<$numchildren; $j++) {
13131: $name = $cats->[$depth]{$parent}[$j];
13132: $item = &escape($name).':'.&escape($parent).':'.$depth;
13133: my $deeper = $depth+1;
13134: my $checked = '';
13135: if (ref($currcategories) eq 'ARRAY') {
13136: if (@{$currcategories} > 0) {
13137: if (grep(/^\Q$item\E$/,@{$currcategories})) {
1.772 bisitz 13138: $checked = ' checked="checked"';
1.663 raeburn 13139: }
13140: }
13141: }
1.664 raeburn 13142: $text .= '<tr><td><span class="LC_nobreak"><label>'.
13143: '<input type="checkbox" name="usecategory" value="'.
1.675 raeburn 13144: $item.'"'.$checked.' />'.$name.'</label></span>'.
13145: '<input type="hidden" name="catname" value="'.$name.'" />'.
13146: '</td><td>';
1.663 raeburn 13147: if (ref($path) eq 'ARRAY') {
13148: push(@{$path},$name);
13149: $text .= &assign_category_rows($itemcount,$cats,$deeper,$name,$path,$currcategories);
13150: pop(@{$path});
13151: }
13152: $text .= '</td></tr>';
13153: }
13154: $text .= '</table></td>';
13155: }
13156: }
13157: }
13158: return $text;
13159: }
13160:
1.655 raeburn 13161: ############################################################
13162: ############################################################
13163:
13164:
1.443 albertel 13165: sub commit_customrole {
1.664 raeburn 13166: my ($udom,$uname,$url,$three,$four,$five,$start,$end,$context) = @_;
1.630 raeburn 13167: my $output = &mt('Assigning custom role').' "'.$five.'" by '.$four.':'.$three.' in '.$url.
1.443 albertel 13168: ($start?', '.&mt('starting').' '.localtime($start):'').
13169: ($end?', ending '.localtime($end):'').': <b>'.
13170: &Apache::lonnet::assigncustomrole(
1.664 raeburn 13171: $udom,$uname,$url,$three,$four,$five,$end,$start,undef,undef,$context).
1.443 albertel 13172: '</b><br />';
13173: return $output;
13174: }
13175:
13176: sub commit_standardrole {
1.541 raeburn 13177: my ($udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context) = @_;
13178: my ($output,$logmsg,$linefeed);
13179: if ($context eq 'auto') {
13180: $linefeed = "\n";
13181: } else {
13182: $linefeed = "<br />\n";
13183: }
1.443 albertel 13184: if ($three eq 'st') {
1.541 raeburn 13185: my $result = &commit_studentrole(\$logmsg,$udom,$uname,$url,$three,$start,$end,
13186: $one,$two,$sec,$context);
13187: if (($result =~ /^error/) || ($result eq 'not_in_class') ||
1.626 raeburn 13188: ($result eq 'unknown_course') || ($result eq 'refused')) {
13189: $output = $logmsg.' '.&mt('Error: ').$result."\n";
1.443 albertel 13190: } else {
1.541 raeburn 13191: $output = $logmsg.$linefeed.&mt('Assigning').' '.$three.' in '.$url.
1.443 albertel 13192: ($start?', '.&mt('starting').' '.localtime($start):'').
1.541 raeburn 13193: ($end?', '.&mt('ending').' '.localtime($end):'').': ';
13194: if ($context eq 'auto') {
13195: $output .= $result.$linefeed.&mt('Add to classlist').': ok';
13196: } else {
13197: $output .= '<b>'.$result.'</b>'.$linefeed.
13198: &mt('Add to classlist').': <b>ok</b>';
13199: }
13200: $output .= $linefeed;
1.443 albertel 13201: }
13202: } else {
13203: $output = &mt('Assigning').' '.$three.' in '.$url.
13204: ($start?', '.&mt('starting').' '.localtime($start):'').
1.541 raeburn 13205: ($end?', '.&mt('ending').' '.localtime($end):'').': ';
1.652 raeburn 13206: my $result = &Apache::lonnet::assignrole($udom,$uname,$url,$three,$end,$start,'','',$context);
1.541 raeburn 13207: if ($context eq 'auto') {
13208: $output .= $result.$linefeed;
13209: } else {
13210: $output .= '<b>'.$result.'</b>'.$linefeed;
13211: }
1.443 albertel 13212: }
13213: return $output;
13214: }
13215:
13216: sub commit_studentrole {
1.541 raeburn 13217: my ($logmsg,$udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context) = @_;
1.626 raeburn 13218: my ($result,$linefeed,$oldsecurl,$newsecurl);
1.541 raeburn 13219: if ($context eq 'auto') {
13220: $linefeed = "\n";
13221: } else {
13222: $linefeed = '<br />'."\n";
13223: }
1.443 albertel 13224: if (defined($one) && defined($two)) {
13225: my $cid=$one.'_'.$two;
13226: my $oldsec=&Apache::lonnet::getsection($udom,$uname,$cid);
13227: my $secchange = 0;
13228: my $expire_role_result;
13229: my $modify_section_result;
1.628 raeburn 13230: if ($oldsec ne '-1') {
13231: if ($oldsec ne $sec) {
1.443 albertel 13232: $secchange = 1;
1.628 raeburn 13233: my $now = time;
1.443 albertel 13234: my $uurl='/'.$cid;
13235: $uurl=~s/\_/\//g;
13236: if ($oldsec) {
13237: $uurl.='/'.$oldsec;
13238: }
1.626 raeburn 13239: $oldsecurl = $uurl;
1.628 raeburn 13240: $expire_role_result =
1.652 raeburn 13241: &Apache::lonnet::assignrole($udom,$uname,$uurl,'st',$now,'','',$context);
1.628 raeburn 13242: if ($env{'request.course.sec'} ne '') {
13243: if ($expire_role_result eq 'refused') {
13244: my @roles = ('st');
13245: my @statuses = ('previous');
13246: my @roledoms = ($one);
13247: my $withsec = 1;
13248: my %roleshash =
13249: &Apache::lonnet::get_my_roles($uname,$udom,'userroles',
13250: \@statuses,\@roles,\@roledoms,$withsec);
13251: if (defined ($roleshash{$two.':'.$one.':st:'.$oldsec})) {
13252: my ($oldstart,$oldend) =
13253: split(':',$roleshash{$two.':'.$one.':st:'.$oldsec});
13254: if ($oldend > 0 && $oldend <= $now) {
13255: $expire_role_result = 'ok';
13256: }
13257: }
13258: }
13259: }
1.443 albertel 13260: $result = $expire_role_result;
13261: }
13262: }
13263: if (($expire_role_result eq 'ok') || ($secchange == 0)) {
1.652 raeburn 13264: $modify_section_result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,'','',$cid,'',$context);
1.443 albertel 13265: if ($modify_section_result =~ /^ok/) {
13266: if ($secchange == 1) {
1.628 raeburn 13267: if ($sec eq '') {
13268: $$logmsg .= &mt('Section for [_1] switched from (possibly expired) old section: [_2] to student role without a section.',$uname,$oldsec).$linefeed;
13269: } else {
13270: $$logmsg .= &mt('Section for [_1] switched from (possibly expired) old section: [_2] to new section: [_3].',$uname,$oldsec,$sec).$linefeed;
13271: }
1.443 albertel 13272: } elsif ($oldsec eq '-1') {
1.628 raeburn 13273: if ($sec eq '') {
13274: $$logmsg .= &mt('New student role without a section for [_1] in course [_2].',$uname,$cid).$linefeed;
13275: } else {
13276: $$logmsg .= &mt('New student role for [_1] in section [_2] in course [_3].',$uname,$sec,$cid).$linefeed;
13277: }
1.443 albertel 13278: } else {
1.628 raeburn 13279: if ($sec eq '') {
13280: $$logmsg .= &mt('Student [_1] assigned to course [_2] without a section.',$uname,$cid).$linefeed;
13281: } else {
13282: $$logmsg .= &mt('Student [_1] assigned to section [_2] in course [_3].',$uname,$sec,$cid).$linefeed;
13283: }
1.443 albertel 13284: }
13285: } else {
1.628 raeburn 13286: if ($secchange) {
13287: $$logmsg .= &mt('Error when attempting section change for [_1] from old section "[_2]" to new section: "[_3]" in course [_4] -error:',$uname,$oldsec,$sec,$cid).' '.$modify_section_result.$linefeed;
13288: } else {
13289: $$logmsg .= &mt('Error when attempting to modify role for [_1] for section: "[_2]" in course [_3] -error:',$uname,$sec,$cid).' '.$modify_section_result.$linefeed;
13290: }
1.443 albertel 13291: }
13292: $result = $modify_section_result;
13293: } elsif ($secchange == 1) {
1.628 raeburn 13294: if ($oldsec eq '') {
1.1103 raeburn 13295: $$logmsg .= &mt('Error when attempting to expire existing role without a section for [_1] in course [_2] -error: ',$uname,$cid).' '.$expire_role_result.$linefeed;
1.628 raeburn 13296: } else {
13297: $$logmsg .= &mt('Error when attempting to expire existing role for [_1] in section [_2] in course [_3] -error: ',$uname,$oldsec,$cid).' '.$expire_role_result.$linefeed;
13298: }
1.626 raeburn 13299: if ($expire_role_result eq 'refused') {
13300: my $newsecurl = '/'.$cid;
13301: $newsecurl =~ s/\_/\//g;
13302: if ($sec ne '') {
13303: $newsecurl.='/'.$sec;
13304: }
13305: if (&Apache::lonnet::allowed('cst',$newsecurl) && !(&Apache::lonnet::allowed('cst',$oldsecurl))) {
13306: if ($sec eq '') {
13307: $$logmsg .= &mt('Although your current role has privileges to add students to section "[_1]", you do not have privileges to modify existing enrollments unaffiliated with any section.',$sec).$linefeed;
13308: } else {
13309: $$logmsg .= &mt('Although your current role has privileges to add students to section "[_1]", you do not have privileges to modify existing enrollments in other sections.',$sec).$linefeed;
13310: }
13311: }
13312: }
1.443 albertel 13313: }
13314: } else {
1.626 raeburn 13315: $$logmsg .= &mt('Incomplete course id defined.').$linefeed.&mt('Addition of user [_1] from domain [_2] to course [_3], section [_4] not completed.',$uname,$udom,$one.'_'.$two,$sec).$linefeed;
1.443 albertel 13316: $result = "error: incomplete course id\n";
13317: }
13318: return $result;
13319: }
13320:
1.1108 raeburn 13321: sub show_role_extent {
13322: my ($scope,$context,$role) = @_;
13323: $scope =~ s{^/}{};
13324: my @courseroles = &Apache::lonuserutils::roles_by_context('course',1);
13325: push(@courseroles,'co');
13326: my @authorroles = &Apache::lonuserutils::roles_by_context('author');
13327: if (($context eq 'course') || (grep(/^\Q$role\E/,@courseroles))) {
13328: $scope =~ s{/}{_};
13329: return '<span class="LC_cusr_emph">'.$env{'course.'.$scope.'.description'}.'</span>';
13330: } elsif (($context eq 'author') || (grep(/^\Q$role\E/,@authorroles))) {
13331: my ($audom,$auname) = split(/\//,$scope);
13332: return &mt('[_1] Author Space','<span class="LC_cusr_emph">'.
13333: &Apache::loncommon::plainname($auname,$audom).'</span>');
13334: } else {
13335: $scope =~ s{/$}{};
13336: return &mt('Domain: [_1]','<span class="LC_cusr_emph">'.
13337: &Apache::lonnet::domain($scope,'description').'</span>');
13338: }
13339: }
13340:
1.443 albertel 13341: ############################################################
13342: ############################################################
13343:
1.566 albertel 13344: sub check_clone {
1.578 raeburn 13345: my ($args,$linefeed) = @_;
1.566 albertel 13346: my $cloneid='/'.$args->{'clonedomain'}.'/'.$args->{'clonecourse'};
13347: my ($clonecrsudom,$clonecrsunum)= &LONCAPA::split_courseid($cloneid);
13348: my $clonehome=&Apache::lonnet::homeserver($clonecrsunum,$clonecrsudom);
13349: my $clonemsg;
13350: my $can_clone = 0;
1.944 raeburn 13351: my $lctype = lc($args->{'crstype'});
1.908 raeburn 13352: if ($lctype ne 'community') {
13353: $lctype = 'course';
13354: }
1.566 albertel 13355: if ($clonehome eq 'no_host') {
1.944 raeburn 13356: if ($args->{'crstype'} eq 'Community') {
1.908 raeburn 13357: $clonemsg = &mt('No new community created.').$linefeed.&mt('A new community could not be cloned from the specified original - [_1] - because it is a non-existent community.',$args->{'clonecourse'}.':'.$args->{'clonedomain'});
13358: } else {
13359: $clonemsg = &mt('No new course created.').$linefeed.&mt('A new course could not be cloned from the specified original - [_1] - because it is a non-existent course.',$args->{'clonecourse'}.':'.$args->{'clonedomain'});
13360: }
1.566 albertel 13361: } else {
13362: my %clonedesc = &Apache::lonnet::coursedescription($cloneid,{'one_time' => 1});
1.944 raeburn 13363: if ($args->{'crstype'} eq 'Community') {
1.908 raeburn 13364: if ($clonedesc{'type'} ne 'Community') {
13365: $clonemsg = &mt('No new community created.').$linefeed.&mt('A new community could not be cloned from the specified original - [_1] - because it is a course not a community.',$args->{'clonecourse'}.':'.$args->{'clonedomain'});
13366: return ($can_clone, $clonemsg, $cloneid, $clonehome);
13367: }
13368: }
1.882 raeburn 13369: if (($env{'request.role.domain'} eq $args->{'clonedomain'}) &&
13370: (&Apache::lonnet::allowed('ccc',$env{'request.role.domain'}))) {
1.566 albertel 13371: $can_clone = 1;
13372: } else {
13373: my %clonehash = &Apache::lonnet::get('environment',['cloners'],
13374: $args->{'clonedomain'},$args->{'clonecourse'});
13375: my @cloners = split(/,/,$clonehash{'cloners'});
1.578 raeburn 13376: if (grep(/^\*$/,@cloners)) {
13377: $can_clone = 1;
13378: } elsif (grep(/^\*\:\Q$args->{'ccdomain'}\E$/,@cloners)) {
13379: $can_clone = 1;
13380: } else {
1.908 raeburn 13381: my $ccrole = 'cc';
1.944 raeburn 13382: if ($args->{'crstype'} eq 'Community') {
1.908 raeburn 13383: $ccrole = 'co';
13384: }
1.578 raeburn 13385: my %roleshash =
13386: &Apache::lonnet::get_my_roles($args->{'ccuname'},
13387: $args->{'ccdomain'},
1.908 raeburn 13388: 'userroles',['active'],[$ccrole],
1.578 raeburn 13389: [$args->{'clonedomain'}]);
1.908 raeburn 13390: if (($roleshash{$args->{'clonecourse'}.':'.$args->{'clonedomain'}.':'.$ccrole}) || (grep(/^\Q$args->{'ccuname'}\E:\Q$args->{'ccdomain'}\E$/,@cloners))) {
1.942 raeburn 13391: $can_clone = 1;
13392: } elsif (&Apache::lonnet::is_course_owner($args->{'clonedomain'},$args->{'clonecourse'},$args->{'ccuname'},$args->{'ccdomain'})) {
13393: $can_clone = 1;
13394: } else {
1.944 raeburn 13395: if ($args->{'crstype'} eq 'Community') {
1.908 raeburn 13396: $clonemsg = &mt('No new community created.').$linefeed.&mt('The new community could not be cloned from the existing community because the new community owner ([_1]) does not have cloning rights in the existing community ([_2]).',$args->{'ccuname'}.':'.$args->{'ccdomain'},$clonedesc{'description'});
13397: } else {
13398: $clonemsg = &mt('No new course created.').$linefeed.&mt('The new course could not be cloned from the existing course because the new course owner ([_1]) does not have cloning rights in the existing course ([_2]).',$args->{'ccuname'}.':'.$args->{'ccdomain'},$clonedesc{'description'});
13399: }
1.578 raeburn 13400: }
1.566 albertel 13401: }
1.578 raeburn 13402: }
1.566 albertel 13403: }
13404: return ($can_clone, $clonemsg, $cloneid, $clonehome);
13405: }
13406:
1.444 albertel 13407: sub construct_course {
1.885 raeburn 13408: my ($args,$logmsg,$courseid,$crsudom,$crsunum,$udom,$uname,$context,$cnum,$category) = @_;
1.444 albertel 13409: my $outcome;
1.541 raeburn 13410: my $linefeed = '<br />'."\n";
13411: if ($context eq 'auto') {
13412: $linefeed = "\n";
13413: }
1.566 albertel 13414:
13415: #
13416: # Are we cloning?
13417: #
13418: my ($can_clone, $clonemsg, $cloneid, $clonehome);
13419: if (($args->{'clonecourse'}) && ($args->{'clonedomain'})) {
1.578 raeburn 13420: ($can_clone, $clonemsg, $cloneid, $clonehome) = &check_clone($args,$linefeed);
1.566 albertel 13421: if ($context ne 'auto') {
1.578 raeburn 13422: if ($clonemsg ne '') {
13423: $clonemsg = '<span class="LC_error">'.$clonemsg.'</span>';
13424: }
1.566 albertel 13425: }
13426: $outcome .= $clonemsg.$linefeed;
13427:
13428: if (!$can_clone) {
13429: return (0,$outcome);
13430: }
13431: }
13432:
1.444 albertel 13433: #
13434: # Open course
13435: #
13436: my $crstype = lc($args->{'crstype'});
13437: my %cenv=();
13438: $$courseid=&Apache::lonnet::createcourse($args->{'course_domain'},
13439: $args->{'cdescr'},
13440: $args->{'curl'},
13441: $args->{'course_home'},
13442: $args->{'nonstandard'},
13443: $args->{'crscode'},
13444: $args->{'ccuname'}.':'.
13445: $args->{'ccdomain'},
1.882 raeburn 13446: $args->{'crstype'},
1.885 raeburn 13447: $cnum,$context,$category);
1.444 albertel 13448:
13449: # Note: The testing routines depend on this being output; see
13450: # Utils::Course. This needs to at least be output as a comment
13451: # if anyone ever decides to not show this, and Utils::Course::new
13452: # will need to be suitably modified.
1.541 raeburn 13453: $outcome .= &mt('New LON-CAPA [_1] ID: [_2]',$crstype,$$courseid).$linefeed;
1.943 raeburn 13454: if ($$courseid =~ /^error:/) {
13455: return (0,$outcome);
13456: }
13457:
1.444 albertel 13458: #
13459: # Check if created correctly
13460: #
1.479 albertel 13461: ($$crsudom,$$crsunum)= &LONCAPA::split_courseid($$courseid);
1.444 albertel 13462: my $crsuhome=&Apache::lonnet::homeserver($$crsunum,$$crsudom);
1.943 raeburn 13463: if ($crsuhome eq 'no_host') {
13464: $outcome .= &mt('Course creation failed, unrecognized course home server.').$linefeed;
13465: return (0,$outcome);
13466: }
1.541 raeburn 13467: $outcome .= &mt('Created on').': '.$crsuhome.$linefeed;
1.566 albertel 13468:
1.444 albertel 13469: #
1.566 albertel 13470: # Do the cloning
13471: #
13472: if ($can_clone && $cloneid) {
13473: $clonemsg = &mt('Cloning [_1] from [_2]',$crstype,$clonehome);
13474: if ($context ne 'auto') {
13475: $clonemsg = '<span class="LC_success">'.$clonemsg.'</span>';
13476: }
13477: $outcome .= $clonemsg.$linefeed;
13478: my %oldcenv=&Apache::lonnet::dump('environment',$$crsudom,$$crsunum);
1.444 albertel 13479: # Copy all files
1.637 www 13480: &Apache::lonclonecourse::copycoursefiles($cloneid,$$courseid,$args->{'datemode'},$args->{'dateshift'});
1.444 albertel 13481: # Restore URL
1.566 albertel 13482: $cenv{'url'}=$oldcenv{'url'};
1.444 albertel 13483: # Restore title
1.566 albertel 13484: $cenv{'description'}=$oldcenv{'description'};
1.955 raeburn 13485: # Restore creation date, creator and creation context.
13486: $cenv{'internal.created'}=$oldcenv{'internal.created'};
13487: $cenv{'internal.creator'}=$oldcenv{'internal.creator'};
13488: $cenv{'internal.creationcontext'}=$oldcenv{'internal.creationcontext'};
1.444 albertel 13489: # Mark as cloned
1.566 albertel 13490: $cenv{'clonedfrom'}=$cloneid;
1.638 www 13491: # Need to clone grading mode
13492: my %newenv=&Apache::lonnet::get('environment',['grading'],$$crsudom,$$crsunum);
13493: $cenv{'grading'}=$newenv{'grading'};
13494: # Do not clone these environment entries
13495: &Apache::lonnet::del('environment',
13496: ['default_enrollment_start_date',
13497: 'default_enrollment_end_date',
13498: 'question.email',
13499: 'policy.email',
13500: 'comment.email',
13501: 'pch.users.denied',
1.725 raeburn 13502: 'plc.users.denied',
13503: 'hidefromcat',
13504: 'categories'],
1.638 www 13505: $$crsudom,$$crsunum);
1.444 albertel 13506: }
1.566 albertel 13507:
1.444 albertel 13508: #
13509: # Set environment (will override cloned, if existing)
13510: #
13511: my @sections = ();
13512: my @xlists = ();
13513: if ($args->{'crstype'}) {
13514: $cenv{'type'}=$args->{'crstype'};
13515: }
13516: if ($args->{'crsid'}) {
13517: $cenv{'courseid'}=$args->{'crsid'};
13518: }
13519: if ($args->{'crscode'}) {
13520: $cenv{'internal.coursecode'}=$args->{'crscode'};
13521: }
13522: if ($args->{'crsquota'} ne '') {
13523: $cenv{'internal.coursequota'}=$args->{'crsquota'};
13524: } else {
13525: $cenv{'internal.coursequota'}=$args->{'crsquota'} = 20;
13526: }
13527: if ($args->{'ccuname'}) {
13528: $cenv{'internal.courseowner'} = $args->{'ccuname'}.
13529: ':'.$args->{'ccdomain'};
13530: } else {
13531: $cenv{'internal.courseowner'} = $args->{'curruser'};
13532: }
13533: my @badclasses = (); # Used to accumulate sections/crosslistings that did not pass classlist access check for course owner.
13534: if ($args->{'crssections'}) {
13535: $cenv{'internal.sectionnums'} = '';
13536: if ($args->{'crssections'} =~ m/,/) {
13537: @sections = split/,/,$args->{'crssections'};
13538: } else {
13539: $sections[0] = $args->{'crssections'};
13540: }
13541: if (@sections > 0) {
13542: foreach my $item (@sections) {
13543: my ($sec,$gp) = split/:/,$item;
13544: my $class = $args->{'crscode'}.$sec;
13545: my $addcheck = &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$class,$cenv{'internal.courseowner'});
13546: $cenv{'internal.sectionnums'} .= $item.',';
13547: unless ($addcheck eq 'ok') {
13548: push @badclasses, $class;
13549: }
13550: }
13551: $cenv{'internal.sectionnums'} =~ s/,$//;
13552: }
13553: }
13554: # do not hide course coordinator from staff listing,
13555: # even if privileged
13556: $cenv{'nothideprivileged'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
13557: # add crosslistings
13558: if ($args->{'crsxlist'}) {
13559: $cenv{'internal.crosslistings'}='';
13560: if ($args->{'crsxlist'} =~ m/,/) {
13561: @xlists = split/,/,$args->{'crsxlist'};
13562: } else {
13563: $xlists[0] = $args->{'crsxlist'};
13564: }
13565: if (@xlists > 0) {
13566: foreach my $item (@xlists) {
13567: my ($xl,$gp) = split/:/,$item;
13568: my $addcheck = &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$xl,$cenv{'internal.courseowner'});
13569: $cenv{'internal.crosslistings'} .= $item.',';
13570: unless ($addcheck eq 'ok') {
13571: push @badclasses, $xl;
13572: }
13573: }
13574: $cenv{'internal.crosslistings'} =~ s/,$//;
13575: }
13576: }
13577: if ($args->{'autoadds'}) {
13578: $cenv{'internal.autoadds'}=$args->{'autoadds'};
13579: }
13580: if ($args->{'autodrops'}) {
13581: $cenv{'internal.autodrops'}=$args->{'autodrops'};
13582: }
13583: # check for notification of enrollment changes
13584: my @notified = ();
13585: if ($args->{'notify_owner'}) {
13586: if ($args->{'ccuname'} ne '') {
13587: push(@notified,$args->{'ccuname'}.':'.$args->{'ccdomain'});
13588: }
13589: }
13590: if ($args->{'notify_dc'}) {
13591: if ($uname ne '') {
1.630 raeburn 13592: push(@notified,$uname.':'.$udom);
1.444 albertel 13593: }
13594: }
13595: if (@notified > 0) {
13596: my $notifylist;
13597: if (@notified > 1) {
13598: $notifylist = join(',',@notified);
13599: } else {
13600: $notifylist = $notified[0];
13601: }
13602: $cenv{'internal.notifylist'} = $notifylist;
13603: }
13604: if (@badclasses > 0) {
13605: my %lt=&Apache::lonlocal::texthash(
13606: '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',
13607: 'dnhr' => 'does not have rights to access enrollment in these classes',
13608: 'adby' => 'as determined by the policies of your institution on access to official classlists'
13609: );
1.541 raeburn 13610: my $badclass_msg = $cenv{'internal.courseowner'}.') - '.$lt{'dnhr'}.
13611: ' ('.$lt{'adby'}.')';
13612: if ($context eq 'auto') {
13613: $outcome .= $badclass_msg.$linefeed;
1.566 albertel 13614: $outcome .= '<div class="LC_warning">'.$badclass_msg.$linefeed.'<ul>'."\n";
1.541 raeburn 13615: foreach my $item (@badclasses) {
13616: if ($context eq 'auto') {
13617: $outcome .= " - $item\n";
13618: } else {
13619: $outcome .= "<li>$item</li>\n";
13620: }
13621: }
13622: if ($context eq 'auto') {
13623: $outcome .= $linefeed;
13624: } else {
1.566 albertel 13625: $outcome .= "</ul><br /><br /></div>\n";
1.541 raeburn 13626: }
13627: }
1.444 albertel 13628: }
13629: if ($args->{'no_end_date'}) {
13630: $args->{'endaccess'} = 0;
13631: }
13632: $cenv{'internal.autostart'}=$args->{'enrollstart'};
13633: $cenv{'internal.autoend'}=$args->{'enrollend'};
13634: $cenv{'default_enrollment_start_date'}=$args->{'startaccess'};
13635: $cenv{'default_enrollment_end_date'}=$args->{'endaccess'};
13636: if ($args->{'showphotos'}) {
13637: $cenv{'internal.showphotos'}=$args->{'showphotos'};
13638: }
13639: $cenv{'internal.authtype'} = $args->{'authtype'};
13640: $cenv{'internal.autharg'} = $args->{'autharg'};
13641: if ( ($cenv{'internal.authtype'} =~ /^krb/) && ($cenv{'internal.autoadds'} == 1)) {
13642: if (! defined($cenv{'internal.autharg'}) || $cenv{'internal.autharg'} eq '') {
1.541 raeburn 13643: 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');
13644: if ($context eq 'auto') {
13645: $outcome .= $krb_msg;
13646: } else {
1.566 albertel 13647: $outcome .= '<span class="LC_error">'.$krb_msg.'</span>';
1.541 raeburn 13648: }
13649: $outcome .= $linefeed;
1.444 albertel 13650: }
13651: }
13652: if (($args->{'ccdomain'}) && ($args->{'ccuname'})) {
13653: if ($args->{'setpolicy'}) {
13654: $cenv{'policy.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
13655: }
13656: if ($args->{'setcontent'}) {
13657: $cenv{'question.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
13658: }
13659: }
13660: if ($args->{'reshome'}) {
13661: $cenv{'reshome'}=$args->{'reshome'}.'/';
13662: $cenv{'reshome'}=~s/\/+$/\//;
13663: }
13664: #
13665: # course has keyed access
13666: #
13667: if ($args->{'setkeys'}) {
13668: $cenv{'keyaccess'}='yes';
13669: }
13670: # if specified, key authority is not course, but user
13671: # only active if keyaccess is yes
13672: if ($args->{'keyauth'}) {
1.487 albertel 13673: my ($user,$domain) = split(':',$args->{'keyauth'});
13674: $user = &LONCAPA::clean_username($user);
13675: $domain = &LONCAPA::clean_username($domain);
1.488 foxr 13676: if ($user ne '' && $domain ne '') {
1.487 albertel 13677: $cenv{'keyauth'}=$user.':'.$domain;
1.444 albertel 13678: }
13679: }
13680:
13681: if ($args->{'disresdis'}) {
13682: $cenv{'pch.roles.denied'}='st';
13683: }
13684: if ($args->{'disablechat'}) {
13685: $cenv{'plc.roles.denied'}='st';
13686: }
13687:
13688: # Record we've not yet viewed the Course Initialization Helper for this
13689: # course
13690: $cenv{'course.helper.not.run'} = 1;
13691: #
13692: # Use new Randomseed
13693: #
13694: $cenv{'rndseed'}=&Apache::lonnet::latest_rnd_algorithm_id();;
13695: $cenv{'receiptalg'}=&Apache::lonnet::latest_receipt_algorithm_id();;
13696: #
13697: # The encryption code and receipt prefix for this course
13698: #
13699: $cenv{'internal.encseed'}=$Apache::lonnet::perlvar{'lonReceipt'}.$$.time.int(rand(9999));
13700: $cenv{'internal.encpref'}=100+int(9*rand(99));
13701: #
13702: # By default, use standard grading
13703: if (!defined($cenv{'grading'})) { $cenv{'grading'} = 'standard'; }
13704:
1.541 raeburn 13705: $outcome .= $linefeed.&mt('Setting environment').': '.
13706: &Apache::lonnet::put('environment',\%cenv,$$crsudom,$$crsunum).$linefeed;
1.444 albertel 13707: #
13708: # Open all assignments
13709: #
13710: if ($args->{'openall'}) {
13711: my $storeunder=$$crsudom.'_'.$$crsunum.'.0.opendate';
13712: my %storecontent = ($storeunder => time,
13713: $storeunder.'.type' => 'date_start');
13714:
13715: $outcome .= &mt('Opening all assignments').': '.&Apache::lonnet::cput
1.541 raeburn 13716: ('resourcedata',\%storecontent,$$crsudom,$$crsunum).$linefeed;
1.444 albertel 13717: }
13718: #
13719: # Set first page
13720: #
13721: unless (($args->{'nonstandard'}) || ($args->{'firstres'} eq 'blank')
13722: || ($cloneid)) {
1.445 albertel 13723: use LONCAPA::map;
1.444 albertel 13724: $outcome .= &mt('Setting first resource').': ';
1.445 albertel 13725:
13726: my $map = '/uploaded/'.$$crsudom.'/'.$$crsunum.'/default.sequence';
13727: my ($errtext,$fatal)=&LONCAPA::map::mapread($map);
13728:
1.444 albertel 13729: $outcome .= ($fatal?$errtext:'read ok').' - ';
13730: my $title; my $url;
13731: if ($args->{'firstres'} eq 'syl') {
1.690 bisitz 13732: $title=&mt('Syllabus');
1.444 albertel 13733: $url='/public/'.$$crsudom.'/'.$$crsunum.'/syllabus';
13734: } else {
1.963 raeburn 13735: $title=&mt('Table of Contents');
1.444 albertel 13736: $url='/adm/navmaps';
13737: }
1.445 albertel 13738:
13739: $LONCAPA::map::resources[1]=$title.':'.$url.':false:start:res';
13740: (my $outtext,$errtext) = &LONCAPA::map::storemap($map,1);
13741:
13742: if ($errtext) { $fatal=2; }
1.541 raeburn 13743: $outcome .= ($fatal?$errtext:'write ok').$linefeed;
1.444 albertel 13744: }
1.566 albertel 13745:
13746: return (1,$outcome);
1.444 albertel 13747: }
13748:
13749: ############################################################
13750: ############################################################
13751:
1.953 droeschl 13752: #SD
13753: # only Community and Course, or anything else?
1.378 raeburn 13754: sub course_type {
13755: my ($cid) = @_;
13756: if (!defined($cid)) {
13757: $cid = $env{'request.course.id'};
13758: }
1.404 albertel 13759: if (defined($env{'course.'.$cid.'.type'})) {
13760: return $env{'course.'.$cid.'.type'};
1.378 raeburn 13761: } else {
13762: return 'Course';
1.377 raeburn 13763: }
13764: }
1.156 albertel 13765:
1.406 raeburn 13766: sub group_term {
13767: my $crstype = &course_type();
13768: my %names = (
13769: 'Course' => 'group',
1.865 raeburn 13770: 'Community' => 'group',
1.406 raeburn 13771: );
13772: return $names{$crstype};
13773: }
13774:
1.902 raeburn 13775: sub course_types {
13776: my @types = ('official','unofficial','community');
13777: my %typename = (
13778: official => 'Official course',
13779: unofficial => 'Unofficial course',
13780: community => 'Community',
13781: );
13782: return (\@types,\%typename);
13783: }
13784:
1.156 albertel 13785: sub icon {
13786: my ($file)=@_;
1.505 albertel 13787: my $curfext = lc((split(/\./,$file))[-1]);
1.168 albertel 13788: my $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/unknown.gif';
1.156 albertel 13789: my $embstyle = &Apache::loncommon::fileembstyle($curfext);
1.168 albertel 13790: if (!(!defined($embstyle) || $embstyle eq 'unk' || $embstyle eq 'hdn')) {
13791: if (-e $Apache::lonnet::perlvar{'lonDocRoot'}.'/'.
13792: $Apache::lonnet::perlvar{'lonIconsURL'}.'/'.
13793: $curfext.".gif") {
13794: $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/'.
13795: $curfext.".gif";
13796: }
13797: }
1.249 albertel 13798: return &lonhttpdurl($iconname);
1.154 albertel 13799: }
1.84 albertel 13800:
1.575 albertel 13801: sub lonhttpdurl {
1.692 www 13802: #
13803: # Had been used for "small fry" static images on separate port 8080.
13804: # Modify here if lightweight http functionality desired again.
13805: # Currently eliminated due to increasing firewall issues.
13806: #
1.575 albertel 13807: my ($url)=@_;
1.692 www 13808: return $url;
1.215 albertel 13809: }
13810:
1.213 albertel 13811: sub connection_aborted {
13812: my ($r)=@_;
13813: $r->print(" ");$r->rflush();
13814: my $c = $r->connection;
13815: return $c->aborted();
13816: }
13817:
1.221 foxr 13818: # Escapes strings that may have embedded 's that will be put into
1.222 foxr 13819: # strings as 'strings'.
13820: sub escape_single {
1.221 foxr 13821: my ($input) = @_;
1.223 albertel 13822: $input =~ s/\\/\\\\/g; # Escape the \'s..(must be first)>
1.221 foxr 13823: $input =~ s/\'/\\\'/g; # Esacpe the 's....
13824: return $input;
13825: }
1.223 albertel 13826:
1.222 foxr 13827: # Same as escape_single, but escape's "'s This
13828: # can be used for "strings"
13829: sub escape_double {
13830: my ($input) = @_;
13831: $input =~ s/\\/\\\\/g; # Escape the /'s..(must be first)>
13832: $input =~ s/\"/\\\"/g; # Esacpe the "s....
13833: return $input;
13834: }
1.223 albertel 13835:
1.222 foxr 13836: # Escapes the last element of a full URL.
13837: sub escape_url {
13838: my ($url) = @_;
1.238 raeburn 13839: my @urlslices = split(/\//, $url,-1);
1.369 www 13840: my $lastitem = &escape(pop(@urlslices));
1.223 albertel 13841: return join('/',@urlslices).'/'.$lastitem;
1.222 foxr 13842: }
1.462 albertel 13843:
1.820 raeburn 13844: sub compare_arrays {
13845: my ($arrayref1,$arrayref2) = @_;
13846: my (@difference,%count);
13847: @difference = ();
13848: %count = ();
13849: if ((ref($arrayref1) eq 'ARRAY') && (ref($arrayref2) eq 'ARRAY')) {
13850: foreach my $element (@{$arrayref1}, @{$arrayref2}) { $count{$element}++; }
13851: foreach my $element (keys(%count)) {
13852: if ($count{$element} == 1) {
13853: push(@difference,$element);
13854: }
13855: }
13856: }
13857: return @difference;
13858: }
13859:
1.817 bisitz 13860: # -------------------------------------------------------- Initialize user login
1.462 albertel 13861: sub init_user_environment {
1.463 albertel 13862: my ($r, $username, $domain, $authhost, $form, $args) = @_;
1.462 albertel 13863: my $lonids=$Apache::lonnet::perlvar{'lonIDsDir'};
13864:
13865: my $public=($username eq 'public' && $domain eq 'public');
13866:
13867: # See if old ID present, if so, remove
13868:
1.1062 raeburn 13869: my ($filename,$cookie,$userroles,$firstaccenv,$timerintenv);
1.462 albertel 13870: my $now=time;
13871:
13872: if ($public) {
13873: my $max_public=100;
13874: my $oldest;
13875: my $oldest_time=0;
13876: for(my $next=1;$next<=$max_public;$next++) {
13877: if (-e $lonids."/publicuser_$next.id") {
13878: my $mtime=(stat($lonids."/publicuser_$next.id"))[9];
13879: if ($mtime<$oldest_time || !$oldest_time) {
13880: $oldest_time=$mtime;
13881: $oldest=$next;
13882: }
13883: } else {
13884: $cookie="publicuser_$next";
13885: last;
13886: }
13887: }
13888: if (!$cookie) { $cookie="publicuser_$oldest"; }
13889: } else {
1.463 albertel 13890: # if this isn't a robot, kill any existing non-robot sessions
13891: if (!$args->{'robot'}) {
13892: opendir(DIR,$lonids);
13893: while ($filename=readdir(DIR)) {
13894: if ($filename=~/^$username\_\d+\_$domain\_$authhost\.id$/) {
13895: unlink($lonids.'/'.$filename);
13896: }
1.462 albertel 13897: }
1.463 albertel 13898: closedir(DIR);
1.462 albertel 13899: }
13900: # Give them a new cookie
1.463 albertel 13901: my $id = ($args->{'robot'} ? 'robot'.$args->{'robot'}
1.684 www 13902: : $now.$$.int(rand(10000)));
1.463 albertel 13903: $cookie="$username\_$id\_$domain\_$authhost";
1.462 albertel 13904:
13905: # Initialize roles
13906:
1.1062 raeburn 13907: ($userroles,$firstaccenv,$timerintenv) =
13908: &Apache::lonnet::rolesinit($domain,$username,$authhost);
1.462 albertel 13909: }
13910: # ------------------------------------ Check browser type and MathML capability
13911:
13912: my ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,
13913: $clientunicode,$clientos) = &decode_user_agent($r);
13914:
13915: # ------------------------------------------------------------- Get environment
13916:
13917: my %userenv = &Apache::lonnet::dump('environment',$domain,$username);
13918: my ($tmp) = keys(%userenv);
13919: if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
13920: } else {
13921: undef(%userenv);
13922: }
13923: if (($userenv{'interface'}) && (!$form->{'interface'})) {
13924: $form->{'interface'}=$userenv{'interface'};
13925: }
13926: if ($userenv{'texengine'} eq 'ttm') { $clientmathml=1; }
13927:
13928: # --------------- Do not trust query string to be put directly into environment
1.817 bisitz 13929: foreach my $option ('interface','localpath','localres') {
13930: $form->{$option}=~s/[\n\r\=]//gs;
1.462 albertel 13931: }
13932: # --------------------------------------------------------- Write first profile
13933:
13934: {
13935: my %initial_env =
13936: ("user.name" => $username,
13937: "user.domain" => $domain,
13938: "user.home" => $authhost,
13939: "browser.type" => $clientbrowser,
13940: "browser.version" => $clientversion,
13941: "browser.mathml" => $clientmathml,
13942: "browser.unicode" => $clientunicode,
13943: "browser.os" => $clientos,
13944: "server.domain" => $Apache::lonnet::perlvar{'lonDefDomain'},
13945: "request.course.fn" => '',
13946: "request.course.uri" => '',
13947: "request.course.sec" => '',
13948: "request.role" => 'cm',
13949: "request.role.adv" => $env{'user.adv'},
13950: "request.host" => $ENV{'REMOTE_ADDR'},);
13951:
13952: if ($form->{'localpath'}) {
13953: $initial_env{"browser.localpath"} = $form->{'localpath'};
13954: $initial_env{"browser.localres"} = $form->{'localres'};
13955: }
13956:
13957: if ($form->{'interface'}) {
13958: $form->{'interface'}=~s/\W//gs;
13959: $initial_env{"browser.interface"} = $form->{'interface'};
13960: $env{'browser.interface'}=$form->{'interface'};
13961: }
13962:
1.981 raeburn 13963: my %is_adv = ( is_adv => $env{'user.adv'} );
1.1016 raeburn 13964: my %domdef;
13965: unless ($domain eq 'public') {
13966: %domdef = &Apache::lonnet::get_domain_defaults($domain);
13967: }
1.980 raeburn 13968:
1.1081 raeburn 13969: foreach my $tool ('aboutme','blog','webdav','portfolio') {
1.724 raeburn 13970: $userenv{'availabletools.'.$tool} =
1.980 raeburn 13971: &Apache::lonnet::usertools_access($username,$domain,$tool,'reload',
13972: undef,\%userenv,\%domdef,\%is_adv);
1.724 raeburn 13973: }
13974:
1.864 raeburn 13975: foreach my $crstype ('official','unofficial','community') {
1.765 raeburn 13976: $userenv{'canrequest.'.$crstype} =
13977: &Apache::lonnet::usertools_access($username,$domain,$crstype,
1.980 raeburn 13978: 'reload','requestcourses',
13979: \%userenv,\%domdef,\%is_adv);
1.765 raeburn 13980: }
13981:
1.1092 raeburn 13982: $userenv{'canrequest.author'} =
13983: &Apache::lonnet::usertools_access($username,$domain,'requestauthor',
13984: 'reload','requestauthor',
13985: \%userenv,\%domdef,\%is_adv);
13986: my %reqauthor = &Apache::lonnet::get('requestauthor',['author_status','author'],
13987: $domain,$username);
13988: my $reqstatus = $reqauthor{'author_status'};
13989: if ($reqstatus eq 'approval' || $reqstatus eq 'approved') {
13990: if (ref($reqauthor{'author'}) eq 'HASH') {
13991: $userenv{'requestauthorqueued'} = $reqstatus.':'.
13992: $reqauthor{'author'}{'timestamp'};
13993: }
13994: }
13995:
1.462 albertel 13996: $env{'user.environment'} = "$lonids/$cookie.id";
1.1062 raeburn 13997:
1.462 albertel 13998: if (tie(my %disk_env,'GDBM_File',"$lonids/$cookie.id",
13999: &GDBM_WRCREAT(),0640)) {
14000: &_add_to_env(\%disk_env,\%initial_env);
14001: &_add_to_env(\%disk_env,\%userenv,'environment.');
14002: &_add_to_env(\%disk_env,$userroles);
1.1062 raeburn 14003: if (ref($firstaccenv) eq 'HASH') {
14004: &_add_to_env(\%disk_env,$firstaccenv);
14005: }
14006: if (ref($timerintenv) eq 'HASH') {
14007: &_add_to_env(\%disk_env,$timerintenv);
14008: }
1.463 albertel 14009: if (ref($args->{'extra_env'})) {
14010: &_add_to_env(\%disk_env,$args->{'extra_env'});
14011: }
1.462 albertel 14012: untie(%disk_env);
14013: } else {
1.705 tempelho 14014: &Apache::lonnet::logthis("<span style=\"color:blue;\">WARNING: ".
14015: 'Could not create environment storage in lonauth: '.$!.'</span>');
1.462 albertel 14016: return 'error: '.$!;
14017: }
14018: }
14019: $env{'request.role'}='cm';
14020: $env{'request.role.adv'}=$env{'user.adv'};
14021: $env{'browser.type'}=$clientbrowser;
14022:
14023: return $cookie;
14024:
14025: }
14026:
14027: sub _add_to_env {
14028: my ($idf,$env_data,$prefix) = @_;
1.676 raeburn 14029: if (ref($env_data) eq 'HASH') {
14030: while (my ($key,$value) = each(%$env_data)) {
14031: $idf->{$prefix.$key} = $value;
14032: $env{$prefix.$key} = $value;
14033: }
1.462 albertel 14034: }
14035: }
14036:
1.685 tempelho 14037: # --- Get the symbolic name of a problem and the url
14038: sub get_symb {
14039: my ($request,$silent) = @_;
1.726 raeburn 14040: (my $url=$env{'form.url'}) =~ s-^https?\://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
1.685 tempelho 14041: my $symb=($env{'form.symb'} ne '' ? $env{'form.symb'} : (&Apache::lonnet::symbread($url)));
14042: if ($symb eq '') {
14043: if (!$silent) {
1.1071 raeburn 14044: if (ref($request)) {
14045: $request->print("Unable to handle ambiguous references:$url:.");
14046: }
1.685 tempelho 14047: return ();
14048: }
14049: }
14050: &Apache::lonenc::check_decrypt(\$symb);
14051: return ($symb);
14052: }
14053:
14054: # --------------------------------------------------------------Get annotation
14055:
14056: sub get_annotation {
14057: my ($symb,$enc) = @_;
14058:
14059: my $key = $symb;
14060: if (!$enc) {
14061: $key =
14062: &Apache::lonnet::clutter((&Apache::lonnet::decode_symb($symb))[2]);
14063: }
14064: my %annotation=&Apache::lonnet::get('nohist_annotations',[$key]);
14065: return $annotation{$key};
14066: }
14067:
14068: sub clean_symb {
1.731 raeburn 14069: my ($symb,$delete_enc) = @_;
1.685 tempelho 14070:
14071: &Apache::lonenc::check_decrypt(\$symb);
14072: my $enc = $env{'request.enc'};
1.731 raeburn 14073: if ($delete_enc) {
1.730 raeburn 14074: delete($env{'request.enc'});
14075: }
1.685 tempelho 14076:
14077: return ($symb,$enc);
14078: }
1.462 albertel 14079:
1.990 raeburn 14080: sub build_release_hashes {
14081: my ($checkparms,$checkresponsetypes,$checkcrstypes,$anonsurvey,$randomizetry) = @_;
14082: return unless((ref($checkparms) eq 'HASH') && (ref($checkresponsetypes) eq 'HASH') &&
14083: (ref($checkcrstypes) eq 'HASH') && (ref($anonsurvey) eq 'HASH') &&
14084: (ref($randomizetry) eq 'HASH'));
14085: foreach my $key (keys(%Apache::lonnet::needsrelease)) {
14086: my ($item,$name,$value) = split(/:/,$key);
14087: if ($item eq 'parameter') {
14088: if (ref($checkparms->{$name}) eq 'ARRAY') {
14089: unless(grep(/^\Q$name\E$/,@{$checkparms->{$name}})) {
14090: push(@{$checkparms->{$name}},$value);
14091: }
14092: } else {
14093: push(@{$checkparms->{$name}},$value);
14094: }
14095: } elsif ($item eq 'resourcetag') {
14096: if ($name eq 'responsetype') {
14097: $checkresponsetypes->{$value} = $Apache::lonnet::needsrelease{$key}
14098: }
14099: } elsif ($item eq 'course') {
14100: if ($name eq 'crstype') {
14101: $checkcrstypes->{$value} = $Apache::lonnet::needsrelease{$key};
14102: }
14103: }
14104: }
14105: ($anonsurvey->{major},$anonsurvey->{minor}) = split(/\./,$Apache::lonnet::needsrelease{'parameter:type:anonsurvey'});
14106: ($randomizetry->{major},$randomizetry->{minor}) = split(/\./,$Apache::lonnet::needsrelease{'parameter:type:randomizetry'});
14107: return;
14108: }
14109:
1.1083 raeburn 14110: sub update_content_constraints {
14111: my ($cdom,$cnum,$chome,$cid) = @_;
14112: my %curr_reqd_hash = &Apache::lonnet::userenvironment($cdom,$cnum,'internal.releaserequired');
14113: my ($reqdmajor,$reqdminor) = split(/\./,$curr_reqd_hash{'internal.releaserequired'});
14114: my %checkresponsetypes;
14115: foreach my $key (keys(%Apache::lonnet::needsrelease)) {
14116: my ($item,$name,$value) = split(/:/,$key);
14117: if ($item eq 'resourcetag') {
14118: if ($name eq 'responsetype') {
14119: $checkresponsetypes{$value} = $Apache::lonnet::needsrelease{$key}
14120: }
14121: }
14122: }
14123: my $navmap = Apache::lonnavmaps::navmap->new();
14124: if (defined($navmap)) {
14125: my %allresponses;
14126: foreach my $res ($navmap->retrieveResources(undef,sub { $_[0]->is_problem() },1,0)) {
14127: my %responses = $res->responseTypes();
14128: foreach my $key (keys(%responses)) {
14129: next unless(exists($checkresponsetypes{$key}));
14130: $allresponses{$key} += $responses{$key};
14131: }
14132: }
14133: foreach my $key (keys(%allresponses)) {
14134: my ($major,$minor) = split(/\./,$checkresponsetypes{$key});
14135: if (($major > $reqdmajor) || ($major == $reqdmajor && $minor > $reqdminor)) {
14136: ($reqdmajor,$reqdminor) = ($major,$minor);
14137: }
14138: }
14139: undef($navmap);
14140: }
14141: unless (($reqdmajor eq '') && ($reqdminor eq '')) {
14142: &Apache::lonnet::update_released_required($reqdmajor.'.'.$reqdminor,$cdom,$cnum,$chome,$cid);
14143: }
14144: return;
14145: }
14146:
14147: sub parse_supplemental_title {
14148: my ($title) = @_;
14149:
14150: my ($foldertitle,$renametitle);
14151: if ($title =~ /&&&/) {
14152: $title = &HTML::Entites::decode($title);
14153: }
14154: if ($title =~ m/^(\d+)___&&&___($match_username)___&&&___($match_domain)___&&&___(.*)$/) {
14155: $renametitle=$4;
14156: my ($time,$uname,$udom) = ($1,$2,$3);
14157: $foldertitle=&Apache::lontexconvert::msgtexconverted($4);
14158: my $name = &plainname($uname,$udom);
14159: $name = &HTML::Entities::encode($name,'"<>&\'');
14160: $renametitle = &HTML::Entities::encode($renametitle,'"<>&\'');
14161: $title='<i>'.&Apache::lonlocal::locallocaltime($time).'</i> '.
14162: $name.': <br />'.$foldertitle;
14163: }
14164: if (wantarray) {
14165: return ($title,$foldertitle,$renametitle);
14166: }
14167: return $title;
14168: }
14169:
1.1101 raeburn 14170: sub symb_to_docspath {
14171: my ($symb) = @_;
14172: return unless ($symb);
14173: my ($mapurl,$id,$resurl) = &Apache::lonnet::decode_symb($symb);
14174: if ($resurl=~/\.(sequence|page)$/) {
14175: $mapurl=$resurl;
14176: } elsif ($resurl eq 'adm/navmaps') {
14177: $mapurl=$env{'course.'.$env{'request.course.id'}.'.url'};
14178: }
14179: my $mapresobj;
14180: my $navmap = Apache::lonnavmaps::navmap->new();
14181: if (ref($navmap)) {
14182: $mapresobj = $navmap->getResourceByUrl($mapurl);
14183: }
14184: $mapurl=~s{^.*/([^/]+)\.(\w+)$}{$1};
14185: my $type=$2;
14186: my $path;
14187: if (ref($mapresobj)) {
14188: my $pcslist = $mapresobj->map_hierarchy();
14189: if ($pcslist ne '') {
14190: foreach my $pc (split(/,/,$pcslist)) {
14191: next if ($pc <= 1);
14192: my $res = $navmap->getByMapPc($pc);
14193: if (ref($res)) {
14194: my $thisurl = $res->src();
14195: $thisurl=~s{^.*/([^/]+)\.\w+$}{$1};
14196: my $thistitle = $res->title();
14197: $path .= '&'.
14198: &Apache::lonhtmlcommon::entity_encode($thisurl).'&'.
14199: &Apache::lonhtmlcommon::entity_encode($thistitle).
14200: ':'.$res->randompick().
14201: ':'.$res->randomout().
14202: ':'.$res->encrypted().
14203: ':'.$res->randomorder().
14204: ':'.$res->is_page();
14205: }
14206: }
14207: }
14208: $path =~ s/^\&//;
14209: my $maptitle = $mapresobj->title();
14210: if ($mapurl eq 'default') {
14211: $maptitle = 'Main Course Documents';
14212: }
14213: $path .= (($path ne '')? '&' : '').
14214: &Apache::lonhtmlcommon::entity_encode($mapurl).'&'.
14215: &Apache::lonhtmlcommon::entity_encode($maptitle).
14216: ':'.$mapresobj->randompick().
14217: ':'.$mapresobj->randomout().
14218: ':'.$mapresobj->encrypted().
14219: ':'.$mapresobj->randomorder().
14220: ':'.$mapresobj->is_page();
14221: } else {
14222: my $maptitle = &Apache::lonnet::gettitle($mapurl);
14223: my $ispage = (($type eq 'page')? 1 : '');
14224: if ($mapurl eq 'default') {
14225: $maptitle = 'Main Course Documents';
14226: }
14227: $path = &Apache::lonhtmlcommon::entity_encode($mapurl).'&'.
14228: &Apache::lonhtmlcommon::entity_encode($maptitle).':::::'.$ispage;
14229: }
14230: unless ($mapurl eq 'default') {
14231: $path = 'default&'.
14232: &Apache::lonhtmlcommon::entity_encode('Main Course Documents').
14233: ':::::&'.$path;
14234: }
14235: return $path;
14236: }
14237:
1.1094 raeburn 14238: sub captcha_display {
14239: my ($context,$lonhost) = @_;
14240: my ($output,$error);
14241: my ($captcha,$pubkey,$privkey) = &get_captcha_config($context,$lonhost);
1.1095 raeburn 14242: if ($captcha eq 'original') {
1.1094 raeburn 14243: $output = &create_captcha();
14244: unless ($output) {
14245: $error = 'captcha';
14246: }
14247: } elsif ($captcha eq 'recaptcha') {
14248: $output = &create_recaptcha($pubkey);
14249: unless ($output) {
1.1095 raeburn 14250: $error = 'recaptcha';
1.1094 raeburn 14251: }
14252: }
14253: return ($output,$error);
14254: }
14255:
14256: sub captcha_response {
14257: my ($context,$lonhost) = @_;
14258: my ($captcha_chk,$captcha_error);
14259: my ($captcha,$pubkey,$privkey) = &get_captcha_config($context,$lonhost);
1.1095 raeburn 14260: if ($captcha eq 'original') {
1.1094 raeburn 14261: ($captcha_chk,$captcha_error) = &check_captcha();
14262: } elsif ($captcha eq 'recaptcha') {
14263: $captcha_chk = &check_recaptcha($privkey);
14264: } else {
14265: $captcha_chk = 1;
14266: }
14267: return ($captcha_chk,$captcha_error);
14268: }
14269:
14270: sub get_captcha_config {
14271: my ($context,$lonhost) = @_;
1.1095 raeburn 14272: my ($captcha,$pubkey,$privkey,$hashtocheck);
1.1094 raeburn 14273: my $hostname = &Apache::lonnet::hostname($lonhost);
14274: my $serverhomeID = &Apache::lonnet::get_server_homeID($hostname);
14275: my $serverhomedom = &Apache::lonnet::host_domain($serverhomeID);
1.1095 raeburn 14276: if ($context eq 'usercreation') {
14277: my %domconfig = &Apache::lonnet::get_dom('configuration',[$context],$serverhomedom);
14278: if (ref($domconfig{$context}) eq 'HASH') {
14279: $hashtocheck = $domconfig{$context}{'cancreate'};
14280: if (ref($hashtocheck) eq 'HASH') {
14281: if ($hashtocheck->{'captcha'} eq 'recaptcha') {
14282: if (ref($hashtocheck->{'recaptchakeys'}) eq 'HASH') {
14283: $pubkey = $hashtocheck->{'recaptchakeys'}{'public'};
14284: $privkey = $hashtocheck->{'recaptchakeys'}{'private'};
14285: }
14286: if ($privkey && $pubkey) {
14287: $captcha = 'recaptcha';
14288: } else {
14289: $captcha = 'original';
14290: }
14291: } elsif ($hashtocheck->{'captcha'} ne 'notused') {
14292: $captcha = 'original';
14293: }
1.1094 raeburn 14294: }
1.1095 raeburn 14295: } else {
14296: $captcha = 'captcha';
14297: }
14298: } elsif ($context eq 'login') {
14299: my %domconfhash = &Apache::loncommon::get_domainconf($serverhomedom);
14300: if ($domconfhash{$serverhomedom.'.login.captcha'} eq 'recaptcha') {
14301: $pubkey = $domconfhash{$serverhomedom.'.login.recaptchakeys_public'};
14302: $privkey = $domconfhash{$serverhomedom.'.login.recaptchakeys_private'};
1.1094 raeburn 14303: if ($privkey && $pubkey) {
14304: $captcha = 'recaptcha';
1.1095 raeburn 14305: } else {
14306: $captcha = 'original';
1.1094 raeburn 14307: }
1.1095 raeburn 14308: } elsif ($domconfhash{$serverhomedom.'.login.captcha'} eq 'original') {
14309: $captcha = 'original';
1.1094 raeburn 14310: }
14311: }
14312: return ($captcha,$pubkey,$privkey);
14313: }
14314:
14315: sub create_captcha {
14316: my %captcha_params = &captcha_settings();
14317: my ($output,$maxtries,$tries) = ('',10,0);
14318: while ($tries < $maxtries) {
14319: $tries ++;
14320: my $captcha = Authen::Captcha->new (
14321: output_folder => $captcha_params{'output_dir'},
14322: data_folder => $captcha_params{'db_dir'},
14323: );
14324: my $md5sum = $captcha->generate_code($captcha_params{'numchars'});
14325:
14326: if (-e $Apache::lonnet::perlvar{'lonCaptchaDir'}.'/'.$md5sum.'.png') {
14327: $output = '<input type="hidden" name="crypt" value="'.$md5sum.'" />'."\n".
14328: &mt('Type in the letters/numbers shown below').' '.
14329: '<input type="text" size="5" name="code" value="" /><br />'.
14330: '<img src="'.$captcha_params{'www_output_dir'}.'/'.$md5sum.'.png" />';
14331: last;
14332: }
14333: }
14334: return $output;
14335: }
14336:
14337: sub captcha_settings {
14338: my %captcha_params = (
14339: output_dir => $Apache::lonnet::perlvar{'lonCaptchaDir'},
14340: www_output_dir => "/captchaspool",
14341: db_dir => $Apache::lonnet::perlvar{'lonCaptchaDb'},
14342: numchars => '5',
14343: );
14344: return %captcha_params;
14345: }
14346:
14347: sub check_captcha {
14348: my ($captcha_chk,$captcha_error);
14349: my $code = $env{'form.code'};
14350: my $md5sum = $env{'form.crypt'};
14351: my %captcha_params = &captcha_settings();
14352: my $captcha = Authen::Captcha->new(
14353: output_folder => $captcha_params{'output_dir'},
14354: data_folder => $captcha_params{'db_dir'},
14355: );
1.1109 ! raeburn 14356: $captcha_chk = $captcha->check_code($code,$md5sum);
1.1094 raeburn 14357: my %captcha_hash = (
14358: 0 => 'Code not checked (file error)',
14359: -1 => 'Failed: code expired',
14360: -2 => 'Failed: invalid code (not in database)',
14361: -3 => 'Failed: invalid code (code does not match crypt)',
14362: );
14363: if ($captcha_chk != 1) {
14364: $captcha_error = $captcha_hash{$captcha_chk}
14365: }
14366: return ($captcha_chk,$captcha_error);
14367: }
14368:
14369: sub create_recaptcha {
14370: my ($pubkey) = @_;
14371: my $captcha = Captcha::reCAPTCHA->new;
14372: return $captcha->get_options_setter({theme => 'white'})."\n".
14373: $captcha->get_html($pubkey).
14374: &mt('If either word is hard to read, [_1] will replace them.',
14375: '<image src="/res/adm/pages/refresh.gif" alt="reCAPTCHA refresh" />').
14376: '<br /><br />';
14377: }
14378:
14379: sub check_recaptcha {
14380: my ($privkey) = @_;
14381: my $captcha_chk;
14382: my $captcha = Captcha::reCAPTCHA->new;
14383: my $captcha_result =
14384: $captcha->check_answer(
14385: $privkey,
14386: $ENV{'REMOTE_ADDR'},
14387: $env{'form.recaptcha_challenge_field'},
14388: $env{'form.recaptcha_response_field'},
14389: );
14390: if ($captcha_result->{is_valid}) {
14391: $captcha_chk = 1;
14392: }
14393: return $captcha_chk;
14394: }
14395:
1.41 ng 14396: =pod
14397:
14398: =back
14399:
1.112 bowersj2 14400: =cut
1.41 ng 14401:
1.112 bowersj2 14402: 1;
14403: __END__;
1.41 ng 14404:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>