Annotation of loncom/interface/loncommon.pm, revision 1.1471
1.10 albertel 1: # The LearningOnline Network with CAPA
1.1 albertel 2: # a pile of common routines
1.10 albertel 3: #
1.1471 ! raeburn 4: # $Id: loncommon.pm,v 1.1470 2025/03/17 00:25:51 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.1383 raeburn 64: use Apache::lonnavmaps();
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.1110 raeburn 71: use Apache::lonuserstate();
1.1182 raeburn 72: use Apache::courseclassifier();
1.479 albertel 73: use LONCAPA qw(:DEFAULT :match);
1.1409 raeburn 74: use LONCAPA::ltiutils;
1.1280 raeburn 75: use LONCAPA::LWPReq;
1.1395 raeburn 76: use LONCAPA::map();
1.1328 raeburn 77: use HTTP::Request;
1.657 raeburn 78: use DateTime::TimeZone;
1.1241 raeburn 79: use DateTime::Locale;
1.1220 raeburn 80: use Encode();
1.1091 foxr 81: use Text::Aspell;
1.1094 raeburn 82: use Authen::Captcha;
83: use Captcha::reCAPTCHA;
1.1234 raeburn 84: use JSON::DWIW;
1.1174 raeburn 85: use Crypt::DES;
86: use DynaLoader; # for Crypt::DES version
1.1223 musolffc 87: use MIME::Lite;
88: use MIME::Types;
1.1292 raeburn 89: use File::Copy();
1.1300 raeburn 90: use File::Path();
1.1309 raeburn 91: use String::CRC32();
92: use Short::URL();
1.117 www 93:
1.517 raeburn 94: # ---------------------------------------------- Designs
95: use vars qw(%defaultdesign);
96:
1.22 www 97: my $readit;
98:
1.517 raeburn 99:
1.157 matthew 100: ##
101: ## Global Variables
102: ##
1.46 matthew 103:
1.643 foxr 104:
105: # ----------------------------------------------- SSI with retries:
106: #
107:
108: =pod
109:
1.648 raeburn 110: =head1 Server Side include with retries:
1.643 foxr 111:
112: =over 4
113:
1.648 raeburn 114: =item * &ssi_with_retries(resource,retries form)
1.643 foxr 115:
116: Performs an ssi with some number of retries. Retries continue either
117: until the result is ok or until the retry count supplied by the
118: caller is exhausted.
119:
120: Inputs:
1.648 raeburn 121:
122: =over 4
123:
1.643 foxr 124: resource - Identifies the resource to insert.
1.648 raeburn 125:
1.643 foxr 126: retries - Count of the number of retries allowed.
1.648 raeburn 127:
1.643 foxr 128: form - Hash that identifies the rendering options.
129:
1.648 raeburn 130: =back
131:
132: Returns:
133:
134: =over 4
135:
1.643 foxr 136: content - The content of the response. If retries were exhausted this is empty.
1.648 raeburn 137:
1.643 foxr 138: response - The response from the last attempt (which may or may not have been successful.
139:
1.648 raeburn 140: =back
141:
142: =back
143:
1.643 foxr 144: =cut
145:
146: sub ssi_with_retries {
147: my ($resource, $retries, %form) = @_;
148:
149:
150: my $ok = 0; # True if we got a good response.
151: my $content;
152: my $response;
153:
154: # Try to get the ssi done. within the retries count:
155:
156: do {
157: ($content, $response) = &Apache::lonnet::ssi($resource, %form);
158: $ok = $response->is_success;
1.650 www 159: if (!$ok) {
160: &Apache::lonnet::logthis("Failed ssi_with_retries on $resource: ".$response->is_success.', '.$response->code.', '.$response->message);
161: }
1.643 foxr 162: $retries--;
163: } while (!$ok && ($retries > 0));
164:
165: if (!$ok) {
166: $content = ''; # On error return an empty content.
167: }
168: return ($content, $response);
169:
170: }
171:
172:
173:
1.20 www 174: # ----------------------------------------------- Filetypes/Languages/Copyright
1.12 harris41 175: my %language;
1.124 www 176: my %supported_language;
1.1088 foxr 177: my %supported_codes;
1.1048 foxr 178: my %latex_language; # For choosing hyphenation in <transl..>
179: my %latex_language_bykey; # for choosing hyphenation from metadata
1.12 harris41 180: my %cprtag;
1.192 taceyjo1 181: my %scprtag;
1.351 www 182: my %fe; my %fd; my %fm;
1.41 ng 183: my %category_extensions;
1.12 harris41 184:
1.46 matthew 185: # ---------------------------------------------- Thesaurus variables
1.144 matthew 186: #
187: # %Keywords:
188: # A hash used by &keyword to determine if a word is considered a keyword.
189: # $thesaurus_db_file
190: # Scalar containing the full path to the thesaurus database.
1.46 matthew 191:
192: my %Keywords;
193: my $thesaurus_db_file;
194:
1.144 matthew 195: #
196: # Initialize values from language.tab, copyright.tab, filetypes.tab,
197: # thesaurus.tab, and filecategories.tab.
198: #
1.18 www 199: BEGIN {
1.46 matthew 200: # Variable initialization
201: $thesaurus_db_file = $Apache::lonnet::perlvar{'lonTabDir'}."/thesaurus.db";
202: #
1.22 www 203: unless ($readit) {
1.12 harris41 204: # ------------------------------------------------------------------- languages
205: {
1.158 raeburn 206: my $langtabfile = $Apache::lonnet::perlvar{'lonTabDir'}.
207: '/language.tab';
1.1317 raeburn 208: if ( open(my $fh,'<',$langtabfile) ) {
1.356 albertel 209: while (my $line = <$fh>) {
210: next if ($line=~/^\#/);
211: chomp($line);
1.1088 foxr 212: my ($key,$code,$country,$three,$enc,$val,$sup,$latex)=(split(/\t/,$line));
1.158 raeburn 213: $language{$key}=$val.' - '.$enc;
214: if ($sup) {
215: $supported_language{$key}=$sup;
1.1088 foxr 216: $supported_codes{$key} = $code;
1.158 raeburn 217: }
1.1048 foxr 218: if ($latex) {
219: $latex_language_bykey{$key} = $latex;
1.1088 foxr 220: $latex_language{$code} = $latex;
1.1048 foxr 221: }
1.158 raeburn 222: }
223: close($fh);
224: }
1.12 harris41 225: }
226: # ------------------------------------------------------------------ copyrights
227: {
1.158 raeburn 228: my $copyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}.
229: '/copyright.tab';
1.1317 raeburn 230: if ( open (my $fh,'<',$copyrightfile) ) {
1.356 albertel 231: while (my $line = <$fh>) {
232: next if ($line=~/^\#/);
233: chomp($line);
234: my ($key,$val)=(split(/\s+/,$line,2));
1.158 raeburn 235: $cprtag{$key}=$val;
236: }
237: close($fh);
238: }
1.12 harris41 239: }
1.351 www 240: # ----------------------------------------------------------- source copyrights
1.192 taceyjo1 241: {
242: my $sourcecopyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}.
243: '/source_copyright.tab';
1.1317 raeburn 244: if ( open (my $fh,'<',$sourcecopyrightfile) ) {
1.356 albertel 245: while (my $line = <$fh>) {
246: next if ($line =~ /^\#/);
247: chomp($line);
248: my ($key,$val)=(split(/\s+/,$line,2));
1.192 taceyjo1 249: $scprtag{$key}=$val;
250: }
251: close($fh);
252: }
253: }
1.63 www 254:
1.517 raeburn 255: # -------------------------------------------------------------- default domain designs
1.63 www 256: my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors';
1.517 raeburn 257: my $designfile = $designdir.'/default.tab';
1.1317 raeburn 258: if ( open (my $fh,'<',$designfile) ) {
1.517 raeburn 259: while (my $line = <$fh>) {
260: next if ($line =~ /^\#/);
261: chomp($line);
262: my ($key,$val)=(split(/\=/,$line));
263: if ($val) { $defaultdesign{$key}=$val; }
264: }
265: close($fh);
1.63 www 266: }
267:
1.15 harris41 268: # ------------------------------------------------------------- file categories
269: {
1.158 raeburn 270: my $categoryfile = $Apache::lonnet::perlvar{'lonTabDir'}.
271: '/filecategories.tab';
1.1317 raeburn 272: if ( open (my $fh,'<',$categoryfile) ) {
1.356 albertel 273: while (my $line = <$fh>) {
274: next if ($line =~ /^\#/);
275: chomp($line);
276: my ($extension,$category)=(split(/\s+/,$line,2));
1.1263 raeburn 277: push(@{$category_extensions{lc($category)}},$extension);
1.158 raeburn 278: }
279: close($fh);
280: }
281:
1.15 harris41 282: }
1.12 harris41 283: # ------------------------------------------------------------------ file types
284: {
1.158 raeburn 285: my $typesfile = $Apache::lonnet::perlvar{'lonTabDir'}.
286: '/filetypes.tab';
1.1317 raeburn 287: if ( open (my $fh,'<',$typesfile) ) {
1.356 albertel 288: while (my $line = <$fh>) {
289: next if ($line =~ /^\#/);
290: chomp($line);
291: my ($ending,$emb,$mime,$descr)=split(/\s+/,$line,4);
1.158 raeburn 292: if ($descr ne '') {
293: $fe{$ending}=lc($emb);
294: $fd{$ending}=$descr;
1.351 www 295: if ($mime ne 'unk') { $fm{$ending}=$mime; }
1.158 raeburn 296: }
297: }
298: close($fh);
299: }
1.12 harris41 300: }
1.22 www 301: &Apache::lonnet::logthis(
1.705 tempelho 302: "<span style='color:yellow;'>INFO: Read file types</span>");
1.22 www 303: $readit=1;
1.46 matthew 304: } # end of unless($readit)
1.32 matthew 305:
306: }
1.112 bowersj2 307:
1.42 matthew 308: ###############################################################
309: ## HTML and Javascript Helper Functions ##
310: ###############################################################
311:
312: =pod
313:
1.112 bowersj2 314: =head1 HTML and Javascript Functions
1.42 matthew 315:
1.112 bowersj2 316: =over 4
317:
1.648 raeburn 318: =item * &browser_and_searcher_javascript()
1.112 bowersj2 319:
320: X<browsing, javascript>X<searching, javascript>Returns a string
321: containing javascript with two functions, C<openbrowser> and
322: C<opensearcher>. Returned string does not contain E<lt>scriptE<gt>
323: tags.
1.42 matthew 324:
1.648 raeburn 325: =item * &openbrowser(formname,elementname,only,omit) [javascript]
1.42 matthew 326:
327: inputs: formname, elementname, only, omit
328:
329: formname and elementname indicate the name of the html form and name of
330: the element that the results of the browsing selection are to be placed in.
331:
332: Specifying 'only' will restrict the browser to displaying only files
1.185 www 333: with the given extension. Can be a comma separated list.
1.42 matthew 334:
335: Specifying 'omit' will restrict the browser to NOT displaying files
1.185 www 336: with the given extension. Can be a comma separated list.
1.42 matthew 337:
1.648 raeburn 338: =item * &opensearcher(formname,elementname) [javascript]
1.42 matthew 339:
340: Inputs: formname, elementname
341:
342: formname and elementname specify the name of the html form and the name
343: of the element the selection from the search results will be placed in.
1.542 raeburn 344:
1.42 matthew 345: =cut
346:
347: sub browser_and_searcher_javascript {
1.199 albertel 348: my ($mode)=@_;
349: if (!defined($mode)) { $mode='edit'; }
1.453 albertel 350: my $resurl=&escape_single(&lastresurl());
1.42 matthew 351: return <<END;
1.219 albertel 352: // <!-- BEGIN LON-CAPA Internal
1.50 matthew 353: var editbrowser = null;
1.135 albertel 354: function openbrowser(formname,elementname,only,omit,titleelement) {
1.170 www 355: var url = '$resurl/?';
1.42 matthew 356: if (editbrowser == null) {
357: url += 'launch=1&';
358: }
359: url += 'catalogmode=interactive&';
1.199 albertel 360: url += 'mode=$mode&';
1.611 albertel 361: url += 'inhibitmenu=yes&';
1.42 matthew 362: url += 'form=' + formname + '&';
363: if (only != null) {
364: url += 'only=' + only + '&';
1.217 albertel 365: } else {
366: url += 'only=&';
367: }
1.42 matthew 368: if (omit != null) {
369: url += 'omit=' + omit + '&';
1.217 albertel 370: } else {
371: url += 'omit=&';
372: }
1.135 albertel 373: if (titleelement != null) {
374: url += 'titleelement=' + titleelement + '&';
1.217 albertel 375: } else {
376: url += 'titleelement=&';
377: }
1.42 matthew 378: url += 'element=' + elementname + '';
379: var title = 'Browser';
1.435 albertel 380: var options = 'scrollbars=1,resizable=1,menubar=0,toolbar=1,location=1';
1.42 matthew 381: options += ',width=700,height=600';
382: editbrowser = open(url,title,options,'1');
383: editbrowser.focus();
384: }
385: var editsearcher;
1.135 albertel 386: function opensearcher(formname,elementname,titleelement) {
1.42 matthew 387: var url = '/adm/searchcat?';
388: if (editsearcher == null) {
389: url += 'launch=1&';
390: }
391: url += 'catalogmode=interactive&';
1.199 albertel 392: url += 'mode=$mode&';
1.42 matthew 393: url += 'form=' + formname + '&';
1.135 albertel 394: if (titleelement != null) {
395: url += 'titleelement=' + titleelement + '&';
1.217 albertel 396: } else {
397: url += 'titleelement=&';
398: }
1.42 matthew 399: url += 'element=' + elementname + '';
400: var title = 'Search';
1.435 albertel 401: var options = 'scrollbars=1,resizable=1,menubar=0,toolbar=1,location=1';
1.42 matthew 402: options += ',width=700,height=600';
403: editsearcher = open(url,title,options,'1');
404: editsearcher.focus();
405: }
1.219 albertel 406: // END LON-CAPA Internal -->
1.42 matthew 407: END
1.170 www 408: }
409:
410: sub lastresurl {
1.258 albertel 411: if ($env{'environment.lastresurl'}) {
412: return $env{'environment.lastresurl'}
1.170 www 413: } else {
414: return '/res';
415: }
416: }
417:
418: sub storeresurl {
419: my $resurl=&Apache::lonnet::clutter(shift);
420: unless ($resurl=~/^\/res/) { return 0; }
421: $resurl=~s/\/$//;
422: &Apache::lonnet::put('environment',{'lastresurl' => $resurl});
1.646 raeburn 423: &Apache::lonnet::appenv({'environment.lastresurl' => $resurl});
1.170 www 424: return 1;
1.42 matthew 425: }
426:
1.74 www 427: sub studentbrowser_javascript {
1.111 www 428: unless (
1.258 albertel 429: (($env{'request.course.id'}) &&
1.302 albertel 430: (&Apache::lonnet::allowed('srm',$env{'request.course.id'})
431: || &Apache::lonnet::allowed('srm',$env{'request.course.id'}.
432: '/'.$env{'request.course.sec'})
433: ))
1.258 albertel 434: || ($env{'request.role'}=~/^(au|dc|su)/)
1.111 www 435: ) { return ''; }
1.74 www 436: return (<<'ENDSTDBRW');
1.776 bisitz 437: <script type="text/javascript" language="Javascript">
1.824 bisitz 438: // <![CDATA[
1.74 www 439: var stdeditbrowser;
1.1413 raeburn 440: function openstdbrowser(formname,uname,udom,clicker,roleflag,ignorefilter,courseadv,uident) {
1.74 www 441: var url = '/adm/pickstudent?';
442: var filter;
1.558 albertel 443: if (!ignorefilter) {
444: eval('filter=document.'+formname+'.'+uname+'.value;');
445: }
1.74 www 446: if (filter != null) {
447: if (filter != '') {
448: url += 'filter='+filter+'&';
449: }
450: }
451: url += 'form=' + formname + '&unameelement='+uname+
1.999 www 452: '&udomelement='+udom+
453: '&clicker='+clicker;
1.111 www 454: if (roleflag) { url+="&roles=1"; }
1.1337 raeburn 455: if (courseadv == 'condition') {
456: if (document.getElementById('courseadv')) {
457: courseadv = document.getElementById('courseadv').value;
458: }
459: }
460: if ((courseadv == 'only') || (courseadv == 'none')) { url+="&courseadv="+courseadv; }
1.1413 raeburn 461: if (uident !== '') { url+="&identelement="+uident; }
1.102 www 462: var title = 'Student_Browser';
1.74 www 463: var options = 'scrollbars=1,resizable=1,menubar=0';
464: options += ',width=700,height=600';
465: stdeditbrowser = open(url,title,options,'1');
466: stdeditbrowser.focus();
467: }
1.824 bisitz 468: // ]]>
1.74 www 469: </script>
470: ENDSTDBRW
471: }
1.42 matthew 472:
1.1003 www 473: sub resourcebrowser_javascript {
474: unless ($env{'request.course.id'}) { return ''; }
1.1004 www 475: return (<<'ENDRESBRW');
1.1003 www 476: <script type="text/javascript" language="Javascript">
477: // <![CDATA[
478: var reseditbrowser;
1.1004 www 479: function openresbrowser(formname,reslink) {
1.1005 www 480: var url = '/adm/pickresource?form='+formname+'&reslink='+reslink;
1.1003 www 481: var title = 'Resource_Browser';
482: var options = 'scrollbars=1,resizable=1,menubar=0';
1.1005 www 483: options += ',width=700,height=500';
1.1004 www 484: reseditbrowser = open(url,title,options,'1');
485: reseditbrowser.focus();
1.1003 www 486: }
487: // ]]>
488: </script>
1.1004 www 489: ENDRESBRW
1.1003 www 490: }
491:
1.74 www 492: sub selectstudent_link {
1.1413 raeburn 493: my ($form,$unameele,$udomele,$courseadv,$clickerid,$identelem)=@_;
1.999 www 494: my $callargs = "'".&Apache::lonhtmlcommon::entity_encode($form)."','".
495: &Apache::lonhtmlcommon::entity_encode($unameele)."','".
496: &Apache::lonhtmlcommon::entity_encode($udomele)."'";
1.258 albertel 497: if ($env{'request.course.id'}) {
1.302 albertel 498: if (!&Apache::lonnet::allowed('srm',$env{'request.course.id'})
499: && !&Apache::lonnet::allowed('srm',$env{'request.course.id'}.
500: '/'.$env{'request.course.sec'})) {
1.111 www 501: return '';
502: }
1.999 www 503: $callargs.=",'".&Apache::lonhtmlcommon::entity_encode($clickerid)."'";
1.1337 raeburn 504: if ($courseadv eq 'only') {
505: $callargs .= ",'',1,'$courseadv'";
506: } elsif ($courseadv eq 'none') {
507: $callargs .= ",'','','$courseadv'";
508: } elsif ($courseadv eq 'condition') {
509: $callargs .= ",'','','$courseadv'";
1.1413 raeburn 510: } elsif ($identelem ne '') {
511: $callargs .= ",'','',''";
512: }
513: if ($identelem ne '') {
514: $callargs .= ",'".&Apache::lonhtmlcommon::entity_encode($identelem)."'";
1.793 raeburn 515: }
516: return '<span class="LC_nobreak">'.
517: '<a href="javascript:openstdbrowser('.$callargs.');">'.
518: &mt('Select User').'</a></span>';
1.74 www 519: }
1.258 albertel 520: if ($env{'request.role'}=~/^(au|dc|su)/) {
1.1012 www 521: $callargs .= ",'',1";
1.793 raeburn 522: return '<span class="LC_nobreak">'.
523: '<a href="javascript:openstdbrowser('.$callargs.');">'.
524: &mt('Select User').'</a></span>';
1.111 www 525: }
526: return '';
1.91 www 527: }
528:
1.1004 www 529: sub selectresource_link {
530: my ($form,$reslink,$arg)=@_;
531:
532: my $callargs = "'".&Apache::lonhtmlcommon::entity_encode($form)."','".
533: &Apache::lonhtmlcommon::entity_encode($reslink)."'";
534: unless ($env{'request.course.id'}) { return $arg; }
535: return '<span class="LC_nobreak">'.
536: '<a href="javascript:openresbrowser('.$callargs.');">'.
537: $arg.'</a></span>';
538: }
539:
540:
541:
1.653 raeburn 542: sub authorbrowser_javascript {
543: return <<"ENDAUTHORBRW";
1.776 bisitz 544: <script type="text/javascript" language="JavaScript">
1.824 bisitz 545: // <![CDATA[
1.653 raeburn 546: var stdeditbrowser;
547:
548: function openauthorbrowser(formname,udom) {
549: var url = '/adm/pickauthor?';
550: url += 'form='+formname+'&roledom='+udom;
551: var title = 'Author_Browser';
552: var options = 'scrollbars=1,resizable=1,menubar=0';
553: options += ',width=700,height=600';
554: stdeditbrowser = open(url,title,options,'1');
555: stdeditbrowser.focus();
556: }
557:
1.824 bisitz 558: // ]]>
1.653 raeburn 559: </script>
560: ENDAUTHORBRW
561: }
562:
1.91 www 563: sub coursebrowser_javascript {
1.1116 raeburn 564: my ($domainfilter,$sec_element,$formname,$role_element,$crstype,
1.1221 raeburn 565: $credits_element,$instcode) = @_;
1.932 raeburn 566: my $wintitle = 'Course_Browser';
1.931 raeburn 567: if ($crstype eq 'Community') {
1.932 raeburn 568: $wintitle = 'Community_Browser';
1.909 raeburn 569: }
1.876 raeburn 570: my $id_functions = &javascript_index_functions();
571: my $output = '
1.776 bisitz 572: <script type="text/javascript" language="JavaScript">
1.824 bisitz 573: // <![CDATA[
1.468 raeburn 574: var stdeditbrowser;'."\n";
1.876 raeburn 575:
576: $output .= <<"ENDSTDBRW";
1.909 raeburn 577: function opencrsbrowser(formname,uname,udom,desc,extra_element,multflag,type,type_elem) {
1.91 www 578: var url = '/adm/pickcourse?';
1.895 raeburn 579: var formid = getFormIdByName(formname);
1.876 raeburn 580: var domainfilter = getDomainFromSelectbox(formname,udom);
1.128 albertel 581: if (domainfilter != null) {
582: if (domainfilter != '') {
583: url += 'domainfilter='+domainfilter+'&';
584: }
585: }
1.91 www 586: url += 'form=' + formname + '&cnumelement='+uname+
1.187 albertel 587: '&cdomelement='+udom+
588: '&cnameelement='+desc;
1.468 raeburn 589: if (extra_element !=null && extra_element != '') {
1.594 raeburn 590: if (formname == 'rolechoice' || formname == 'studentform') {
1.468 raeburn 591: url += '&roleelement='+extra_element;
592: if (domainfilter == null || domainfilter == '') {
593: url += '&domainfilter='+extra_element;
594: }
1.234 raeburn 595: }
1.468 raeburn 596: else {
597: if (formname == 'portform') {
598: url += '&setroles='+extra_element;
1.800 raeburn 599: } else {
600: if (formname == 'rules') {
601: url += '&fixeddom='+extra_element;
602: }
1.468 raeburn 603: }
604: }
1.230 raeburn 605: }
1.909 raeburn 606: if (type != null && type != '') {
607: url += '&type='+type;
608: }
609: if (type_elem != null && type_elem != '') {
610: url += '&typeelement='+type_elem;
611: }
1.872 raeburn 612: if (formname == 'ccrs') {
613: var ownername = document.forms[formid].ccuname.value;
614: var ownerdom = document.forms[formid].ccdomain.options[document.forms[formid].ccdomain.selectedIndex].value;
1.1238 raeburn 615: url += '&cloner='+ownername+':'+ownerdom;
616: if (type == 'Course') {
617: url += '&crscode='+document.forms[formid].crscode.value;
618: }
1.1221 raeburn 619: }
620: if (formname == 'requestcrs') {
621: url += '&crsdom=$domainfilter&crscode=$instcode';
1.872 raeburn 622: }
1.293 raeburn 623: if (multflag !=null && multflag != '') {
624: url += '&multiple='+multflag;
625: }
1.909 raeburn 626: var title = '$wintitle';
1.91 www 627: var options = 'scrollbars=1,resizable=1,menubar=0';
628: options += ',width=700,height=600';
629: stdeditbrowser = open(url,title,options,'1');
630: stdeditbrowser.focus();
631: }
1.876 raeburn 632: $id_functions
633: ENDSTDBRW
1.1116 raeburn 634: if (($sec_element ne '') || ($role_element ne '') || ($credits_element ne '')) {
635: $output .= &setsec_javascript($sec_element,$formname,$role_element,
636: $credits_element);
1.876 raeburn 637: }
638: $output .= '
639: // ]]>
640: </script>';
641: return $output;
642: }
643:
644: sub javascript_index_functions {
645: return <<"ENDJS";
646:
647: function getFormIdByName(formname) {
648: for (var i=0;i<document.forms.length;i++) {
649: if (document.forms[i].name == formname) {
650: return i;
651: }
652: }
653: return -1;
654: }
655:
656: function getIndexByName(formid,item) {
657: for (var i=0;i<document.forms[formid].elements.length;i++) {
658: if (document.forms[formid].elements[i].name == item) {
659: return i;
660: }
661: }
662: return -1;
663: }
1.468 raeburn 664:
1.876 raeburn 665: function getDomainFromSelectbox(formname,udom) {
666: var userdom;
667: var formid = getFormIdByName(formname);
668: if (formid > -1) {
669: var domid = getIndexByName(formid,udom);
670: if (domid > -1) {
671: if (document.forms[formid].elements[domid].type == 'select-one') {
672: userdom=document.forms[formid].elements[domid].options[document.forms[formid].elements[domid].selectedIndex].value;
673: }
674: if (document.forms[formid].elements[domid].type == 'hidden') {
675: userdom=document.forms[formid].elements[domid].value;
1.468 raeburn 676: }
677: }
678: }
1.876 raeburn 679: return userdom;
680: }
681:
682: ENDJS
1.468 raeburn 683:
1.876 raeburn 684: }
685:
1.1017 raeburn 686: sub javascript_array_indexof {
1.1018 raeburn 687: return <<ENDJS;
1.1017 raeburn 688: <script type="text/javascript" language="JavaScript">
689: // <![CDATA[
690:
691: if (!Array.prototype.indexOf) {
692: Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
693: "use strict";
694: if (this === void 0 || this === null) {
695: throw new TypeError();
696: }
697: var t = Object(this);
698: var len = t.length >>> 0;
699: if (len === 0) {
700: return -1;
701: }
702: var n = 0;
703: if (arguments.length > 0) {
704: n = Number(arguments[1]);
1.1088 foxr 705: if (n !== n) { // shortcut for verifying if it is NaN
1.1017 raeburn 706: n = 0;
707: } else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
708: n = (n > 0 || -1) * Math.floor(Math.abs(n));
709: }
710: }
711: if (n >= len) {
712: return -1;
713: }
714: var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
715: for (; k < len; k++) {
716: if (k in t && t[k] === searchElement) {
717: return k;
718: }
719: }
720: return -1;
721: }
722: }
723:
724: // ]]>
725: </script>
726:
727: ENDJS
728:
729: }
730:
1.876 raeburn 731: sub userbrowser_javascript {
732: my $id_functions = &javascript_index_functions();
733: return <<"ENDUSERBRW";
734:
1.888 raeburn 735: function openuserbrowser(formname,uname,udom,ulast,ufirst,uemail,hideudom,crsdom,caller) {
1.876 raeburn 736: var url = '/adm/pickuser?';
737: var userdom = getDomainFromSelectbox(formname,udom);
738: if (userdom != null) {
739: if (userdom != '') {
740: url += 'srchdom='+userdom+'&';
741: }
742: }
743: url += 'form=' + formname + '&unameelement='+uname+
744: '&udomelement='+udom+
745: '&ulastelement='+ulast+
746: '&ufirstelement='+ufirst+
747: '&uemailelement='+uemail+
1.881 raeburn 748: '&hideudomelement='+hideudom+
749: '&coursedom='+crsdom;
1.888 raeburn 750: if ((caller != null) && (caller != undefined)) {
751: url += '&caller='+caller;
752: }
1.876 raeburn 753: var title = 'User_Browser';
754: var options = 'scrollbars=1,resizable=1,menubar=0';
755: options += ',width=700,height=600';
756: var stdeditbrowser = open(url,title,options,'1');
757: stdeditbrowser.focus();
758: }
759:
1.888 raeburn 760: function fix_domain (formname,udom,origdom,uname) {
1.876 raeburn 761: var formid = getFormIdByName(formname);
762: if (formid > -1) {
1.888 raeburn 763: var unameid = getIndexByName(formid,uname);
1.876 raeburn 764: var domid = getIndexByName(formid,udom);
765: var hidedomid = getIndexByName(formid,origdom);
766: if (hidedomid > -1) {
767: var fixeddom = document.forms[formid].elements[hidedomid].value;
1.888 raeburn 768: var unameval = document.forms[formid].elements[unameid].value;
769: if ((fixeddom != '') && (fixeddom != undefined) && (fixeddom != null) && (unameval != '') && (unameval != undefined) && (unameval != null)) {
770: if (domid > -1) {
771: var slct = document.forms[formid].elements[domid];
772: if (slct.type == 'select-one') {
773: var i;
774: for (i=0;i<slct.length;i++) {
775: if (slct.options[i].value==fixeddom) { slct.selectedIndex=i; }
776: }
777: }
778: if (slct.type == 'hidden') {
779: slct.value = fixeddom;
1.876 raeburn 780: }
781: }
1.468 raeburn 782: }
783: }
784: }
1.876 raeburn 785: return;
786: }
787:
788: $id_functions
789: ENDUSERBRW
1.468 raeburn 790: }
791:
792: sub setsec_javascript {
1.1116 raeburn 793: my ($sec_element,$formname,$role_element,$credits_element) = @_;
1.905 raeburn 794: my (@courserolenames,@communityrolenames,$rolestr,$courserolestr,
795: $communityrolestr);
796: if ($role_element ne '') {
797: my @allroles = ('st','ta','ep','in','ad');
798: foreach my $crstype ('Course','Community') {
799: if ($crstype eq 'Community') {
800: foreach my $role (@allroles) {
801: push(@communityrolenames,&Apache::lonnet::plaintext($role,$crstype));
802: }
803: push(@communityrolenames,&Apache::lonnet::plaintext('co'));
804: } else {
805: foreach my $role (@allroles) {
806: push(@courserolenames,&Apache::lonnet::plaintext($role,$crstype));
807: }
808: push(@courserolenames,&Apache::lonnet::plaintext('cc'));
809: }
810: }
811: $rolestr = '"'.join('","',@allroles).'"';
812: $courserolestr = '"'.join('","',@courserolenames).'"';
813: $communityrolestr = '"'.join('","',@communityrolenames).'"';
814: }
1.468 raeburn 815: my $setsections = qq|
816: function setSect(sectionlist) {
1.629 raeburn 817: var sectionsArray = new Array();
818: if ((sectionlist != '') && (typeof sectionlist != "undefined")) {
819: sectionsArray = sectionlist.split(",");
820: }
1.468 raeburn 821: var numSections = sectionsArray.length;
822: document.$formname.$sec_element.length = 0;
823: if (numSections == 0) {
824: document.$formname.$sec_element.multiple=false;
825: document.$formname.$sec_element.size=1;
826: document.$formname.$sec_element.options[0] = new Option('No existing sections','',false,false)
827: } else {
828: if (numSections == 1) {
829: document.$formname.$sec_element.multiple=false;
830: document.$formname.$sec_element.size=1;
831: document.$formname.$sec_element.options[0] = new Option('Select','',true,true);
832: document.$formname.$sec_element.options[1] = new Option('No section','',false,false)
833: document.$formname.$sec_element.options[2] = new Option(sectionsArray[0],sectionsArray[0],false,false);
834: } else {
835: for (var i=0; i<numSections; i++) {
836: document.$formname.$sec_element.options[i] = new Option(sectionsArray[i],sectionsArray[i],false,false)
837: }
838: document.$formname.$sec_element.multiple=true
839: if (numSections < 3) {
840: document.$formname.$sec_element.size=numSections;
841: } else {
842: document.$formname.$sec_element.size=3;
843: }
844: document.$formname.$sec_element.options[0].selected = false
845: }
846: }
1.91 www 847: }
1.905 raeburn 848:
849: function setRole(crstype) {
1.468 raeburn 850: |;
1.905 raeburn 851: if ($role_element eq '') {
852: $setsections .= ' return;
853: }
854: ';
855: } else {
856: $setsections .= qq|
857: var elementLength = document.$formname.$role_element.length;
858: var allroles = Array($rolestr);
859: var courserolenames = Array($courserolestr);
860: var communityrolenames = Array($communityrolestr);
861: if (elementLength != undefined) {
862: if (document.$formname.$role_element.options[5].value == 'cc') {
863: if (crstype == 'Course') {
864: return;
865: } else {
866: allroles[5] = 'co';
867: for (var i=0; i<6; i++) {
868: document.$formname.$role_element.options[i].value = allroles[i];
869: document.$formname.$role_element.options[i].text = communityrolenames[i];
870: }
871: }
872: } else {
873: if (crstype == 'Community') {
874: return;
875: } else {
876: allroles[5] = 'cc';
877: for (var i=0; i<6; i++) {
878: document.$formname.$role_element.options[i].value = allroles[i];
879: document.$formname.$role_element.options[i].text = courserolenames[i];
880: }
881: }
882: }
883: }
884: return;
885: }
886: |;
887: }
1.1116 raeburn 888: if ($credits_element) {
889: $setsections .= qq|
890: function setCredits(defaultcredits) {
891: document.$formname.$credits_element.value = defaultcredits;
892: return;
893: }
894: |;
895: }
1.468 raeburn 896: return $setsections;
897: }
898:
1.91 www 899: sub selectcourse_link {
1.909 raeburn 900: my ($form,$unameele,$udomele,$desc,$extra_element,$multflag,$selecttype,
901: $typeelement) = @_;
902: my $type = $selecttype;
1.871 raeburn 903: my $linktext = &mt('Select Course');
904: if ($selecttype eq 'Community') {
1.909 raeburn 905: $linktext = &mt('Select Community');
1.1239 raeburn 906: } elsif ($selecttype eq 'Placement') {
907: $linktext = &mt('Select Placement Test');
1.906 raeburn 908: } elsif ($selecttype eq 'Course/Community') {
909: $linktext = &mt('Select Course/Community');
1.909 raeburn 910: $type = '';
1.1019 raeburn 911: } elsif ($selecttype eq 'Select') {
912: $linktext = &mt('Select');
913: $type = '';
1.871 raeburn 914: }
1.787 bisitz 915: return '<span class="LC_nobreak">'
916: ."<a href='"
917: .'javascript:opencrsbrowser("'.$form.'","'.$unameele
918: .'","'.$udomele.'","'.$desc.'","'.$extra_element
1.909 raeburn 919: .'","'.$multflag.'","'.$type.'","'.$typeelement.'");'
1.871 raeburn 920: ."'>".$linktext.'</a>'
1.787 bisitz 921: .'</span>';
1.74 www 922: }
1.42 matthew 923:
1.653 raeburn 924: sub selectauthor_link {
925: my ($form,$udom)=@_;
926: return '<a href="javascript:openauthorbrowser('."'$form','$udom'".');">'.
927: &mt('Select Author').'</a>';
928: }
929:
1.876 raeburn 930: sub selectuser_link {
1.881 raeburn 931: my ($form,$unameelem,$domelem,$lastelem,$firstelem,$emailelem,$hdomelem,
1.888 raeburn 932: $coursedom,$linktext,$caller) = @_;
1.876 raeburn 933: return '<a href="javascript:openuserbrowser('."'$form','$unameelem','$domelem',".
1.888 raeburn 934: "'$lastelem','$firstelem','$emailelem','$hdomelem','$coursedom','$caller'".
1.881 raeburn 935: ');">'.$linktext.'</a>';
1.876 raeburn 936: }
937:
1.273 raeburn 938: sub check_uncheck_jscript {
939: my $jscript = <<"ENDSCRT";
940: function checkAll(field) {
941: if (field.length > 0) {
942: for (i = 0; i < field.length; i++) {
1.1093 raeburn 943: if (!field[i].disabled) {
944: field[i].checked = true;
945: }
1.273 raeburn 946: }
947: } else {
1.1093 raeburn 948: if (!field.disabled) {
949: field.checked = true;
950: }
1.273 raeburn 951: }
952: }
953:
954: function uncheckAll(field) {
955: if (field.length > 0) {
956: for (i = 0; i < field.length; i++) {
957: field[i].checked = false ;
1.543 albertel 958: }
959: } else {
1.273 raeburn 960: field.checked = false ;
961: }
962: }
963: ENDSCRT
964: return $jscript;
965: }
966:
1.656 www 967: sub select_timezone {
1.1387 raeburn 968: my ($name,$selected,$onchange,$includeempty,$id,$disabled)=@_;
1.1469 raeburn 969: my $labeltext = &HTML::Entities::encode(&mt('Select Time Zone'));
970: my $output='<select name="'.$name.'" '.$id.$onchange.$disabled.
971: ' aria-label="'.$labeltext.'">'."\n";
1.659 raeburn 972: if ($includeempty) {
973: $output .= '<option value=""';
974: if (($selected eq '') || ($selected eq 'local')) {
975: $output .= ' selected="selected" ';
976: }
977: $output .= '> </option>';
978: }
1.657 raeburn 979: my @timezones = DateTime::TimeZone->all_names;
980: foreach my $tzone (@timezones) {
981: $output.= '<option value="'.$tzone.'"';
982: if ($tzone eq $selected) {
983: $output.=' selected="selected"';
984: }
985: $output.=">$tzone</option>\n";
1.656 www 986: }
987: $output.="</select>";
988: return $output;
989: }
1.273 raeburn 990:
1.687 raeburn 991: sub select_datelocale {
1.1256 raeburn 992: my ($name,$selected,$onchange,$includeempty,$disabled)=@_;
993: my $output='<select name="'.$name.'" '.$onchange.$disabled.'>'."\n";
1.687 raeburn 994: if ($includeempty) {
995: $output .= '<option value=""';
996: if ($selected eq '') {
997: $output .= ' selected="selected" ';
998: }
999: $output .= '> </option>';
1000: }
1.1241 raeburn 1001: my @languages = &Apache::lonlocal::preferred_languages();
1.687 raeburn 1002: my (@possibles,%locale_names);
1.1241 raeburn 1003: my @locales = DateTime::Locale->ids();
1004: foreach my $id (@locales) {
1005: if ($id ne '') {
1006: my ($en_terr,$native_terr);
1007: my $loc = DateTime::Locale->load($id);
1008: if (ref($loc)) {
1009: $en_terr = $loc->name();
1010: $native_terr = $loc->native_name();
1.687 raeburn 1011: if (grep(/^en$/,@languages) || !@languages) {
1012: if ($en_terr ne '') {
1013: $locale_names{$id} = '('.$en_terr.')';
1014: } elsif ($native_terr ne '') {
1015: $locale_names{$id} = $native_terr;
1016: }
1017: } else {
1018: if ($native_terr ne '') {
1019: $locale_names{$id} = $native_terr.' ';
1020: } elsif ($en_terr ne '') {
1021: $locale_names{$id} = '('.$en_terr.')';
1022: }
1023: }
1.1220 raeburn 1024: $locale_names{$id} = Encode::encode('UTF-8',$locale_names{$id});
1.1241 raeburn 1025: push(@possibles,$id);
1026: }
1.687 raeburn 1027: }
1028: }
1029: foreach my $item (sort(@possibles)) {
1030: $output.= '<option value="'.$item.'"';
1031: if ($item eq $selected) {
1032: $output.=' selected="selected"';
1033: }
1034: $output.=">$item";
1035: if ($locale_names{$item} ne '') {
1.1220 raeburn 1036: $output.=' '.$locale_names{$item};
1.687 raeburn 1037: }
1038: $output.="</option>\n";
1039: }
1040: $output.="</select>";
1041: return $output;
1042: }
1043:
1.792 raeburn 1044: sub select_language {
1.1256 raeburn 1045: my ($name,$selected,$includeempty,$noedit) = @_;
1.792 raeburn 1046: my %langchoices;
1047: if ($includeempty) {
1.1117 raeburn 1048: %langchoices = ('' => 'No language preference');
1.792 raeburn 1049: }
1050: foreach my $id (&languageids()) {
1051: my $code = &supportedlanguagecode($id);
1052: if ($code) {
1053: $langchoices{$code} = &plainlanguagedescription($id);
1054: }
1055: }
1.1117 raeburn 1056: %langchoices = &Apache::lonlocal::texthash(%langchoices);
1.1256 raeburn 1057: return &select_form($selected,$name,\%langchoices,undef,$noedit);
1.792 raeburn 1058: }
1059:
1.42 matthew 1060: =pod
1.36 matthew 1061:
1.1088 foxr 1062:
1063: =item * &list_languages()
1064:
1065: Returns an array reference that is suitable for use in language prompters.
1066: Each array element is itself a two element array. The first element
1067: is the language code. The second element a descsriptiuon of the
1068: language itself. This is suitable for use in e.g.
1069: &Apache::edit::select_arg (once dereferenced that is).
1070:
1071: =cut
1072:
1073: sub list_languages {
1074: my @lang_choices;
1075:
1076: foreach my $id (&languageids()) {
1077: my $code = &supportedlanguagecode($id);
1078: if ($code) {
1079: my $selector = $supported_codes{$id};
1080: my $description = &plainlanguagedescription($id);
1.1263 raeburn 1081: push(@lang_choices, [$selector, $description]);
1.1088 foxr 1082: }
1083: }
1084: return \@lang_choices;
1085: }
1086:
1087: =pod
1088:
1.648 raeburn 1089: =item * &linked_select_forms(...)
1.36 matthew 1090:
1091: linked_select_forms returns a string containing a <script></script> block
1092: and html for two <select> menus. The select menus will be linked in that
1093: changing the value of the first menu will result in new values being placed
1094: in the second menu. The values in the select menu will appear in alphabetical
1.609 raeburn 1095: order unless a defined order is provided.
1.36 matthew 1096:
1097: linked_select_forms takes the following ordered inputs:
1098:
1099: =over 4
1100:
1.112 bowersj2 1101: =item * $formname, the name of the <form> tag
1.36 matthew 1102:
1.112 bowersj2 1103: =item * $middletext, the text which appears between the <select> tags
1.36 matthew 1104:
1.112 bowersj2 1105: =item * $firstdefault, the default value for the first menu
1.36 matthew 1106:
1.112 bowersj2 1107: =item * $firstselectname, the name of the first <select> tag
1.36 matthew 1108:
1.112 bowersj2 1109: =item * $secondselectname, the name of the second <select> tag
1.36 matthew 1110:
1.112 bowersj2 1111: =item * $hashref, a reference to a hash containing the data for the menus.
1.36 matthew 1112:
1.609 raeburn 1113: =item * $menuorder, the order of values in the first menu
1114:
1.1115 raeburn 1115: =item * $onchangefirst, additional javascript call to execute for an onchange
1116: event for the first <select> tag
1117:
1118: =item * $onchangesecond, additional javascript call to execute for an onchange
1119: event for the second <select> tag
1120:
1.1245 raeburn 1121: =item * $suffix, to differentiate separate uses of select2data javascript
1122: objects in a page.
1123:
1.41 ng 1124: =back
1125:
1.36 matthew 1126: Below is an example of such a hash. Only the 'text', 'default', and
1127: 'select2' keys must appear as stated. keys(%menu) are the possible
1128: values for the first select menu. The text that coincides with the
1.41 ng 1129: first menu value is given in $menu{$choice1}->{'text'}. The values
1.36 matthew 1130: and text for the second menu are given in the hash pointed to by
1131: $menu{$choice1}->{'select2'}.
1132:
1.112 bowersj2 1133: my %menu = ( A1 => { text =>"Choice A1" ,
1134: default => "B3",
1135: select2 => {
1136: B1 => "Choice B1",
1137: B2 => "Choice B2",
1138: B3 => "Choice B3",
1139: B4 => "Choice B4"
1.609 raeburn 1140: },
1141: order => ['B4','B3','B1','B2'],
1.112 bowersj2 1142: },
1143: A2 => { text =>"Choice A2" ,
1144: default => "C2",
1145: select2 => {
1146: C1 => "Choice C1",
1147: C2 => "Choice C2",
1148: C3 => "Choice C3"
1.609 raeburn 1149: },
1150: order => ['C2','C1','C3'],
1.112 bowersj2 1151: },
1152: A3 => { text =>"Choice A3" ,
1153: default => "D6",
1154: select2 => {
1155: D1 => "Choice D1",
1156: D2 => "Choice D2",
1157: D3 => "Choice D3",
1158: D4 => "Choice D4",
1159: D5 => "Choice D5",
1160: D6 => "Choice D6",
1161: D7 => "Choice D7"
1.609 raeburn 1162: },
1163: order => ['D4','D3','D2','D1','D7','D6','D5'],
1.112 bowersj2 1164: }
1165: );
1.36 matthew 1166:
1167: =cut
1168:
1169: sub linked_select_forms {
1170: my ($formname,
1171: $middletext,
1172: $firstdefault,
1173: $firstselectname,
1174: $secondselectname,
1.609 raeburn 1175: $hashref,
1176: $menuorder,
1.1115 raeburn 1177: $onchangefirst,
1.1245 raeburn 1178: $onchangesecond,
1.1450 raeburn 1179: $suffix,
1180: $haslabel
1.36 matthew 1181: ) = @_;
1182: my $second = "document.$formname.$secondselectname";
1183: my $first = "document.$formname.$firstselectname";
1184: # output the javascript to do the changing
1185: my $result = '';
1.776 bisitz 1186: $result.='<script type="text/javascript" language="JavaScript">'."\n";
1.824 bisitz 1187: $result.="// <![CDATA[\n";
1.1245 raeburn 1188: $result.="var select2data${suffix} = new Object();\n";
1.36 matthew 1189: $" = '","';
1190: my $debug = '';
1191: foreach my $s1 (sort(keys(%$hashref))) {
1.1245 raeburn 1192: $result.="select2data${suffix}['d_$s1'] = new Object();\n";
1193: $result.="select2data${suffix}['d_$s1'].def = new String('".
1.36 matthew 1194: $hashref->{$s1}->{'default'}."');\n";
1.1245 raeburn 1195: $result.="select2data${suffix}['d_$s1'].values = new Array(";
1.36 matthew 1196: my @s2values = sort(keys( %{ $hashref->{$s1}->{'select2'} } ));
1.609 raeburn 1197: if (ref($hashref->{$s1}->{'order'}) eq 'ARRAY') {
1198: @s2values = @{$hashref->{$s1}->{'order'}};
1199: }
1.36 matthew 1200: $result.="\"@s2values\");\n";
1.1245 raeburn 1201: $result.="select2data${suffix}['d_$s1'].texts = new Array(";
1.36 matthew 1202: my @s2texts;
1203: foreach my $value (@s2values) {
1.1263 raeburn 1204: push(@s2texts, $hashref->{$s1}->{'select2'}->{$value});
1.36 matthew 1205: }
1206: $result.="\"@s2texts\");\n";
1207: }
1208: $"=' ';
1209: $result.= <<"END";
1210:
1.1245 raeburn 1211: function select1${suffix}_changed() {
1.36 matthew 1212: // Determine new choice
1.1245 raeburn 1213: var newvalue = "d_" + $first.options[$first.selectedIndex].value;
1.36 matthew 1214: // update select2
1.1245 raeburn 1215: var values = select2data${suffix}[newvalue].values;
1216: var texts = select2data${suffix}[newvalue].texts;
1217: var select2def = select2data${suffix}[newvalue].def;
1.36 matthew 1218: var i;
1219: // out with the old
1.1245 raeburn 1220: $second.options.length = 0;
1221: // in with the new
1.36 matthew 1222: for (i=0;i<values.length; i++) {
1223: $second.options[i] = new Option(values[i]);
1.143 matthew 1224: $second.options[i].value = values[i];
1.36 matthew 1225: $second.options[i].text = texts[i];
1226: if (values[i] == select2def) {
1227: $second.options[i].selected = true;
1228: }
1229: }
1230: }
1.824 bisitz 1231: // ]]>
1.36 matthew 1232: </script>
1233: END
1234: # output the initial values for the selection lists
1.1245 raeburn 1235: $result .= "<select size=\"1\" name=\"$firstselectname\" onchange=\"select1${suffix}_changed();$onchangefirst\">\n";
1.609 raeburn 1236: my @order = sort(keys(%{$hashref}));
1237: if (ref($menuorder) eq 'ARRAY') {
1238: @order = @{$menuorder};
1239: }
1240: foreach my $value (@order) {
1.36 matthew 1241: $result.=" <option value=\"$value\" ";
1.253 albertel 1242: $result.=" selected=\"selected\" " if ($value eq $firstdefault);
1.119 www 1243: $result.=">".&mt($hashref->{$value}->{'text'})."</option>\n";
1.36 matthew 1244: }
1245: $result .= "</select>\n";
1.1450 raeburn 1246: if ($haslabel) {
1247: $result .= '</label>';
1248: }
1.1400 raeburn 1249: my %select2;
1250: if (ref($hashref->{$firstdefault}) eq 'HASH') {
1251: if (ref($hashref->{$firstdefault}->{'select2'}) eq 'HASH') {
1252: %select2 = %{$hashref->{$firstdefault}->{'select2'}};
1253: }
1254: }
1.1450 raeburn 1255: if ($middletext ne '') {
1.1452 raeburn 1256: $result .= '<label>'.$middletext;
1.1450 raeburn 1257: }
1.1115 raeburn 1258: $result .= "<select size=\"1\" name=\"$secondselectname\"";
1259: if ($onchangesecond) {
1260: $result .= ' onchange="'.$onchangesecond.'"';
1261: }
1262: $result .= ">\n";
1.36 matthew 1263: my $seconddefault = $hashref->{$firstdefault}->{'default'};
1.609 raeburn 1264:
1265: my @secondorder = sort(keys(%select2));
1266: if (ref($hashref->{$firstdefault}->{'order'}) eq 'ARRAY') {
1267: @secondorder = @{$hashref->{$firstdefault}->{'order'}};
1268: }
1269: foreach my $value (@secondorder) {
1.36 matthew 1270: $result.=" <option value=\"$value\" ";
1.253 albertel 1271: $result.=" selected=\"selected\" " if ($value eq $seconddefault);
1.119 www 1272: $result.=">".&mt($select2{$value})."</option>\n";
1.36 matthew 1273: }
1274: $result .= "</select>\n";
1.1450 raeburn 1275: if ($middletext ne '') {
1276: $result .= '</label>';
1277: }
1.36 matthew 1278: # return $debug;
1279: return $result;
1280: } # end of sub linked_select_forms {
1281:
1.45 matthew 1282: =pod
1.44 bowersj2 1283:
1.1381 raeburn 1284: =item * &help_open_topic($topic,$text,$stayOnPage,$width,$height,$imgid,$links_target)
1.44 bowersj2 1285:
1.112 bowersj2 1286: Returns a string corresponding to an HTML link to the given help
1287: $topic, where $topic corresponds to the name of a .tex file in
1288: /home/httpd/html/adm/help/tex, with underscores replaced by
1289: spaces.
1290:
1291: $text will optionally be linked to the same topic, allowing you to
1292: link text in addition to the graphic. If you do not want to link
1293: text, but wish to specify one of the later parameters, pass an
1294: empty string.
1295:
1296: $stayOnPage is a value that will be interpreted as a boolean. If true,
1297: the link will not open a new window. If false, the link will open
1298: a new window using Javascript. (Default is false.)
1299:
1300: $width and $height are optional numerical parameters that will
1301: override the width and height of the popped up window, which may
1.973 raeburn 1302: be useful for certain help topics with big pictures included.
1303:
1304: $imgid is the id of the img tag used for the help icon. This may be
1305: used in a javascript call to switch the image src. See
1306: lonhtmlcommon::htmlareaselectactive() for an example.
1.44 bowersj2 1307:
1.1381 raeburn 1308: $links_target will optionally be set to a target (_top, _parent or _self).
1309:
1.44 bowersj2 1310: =cut
1311:
1312: sub help_open_topic {
1.1381 raeburn 1313: my ($topic, $text, $stayOnPage, $width, $height, $imgid, $links_target) = @_;
1.48 bowersj2 1314: $text = "" if (not defined $text);
1.44 bowersj2 1315: $stayOnPage = 0 if (not defined $stayOnPage);
1.1033 www 1316: $width = 500 if (not defined $width);
1.44 bowersj2 1317: $height = 400 if (not defined $height);
1318: my $filename = $topic;
1319: $filename =~ s/ /_/g;
1320:
1.48 bowersj2 1321: my $template = "";
1322: my $link;
1.572 banghart 1323:
1.159 www 1324: $topic=~s/\W/\_/g;
1.44 bowersj2 1325:
1.572 banghart 1326: if (!$stayOnPage) {
1.1033 www 1327: $link = "javascript:openMyModal('/adm/help/${filename}.hlp',$width,$height,'yes');";
1.1037 www 1328: } elsif ($stayOnPage eq 'popup') {
1329: $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 1330: } else {
1.48 bowersj2 1331: $link = "/adm/help/${filename}.hlp";
1332: }
1333:
1334: # Add the text
1.1314 raeburn 1335: my $target = ' target="_top"';
1.1381 raeburn 1336: if ($links_target) {
1337: $target = ' target="'.$links_target.'"';
1338: } elsif ((($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) ||
1339: (($env{'request.deeplink.login'}) && ($env{'request.deeplink.target'} eq '_self'))) {
1340: $target = '';
1.1378 raeburn 1341: }
1.1380 raeburn 1342: if ($text ne "") {
1.763 bisitz 1343: $template.='<span class="LC_help_open_topic">'
1.1314 raeburn 1344: .'<a'.$target.' href="'.$link.'">'
1.763 bisitz 1345: .$text.'</a>';
1.48 bowersj2 1346: }
1347:
1.763 bisitz 1348: # (Always) Add the graphic
1.179 matthew 1349: my $title = &mt('Online Help');
1.667 raeburn 1350: my $helpicon=&lonhttpdurl("/adm/help/help.png");
1.973 raeburn 1351: if ($imgid ne '') {
1352: $imgid = ' id="'.$imgid.'"';
1353: }
1.1314 raeburn 1354: $template.=' <a'.$target.' href="'.$link.'" title="'.$title.'">'
1.763 bisitz 1355: .'<img src="'.$helpicon.'" border="0"'
1.1469 raeburn 1356: .' alt="'.&mt('Help icon').'"'
1.973 raeburn 1357: .' title="'.$title.'" style="vertical-align:middle;"'.$imgid
1.763 bisitz 1358: .' /></a>';
1359: if ($text ne "") {
1360: $template.='</span>';
1361: }
1.44 bowersj2 1362: return $template;
1363:
1.106 bowersj2 1364: }
1365:
1366: # This is a quicky function for Latex cheatsheet editing, since it
1367: # appears in at least four places
1368: sub helpLatexCheatsheet {
1.1037 www 1369: my ($topic,$text,$not_author,$stayOnPage) = @_;
1.732 raeburn 1370: my $out;
1.106 bowersj2 1371: my $addOther = '';
1.732 raeburn 1372: if ($topic) {
1.1037 www 1373: $addOther = '<span>'.&help_open_topic($topic,&mt($text),$stayOnPage, undef, 600).'</span> ';
1.763 bisitz 1374: }
1375: $out = '<span>' # Start cheatsheet
1376: .$addOther
1377: .'<span>'
1.1037 www 1378: .&help_open_topic('Greek_Symbols',&mt('Greek Symbols'),$stayOnPage,undef,600)
1.763 bisitz 1379: .'</span> <span>'
1.1037 www 1380: .&help_open_topic('Other_Symbols',&mt('Other Symbols'),$stayOnPage,undef,600)
1.763 bisitz 1381: .'</span>';
1.732 raeburn 1382: unless ($not_author) {
1.1186 kruse 1383: $out .= '<span>'
1384: .&help_open_topic('Authoring_Output_Tags',&mt('Output Tags'),$stayOnPage,undef,600)
1385: .'</span> <span>'
1.1424 raeburn 1386: .&help_open_topic('Authoring_Multilingual_Problems',&mt('Languages'),$stayOnPage,undef,600)
1.763 bisitz 1387: .'</span>';
1.732 raeburn 1388: }
1.763 bisitz 1389: $out .= '</span>'; # End cheatsheet
1.732 raeburn 1390: return $out;
1.172 www 1391: }
1392:
1.430 albertel 1393: sub general_help {
1394: my $helptopic='Student_Intro';
1395: if ($env{'request.role'}=~/^(ca|au)/) {
1396: $helptopic='Authoring_Intro';
1.907 raeburn 1397: } elsif ($env{'request.role'}=~/^(cc|co)/) {
1.430 albertel 1398: $helptopic='Course_Coordination_Intro';
1.672 raeburn 1399: } elsif ($env{'request.role'}=~/^dc/) {
1400: $helptopic='Domain_Coordination_Intro';
1.430 albertel 1401: }
1402: return $helptopic;
1403: }
1404:
1405: sub update_help_link {
1406: my ($topic,$component_help,$faq,$bug,$stayOnPage) = @_;
1407: my $origurl = $ENV{'REQUEST_URI'};
1408: $origurl=~s|^/~|/priv/|;
1409: my $timestamp = time;
1410: foreach my $datum (\$topic,\$component_help,\$faq,\$bug,\$origurl) {
1411: $$datum = &escape($$datum);
1412: }
1413:
1414: my $banner_link = "/adm/helpmenu?page=banner&topic=$topic&component_help=$component_help&faq=$faq&bug=$bug&origurl=$origurl&stamp=$timestamp&stayonpage=$stayOnPage";
1415: my $output .= <<"ENDOUTPUT";
1416: <script type="text/javascript">
1.824 bisitz 1417: // <![CDATA[
1.430 albertel 1418: banner_link = '$banner_link';
1.824 bisitz 1419: // ]]>
1.430 albertel 1420: </script>
1421: ENDOUTPUT
1422: return $output;
1423: }
1424:
1425: # now just updates the help link and generates a blue icon
1.193 raeburn 1426: sub help_open_menu {
1.1381 raeburn 1427: my ($topic,$component_help,$faq,$bug,$stayOnPage,$width,$height,$text,$links_target)
1.552 banghart 1428: = @_;
1.949 droeschl 1429: $stayOnPage = 1;
1.430 albertel 1430: my $output;
1431: if ($component_help) {
1432: if (!$text) {
1433: $output=&help_open_topic($component_help,undef,$stayOnPage,
1.1381 raeburn 1434: $width,$height,'',$links_target);
1.430 albertel 1435: } else {
1436: my $help_text;
1437: $help_text=&unescape($topic);
1438: $output='<table><tr><td>'.
1439: &help_open_topic($component_help,$help_text,$stayOnPage,
1.1381 raeburn 1440: $width,$height,'',$links_target).'</td></tr></table>';
1.430 albertel 1441: }
1442: }
1443: my $banner_link = &update_help_link($topic,$component_help,$faq,$bug,$stayOnPage);
1444: return $output.$banner_link;
1445: }
1446:
1447: sub top_nav_help {
1.1369 raeburn 1448: my ($text,$linkattr) = @_;
1.436 albertel 1449: $text = &mt($text);
1.949 droeschl 1450: my $stay_on_page = 1;
1451:
1.1168 raeburn 1452: my ($link,$banner_link);
1453: unless ($env{'request.noversionuri'} =~ m{^/adm/helpmenu}) {
1454: $link = ($stay_on_page) ? "javascript:helpMenu('display')"
1455: : "javascript:helpMenu('open')";
1456: $banner_link = &update_help_link(undef,undef,undef,undef,$stay_on_page);
1457: }
1.201 raeburn 1458: my $title = &mt('Get help');
1.1168 raeburn 1459: if ($link) {
1460: return <<"END";
1.436 albertel 1461: $banner_link
1.1369 raeburn 1462: <a href="$link" title="$title" $linkattr>$text</a>
1.436 albertel 1463: END
1.1168 raeburn 1464: } else {
1.1459 raeburn 1465: return ' <h1 class="LC_helpmenu">'.$text.'</h1> ';
1.1168 raeburn 1466: }
1.436 albertel 1467: }
1468:
1469: sub help_menu_js {
1.1154 raeburn 1470: my ($httphost) = @_;
1.949 droeschl 1471: my $stayOnPage = 1;
1.436 albertel 1472: my $width = 620;
1473: my $height = 600;
1.430 albertel 1474: my $helptopic=&general_help();
1.1154 raeburn 1475: my $details_link = $httphost.'/adm/help/'.$helptopic.'.hlp';
1.261 albertel 1476: my $nothing=&Apache::lonhtmlcommon::javascript_nothing();
1.1459 raeburn 1477: my $bannertitle = &mt('Help Menu');
1478: &js_escape(\$bannertitle);
1479: my $bodytitle = &mt('Documentation');
1480: &js_escape(\$bodytitle);
1.331 albertel 1481: my $start_page =
1482: &Apache::loncommon::start_page('Help Menu', undef,
1483: {'frameset' => 1,
1484: 'js_ready' => 1,
1.1154 raeburn 1485: 'use_absolute' => $httphost,
1.331 albertel 1486: 'add_entries' => {
1.1168 raeburn 1487: 'border' => '0',
1.579 raeburn 1488: 'rows' => "110,*",},});
1.331 albertel 1489: my $end_page =
1490: &Apache::loncommon::end_page({'frameset' => 1,
1491: 'js_ready' => 1,});
1.436 albertel 1492: my $template .= <<"ENDTEMPLATE";
1493: <script type="text/javascript">
1.877 bisitz 1494: // <![CDATA[
1.253 albertel 1495: // <!-- BEGIN LON-CAPA Internal
1.430 albertel 1496: var banner_link = '';
1.243 raeburn 1497: function helpMenu(target) {
1498: var caller = this;
1499: if (target == 'open') {
1500: var newWindow = null;
1501: try {
1.262 albertel 1502: newWindow = window.open($nothing,"helpmenu","HEIGHT=$height,WIDTH=$width,resizable=yes,scrollbars=yes" )
1.243 raeburn 1503: }
1504: catch(error) {
1505: writeHelp(caller);
1506: return;
1507: }
1508: if (newWindow) {
1509: caller = newWindow;
1510: }
1.193 raeburn 1511: }
1.243 raeburn 1512: writeHelp(caller);
1513: return;
1514: }
1515: function writeHelp(caller) {
1.1459 raeburn 1516: caller.document.writeln('$start_page\\n<frame name="bannerframe" title="$bannertitle" src="'+banner_link+'" marginwidth="0" marginheight="0" frameborder="0">\\n');
1517: caller.document.writeln('<frame name="bodyframe" title="$bodytitle" src="$details_link" marginwidth="0" marginheight="0" frameborder="0">\\n$end_page');
1.1168 raeburn 1518: caller.document.close();
1519: caller.focus();
1.193 raeburn 1520: }
1.877 bisitz 1521: // END LON-CAPA Internal -->
1.253 albertel 1522: // ]]>
1.436 albertel 1523: </script>
1.193 raeburn 1524: ENDTEMPLATE
1525: return $template;
1526: }
1527:
1.172 www 1528: sub help_open_bug {
1529: my ($topic, $text, $stayOnPage, $width, $height) = @_;
1.258 albertel 1530: unless ($env{'user.adv'}) { return ''; }
1.172 www 1531: unless ($Apache::lonnet::perlvar{'BugzillaHost'}) { return ''; }
1532: $text = "" if (not defined $text);
1533: $stayOnPage=1;
1.184 albertel 1534: $width = 600 if (not defined $width);
1535: $height = 600 if (not defined $height);
1.172 www 1536:
1537: $topic=~s/\W+/\+/g;
1538: my $link='';
1539: my $template='';
1.379 albertel 1540: my $url=$Apache::lonnet::perlvar{'BugzillaHost'}.'enter_bug.cgi?product=LON-CAPA&bug_file_loc='.
1541: &escape($ENV{'REQUEST_URI'}).'&component='.$topic;
1.172 www 1542: if (!$stayOnPage)
1543: {
1544: $link = "javascript:void(open('$url', 'Bugzilla', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
1545: }
1546: else
1547: {
1548: $link = $url;
1549: }
1.1314 raeburn 1550:
1.1382 raeburn 1551: my $target = '_top';
1552: if ((($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) ||
1553: (($env{'request.deeplink.login'}) && ($env{'request.deeplink.target'} eq '_self'))) {
1554: $target = '_blank';
1.1378 raeburn 1555: }
1.1382 raeburn 1556:
1.172 www 1557: # Add the text
1558: if ($text ne "")
1559: {
1560: $template .=
1561: "<table bgcolor='#AA3333' cellspacing='1' cellpadding='1' border='0'><tr>".
1.1382 raeburn 1562: "<td bgcolor='#FF5555'><a target=\"$target\" href=\"$link\"><span style=\"color:#FFFFFF;font-size:10pt;\">$text</span></a>";
1.172 www 1563: }
1564:
1565: # Add the graphic
1.179 matthew 1566: my $title = &mt('Report a Bug');
1.215 albertel 1567: my $bugicon=&lonhttpdurl("/adm/lonMisc/smallBug.gif");
1.172 www 1568: $template .= <<"ENDTEMPLATE";
1.1382 raeburn 1569: <a target="$target" href="$link" title="$title"><img src="$bugicon" border="0" alt="(Bug: $topic)" /></a>
1.172 www 1570: ENDTEMPLATE
1571: if ($text ne '') { $template.='</td></tr></table>' };
1572: return $template;
1573:
1574: }
1575:
1576: sub help_open_faq {
1577: my ($topic, $text, $stayOnPage, $width, $height) = @_;
1.258 albertel 1578: unless ($env{'user.adv'}) { return ''; }
1.172 www 1579: unless ($Apache::lonnet::perlvar{'FAQHost'}) { return ''; }
1580: $text = "" if (not defined $text);
1581: $stayOnPage=1;
1582: $width = 350 if (not defined $width);
1583: $height = 400 if (not defined $height);
1584:
1585: $topic=~s/\W+/\+/g;
1586: my $link='';
1587: my $template='';
1588: my $url=$Apache::lonnet::perlvar{'FAQHost'}.'/fom/cache/'.$topic.'.html';
1589: if (!$stayOnPage)
1590: {
1591: $link = "javascript:void(open('$url', 'FAQ-O-Matic', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
1592: }
1593: else
1594: {
1595: $link = $url;
1596: }
1597:
1598: # Add the text
1599: if ($text ne "")
1600: {
1601: $template .=
1.173 www 1602: "<table bgcolor='#337733' cellspacing='1' cellpadding='1' border='0'><tr>".
1.705 tempelho 1603: "<td bgcolor='#448844'><a target=\"_top\" href=\"$link\"><span style=\"color:#FFFFFF; font-size:10pt;\">$text</span></a>";
1.172 www 1604: }
1605:
1606: # Add the graphic
1.179 matthew 1607: my $title = &mt('View the FAQ');
1.215 albertel 1608: my $faqicon=&lonhttpdurl("/adm/lonMisc/smallFAQ.gif");
1.172 www 1609: $template .= <<"ENDTEMPLATE";
1.436 albertel 1610: <a target="_top" href="$link" title="$title"><img src="$faqicon" border="0" alt="(FAQ: $topic)" /></a>
1.172 www 1611: ENDTEMPLATE
1612: if ($text ne '') { $template.='</td></tr></table>' };
1613: return $template;
1614:
1.44 bowersj2 1615: }
1.37 matthew 1616:
1.180 matthew 1617: ###############################################################
1618: ###############################################################
1619:
1.45 matthew 1620: =pod
1621:
1.648 raeburn 1622: =item * &change_content_javascript():
1.256 matthew 1623:
1624: This and the next function allow you to create small sections of an
1625: otherwise static HTML page that you can update on the fly with
1626: Javascript, even in Netscape 4.
1627:
1628: The Javascript fragment returned by this function (no E<lt>scriptE<gt> tag)
1629: must be written to the HTML page once. It will prove the Javascript
1630: function "change(name, content)". Calling the change function with the
1631: name of the section
1632: you want to update, matching the name passed to C<changable_area>, and
1633: the new content you want to put in there, will put the content into
1634: that area.
1635:
1636: B<Note>: Netscape 4 only reserves enough space for the changable area
1637: to contain room for the original contents. You need to "make space"
1638: for whatever changes you wish to make, and be B<sure> to check your
1639: code in Netscape 4. This feature in Netscape 4 is B<not> powerful;
1640: it's adequate for updating a one-line status display, but little more.
1641: This script will set the space to 100% width, so you only need to
1642: worry about height in Netscape 4.
1643:
1644: Modern browsers are much less limiting, and if you can commit to the
1645: user not using Netscape 4, this feature may be used freely with
1646: pretty much any HTML.
1647:
1648: =cut
1649:
1650: sub change_content_javascript {
1651: # If we're on Netscape 4, we need to use Layer-based code
1.258 albertel 1652: if ($env{'browser.type'} eq 'netscape' &&
1653: $env{'browser.version'} =~ /^4\./) {
1.256 matthew 1654: return (<<NETSCAPE4);
1655: function change(name, content) {
1656: doc = document.layers[name+"___escape"].layers[0].document;
1657: doc.open();
1658: doc.write(content);
1659: doc.close();
1660: }
1661: NETSCAPE4
1662: } else {
1663: # Otherwise, we need to use semi-standards-compliant code
1664: # (technically, "innerHTML" isn't standard but the equivalent
1665: # is really scary, and every useful browser supports it
1666: return (<<DOMBASED);
1667: function change(name, content) {
1668: element = document.getElementById(name);
1669: element.innerHTML = content;
1670: }
1671: DOMBASED
1672: }
1673: }
1674:
1675: =pod
1676:
1.648 raeburn 1677: =item * &changable_area($name,$origContent):
1.256 matthew 1678:
1679: This provides a "changable area" that can be modified on the fly via
1680: the Javascript code provided in C<change_content_javascript>. $name is
1681: the name you will use to reference the area later; do not repeat the
1682: same name on a given HTML page more then once. $origContent is what
1683: the area will originally contain, which can be left blank.
1684:
1685: =cut
1686:
1687: sub changable_area {
1688: my ($name, $origContent) = @_;
1689:
1.258 albertel 1690: if ($env{'browser.type'} eq 'netscape' &&
1691: $env{'browser.version'} =~ /^4\./) {
1.256 matthew 1692: # If this is netscape 4, we need to use the Layer tag
1693: return "<ilayer width='100%' id='${name}___escape' overflow='none'><layer width='100%' id='$name' overflow='none'>$origContent</layer></ilayer>";
1694: } else {
1695: return "<span id='$name'>$origContent</span>";
1696: }
1697: }
1698:
1699: =pod
1700:
1.648 raeburn 1701: =item * &viewport_geometry_js
1.590 raeburn 1702:
1703: Provides javascript object (Geometry) which can provide information about the viewport geometry for the client browser.
1704:
1705: =cut
1706:
1707:
1708: sub viewport_geometry_js {
1709: return <<"GEOMETRY";
1710: var Geometry = {};
1711: function init_geometry() {
1712: if (Geometry.init) { return };
1713: Geometry.init=1;
1714: if (window.innerHeight) {
1715: Geometry.getViewportHeight = function() { return window.innerHeight; };
1716: Geometry.getViewportWidth = function() { return window.innerWidth; };
1717: Geometry.getHorizontalScroll = function() { return window.pageXOffset; };
1718: Geometry.getVerticalScroll = function() { return window.pageYOffset; };
1719: }
1720: else if (document.documentElement && document.documentElement.clientHeight) {
1721: Geometry.getViewportHeight =
1722: function() { return document.documentElement.clientHeight; };
1723: Geometry.getViewportWidth =
1724: function() { return document.documentElement.clientWidth; };
1725:
1726: Geometry.getHorizontalScroll =
1727: function() { return document.documentElement.scrollLeft; };
1728: Geometry.getVerticalScroll =
1729: function() { return document.documentElement.scrollTop; };
1730: }
1731: else if (document.body.clientHeight) {
1732: Geometry.getViewportHeight =
1733: function() { return document.body.clientHeight; };
1734: Geometry.getViewportWidth =
1735: function() { return document.body.clientWidth; };
1736: Geometry.getHorizontalScroll =
1737: function() { return document.body.scrollLeft; };
1738: Geometry.getVerticalScroll =
1739: function() { return document.body.scrollTop; };
1740: }
1741: }
1742:
1743: GEOMETRY
1744: }
1745:
1746: =pod
1747:
1.648 raeburn 1748: =item * &viewport_size_js()
1.590 raeburn 1749:
1750: 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.
1751:
1752: =cut
1753:
1754: sub viewport_size_js {
1755: my $geometry = &viewport_geometry_js();
1756: return <<"DIMS";
1757:
1758: $geometry
1759:
1760: function getViewportDims(width,height) {
1761: init_geometry();
1762: width.value = Geometry.getViewportWidth();
1763: height.value = Geometry.getViewportHeight();
1764: return;
1765: }
1766:
1767: DIMS
1768: }
1769:
1770: =pod
1771:
1.648 raeburn 1772: =item * &resize_textarea_js()
1.565 albertel 1773:
1774: emits the needed javascript to resize a textarea to be as big as possible
1775:
1776: creates a function resize_textrea that takes two IDs first should be
1777: the id of the element to resize, second should be the id of a div that
1778: surrounds everything that comes after the textarea, this routine needs
1779: to be attached to the <body> for the onload and onresize events.
1780:
1781: =cut
1782:
1783: sub resize_textarea_js {
1.590 raeburn 1784: my $geometry = &viewport_geometry_js();
1.565 albertel 1785: return <<"RESIZE";
1786: <script type="text/javascript">
1.824 bisitz 1787: // <![CDATA[
1.590 raeburn 1788: $geometry
1.565 albertel 1789:
1.588 albertel 1790: function getX(element) {
1791: var x = 0;
1792: while (element) {
1793: x += element.offsetLeft;
1794: element = element.offsetParent;
1795: }
1796: return x;
1797: }
1798: function getY(element) {
1799: var y = 0;
1800: while (element) {
1801: y += element.offsetTop;
1802: element = element.offsetParent;
1803: }
1804: return y;
1805: }
1806:
1807:
1.565 albertel 1808: function resize_textarea(textarea_id,bottom_id) {
1809: init_geometry();
1810: var textarea = document.getElementById(textarea_id);
1811: //alert(textarea);
1812:
1.588 albertel 1813: var textarea_top = getY(textarea);
1.565 albertel 1814: var textarea_height = textarea.offsetHeight;
1815: var bottom = document.getElementById(bottom_id);
1.588 albertel 1816: var bottom_top = getY(bottom);
1.565 albertel 1817: var bottom_height = bottom.offsetHeight;
1818: var window_height = Geometry.getViewportHeight();
1.588 albertel 1819: var fudge = 23;
1.565 albertel 1820: var new_height = window_height-fudge-textarea_top-bottom_height;
1821: if (new_height < 300) {
1822: new_height = 300;
1823: }
1824: textarea.style.height=new_height+'px';
1825: }
1.824 bisitz 1826: // ]]>
1.565 albertel 1827: </script>
1828: RESIZE
1829:
1830: }
1831:
1.1205 golterma 1832: sub colorfuleditor_js {
1.1248 raeburn 1833: my $browse_or_search;
1834: my $respath;
1835: my ($cnum,$cdom) = &crsauthor_url();
1836: if ($cnum) {
1837: $respath = "/res/$cdom/$cnum/";
1838: my %js_lt = &Apache::lonlocal::texthash(
1839: sunm => 'Sub-directory name',
1840: save => 'Save page to make this permanent',
1841: );
1842: &js_escape(\%js_lt);
1.1400 raeburn 1843: my $showfile_js = &show_crsfiles_js();
1.1248 raeburn 1844: $browse_or_search = <<"END";
1845:
1.1400 raeburn 1846: $showfile_js
1847:
1.1248 raeburn 1848: function toggleChooser(form,element,titleid,only,search) {
1849: var disp = 'none';
1850: if (document.getElementById('chooser_'+element)) {
1851: var curr = document.getElementById('chooser_'+element).style.display;
1852: if (curr == 'none') {
1853: disp='inline';
1854: if (form.elements['chooser_'+element].length) {
1855: for (var i=0; i<form.elements['chooser_'+element].length; i++) {
1856: form.elements['chooser_'+element][i].checked = false;
1857: }
1858: }
1859: toggleResImport(form,element);
1860: }
1861: document.getElementById('chooser_'+element).style.display = disp;
1.1400 raeburn 1862: var dirsel = '';
1863: var filesel = '';
1864: if (document.getElementById('chooser_'+element+'_crsres')) {
1865: var currcrsres = document.getElementById('chooser_'+element+'_crsres').style.display;
1866: if (currcrsres == 'none') {
1867: dirsel = 'coursepath_'+element;
1868: var filesel = 'coursefile_'+element;
1869: var include;
1870: if (document.getElementById('crsres_include_'+element)) {
1871: include = document.getElementById('crsres_include_'+element).value;
1872: }
1.1402 raeburn 1873: populateCrsSelects(form,dirsel,filesel,1,include,1,0,1,1,0);
1.1400 raeburn 1874: }
1875: }
1876: if (document.getElementById('chooser_'+element+'_upload')) {
1877: var currcrsupload = document.getElementById('chooser_'+element+'_upload').style.display;
1878: if (currcrsupload == 'none') {
1879: dirsel = 'crsauthorpath_'+element;
1880: filesel = '';
1.1402 raeburn 1881: populateCrsSelects(form,dirsel,filesel,0,'',1,0,1,0,1);
1.1400 raeburn 1882: }
1883: }
1.1248 raeburn 1884: }
1885: }
1886:
1.1400 raeburn 1887: function toggleCrsFile(form,element) {
1.1248 raeburn 1888: if (document.getElementById('chooser_'+element+'_crsres')) {
1889: var curr = document.getElementById('chooser_'+element+'_crsres').style.display;
1890: if (curr == 'none') {
1.1400 raeburn 1891: if (document.getElementById('coursepath_'+element)) {
1892: var numdirs;
1893: if (document.getElementById('coursepath_'+element).length) {
1894: numdirs = document.getElementById('coursepath_'+element).length;
1895: }
1.1402 raeburn 1896: if ((document.getElementById('hascrsres_'+element)) &&
1897: (document.getElementById('nocrsres_'+element))) {
1898: if (numdirs) {
1899: document.getElementById('hascrsres_'+element).style.display='inline-block';
1900: document.getElementById('nocrsres_'+element).style.display='none';
1901: } else {
1902: document.getElementById('hascrsres_'+element).style.display='none';
1903: document.getElementById('nocrsres_'+element).style.display='inline-block';
1904: }
1905: }
1.1248 raeburn 1906: form.elements['coursepath_'+element].selectedIndex = 0;
1907: if (numdirs > 1) {
1.1400 raeburn 1908: var selelem = form.elements['coursefile_'+element];
1909: var i, len = selelem.options.length -1;
1910: if (len >=0) {
1911: for (i = len; i >= 0; i--) {
1912: selelem.remove(i);
1913: }
1914: selelem.options[0] = new Option('','');
1915: }
1.1248 raeburn 1916: }
1917: }
1.1400 raeburn 1918: }
1.1248 raeburn 1919: document.getElementById('chooser_'+element+'_crsres').style.display = 'block';
1920: }
1921: if (document.getElementById('chooser_'+element+'_upload')) {
1922: document.getElementById('chooser_'+element+'_upload').style.display = 'none';
1923: if (document.getElementById('uploadcrsres_'+element)) {
1924: document.getElementById('uploadcrsres_'+element).value = '';
1925: }
1926: }
1927: return;
1928: }
1929:
1.1400 raeburn 1930: function toggleCrsUpload(form,element) {
1.1248 raeburn 1931: if (document.getElementById('chooser_'+element+'_crsres')) {
1932: document.getElementById('chooser_'+element+'_crsres').style.display = 'none';
1933: }
1934: if (document.getElementById('chooser_'+element+'_upload')) {
1935: var curr = document.getElementById('chooser_'+element+'_upload').style.display;
1936: if (curr == 'none') {
1.1400 raeburn 1937: form.elements['newsubdir_'+element][0].checked = true;
1938: toggleNewsubdir(form,element);
1939: document.getElementById('chooser_'+element+'_upload').style.display = 'block';
1940: if (document.getElementById('uploadcrsres_'+element)) {
1941: document.getElementById('uploadcrsres_'+element).value = '';
1.1248 raeburn 1942: }
1943: }
1944: }
1945: return;
1946: }
1947:
1948: function toggleResImport(form,element) {
1949: var choices = new Array('crsres','upload');
1950: for (var i=0; i<choices.length; i++) {
1951: if (document.getElementById('chooser_'+element+'_'+choices[i])) {
1952: document.getElementById('chooser_'+element+'_'+choices[i]).style.display = 'none';
1953: }
1954: }
1955: }
1956:
1957: function toggleNewsubdir(form,element) {
1958: var newsub = form.elements['newsubdir_'+element];
1959: if (newsub) {
1960: if (newsub.length) {
1961: for (var j=0; j<newsub.length; j++) {
1962: if (newsub[j].checked) {
1963: if (document.getElementById('newsubdirname_'+element)) {
1964: if (newsub[j].value == '1') {
1965: document.getElementById('newsubdirname_'+element).type = "text";
1966: if (document.getElementById('newsubdir_'+element)) {
1967: document.getElementById('newsubdir_'+element).innerHTML = '<br />$js_lt{sunm}';
1968: }
1969: } else {
1970: document.getElementById('newsubdirname_'+element).type = "hidden";
1971: document.getElementById('newsubdirname_'+element).value = "";
1972: document.getElementById('newsubdir_'+element).innerHTML = "";
1973: }
1974: }
1975: break;
1976: }
1977: }
1978: }
1979: }
1980: }
1981:
1982: function updateCrsFile(form,element) {
1983: var directory = form.elements['coursepath_'+element];
1984: var filename = form.elements['coursefile_'+element];
1985: var path = directory.options[directory.selectedIndex].value;
1986: var file = filename.options[filename.selectedIndex].value;
1.1400 raeburn 1987: if (file != '') {
1988: form.elements[element].value = '$respath';
1989: if (path == '/') {
1990: form.elements[element].value += file;
1991: } else {
1992: form.elements[element].value += path+'/'+file;
1993: }
1994: unClean();
1995: if (document.getElementById('previewimg_'+element)) {
1996: document.getElementById('previewimg_'+element).src = form.elements[element].value;
1997: var newsrc = document.getElementById('previewimg_'+element).src;
1998: }
1999: if (document.getElementById('showimg_'+element)) {
2000: document.getElementById('showimg_'+element).innerHTML = '($js_lt{save})';
2001: }
1.1248 raeburn 2002: }
2003: toggleChooser(form,element);
2004: return;
2005: }
2006:
2007: function uploadDone(suffix,name) {
2008: if (name) {
2009: document.forms["lonhomework"].elements[suffix].value = name;
2010: unClean();
2011: toggleChooser(document.forms["lonhomework"],suffix);
2012: }
2013: }
2014:
2015: \$(document).ready(function(){
2016:
2017: \$(document).delegate('form :submit', 'click', function( event ) {
2018: if ( \$( this ).hasClass( "LC_uploadcrsres" ) ) {
2019: var buttonId = this.id;
2020: var suffix = buttonId.toString();
2021: suffix = suffix.replace(/^crsupload_/,'');
2022: event.preventDefault();
2023: document.lonhomework.target = 'crsupload_target_'+suffix;
2024: document.lonhomework.action = '/adm/coursepub?LC_uploadcrsres='+suffix;
2025: \$(this.form).submit();
2026: document.lonhomework.target = '';
2027: if (document.getElementById('crsuploadto_'+suffix)) {
2028: document.lonhomework.action = document.getElementById('crsuploadto_'+suffix).value;
2029: }
2030: return false;
2031: }
2032: });
2033: });
2034: END
2035: }
1.1205 golterma 2036: return <<"COLORFULEDIT"
2037: <script type="text/javascript">
2038: // <![CDATA[>
2039: function fold_box(curDepth, lastresource){
2040:
2041: // we need a list because there can be several blocks you need to fold in one tag
2042: var block = document.getElementsByName('foldblock_'+curDepth);
2043: // but there is only one folding button per tag
2044: var foldbutton = document.getElementById('folding_btn_'+curDepth);
2045:
2046: if(block.item(0).style.display == 'none'){
2047:
2048: foldbutton.value = '@{[&mt("Hide")]}';
2049: for (i = 0; i < block.length; i++){
2050: block.item(i).style.display = '';
2051: }
2052: }else{
2053:
2054: foldbutton.value = '@{[&mt("Show")]}';
2055: for (i = 0; i < block.length; i++){
2056: // block.item(i).style.visibility = 'collapse';
2057: block.item(i).style.display = 'none';
2058: }
2059: };
2060: saveState(lastresource);
2061: }
2062:
2063: function saveState (lastresource) {
2064:
2065: var tag_list = getTagList();
2066: if(tag_list != null){
2067: var timestamp = new Date().getTime();
2068: var key = lastresource;
2069:
2070: // the value pattern is: 'time;key1,value1;key2,value2; ... '
2071: // starting with timestamp
2072: var value = timestamp+';';
2073:
2074: // building the list of key-value pairs
2075: for(var i = 0; i < tag_list.length; i++){
2076: value += tag_list[i]+',';
2077: value += document.getElementsByName(tag_list[i])[0].style.display+';';
2078: }
2079:
2080: // only iterate whole storage if nothing to override
2081: if(localStorage.getItem(key) == null){
2082:
2083: // prevent storage from growing large
2084: if(localStorage.length > 50){
2085: var regex_getTimestamp = /^(?:\d)+;/;
2086: var oldest_timestamp = regex_getTimestamp.exec(localStorage.key(0));
2087: var oldest_key;
2088:
2089: for(var i = 1; i < localStorage.length; i++){
2090: if (regex_getTimestamp.exec(localStorage.key(i)) < oldest_timestamp) {
2091: oldest_key = localStorage.key(i);
2092: oldest_timestamp = regex_getTimestamp.exec(oldest_key);
2093: }
2094: }
2095: localStorage.removeItem(oldest_key);
2096: }
2097: }
2098: localStorage.setItem(key,value);
2099: }
2100: }
2101:
2102: // restore folding status of blocks (on page load)
2103: function restoreState (lastresource) {
2104: if(localStorage.getItem(lastresource) != null){
2105: var key = lastresource;
2106: var value = localStorage.getItem(key);
2107: var regex_delTimestamp = /^\d+;/;
2108:
2109: value.replace(regex_delTimestamp, '');
2110:
2111: var valueArr = value.split(';');
2112: var pairs;
2113: var elements;
2114: for (var i = 0; i < valueArr.length; i++){
2115: pairs = valueArr[i].split(',');
2116: elements = document.getElementsByName(pairs[0]);
2117:
2118: for (var j = 0; j < elements.length; j++){
2119: elements[j].style.display = pairs[1];
2120: if (pairs[1] == "none"){
2121: var regex_id = /([_\\d]+)\$/;
2122: regex_id.exec(pairs[0]);
2123: document.getElementById("folding_btn"+RegExp.\$1).value = "Show";
2124: }
2125: }
2126: }
2127: }
2128: }
2129:
2130: function getTagList () {
2131:
2132: var stringToSearch = document.lonhomework.innerHTML;
2133:
2134: var ret = new Array();
2135: var regex_findBlock = /(foldblock_.*?)"/g;
2136: var tag_list = stringToSearch.match(regex_findBlock);
2137:
2138: if(tag_list != null){
2139: for(var i = 0; i < tag_list.length; i++){
2140: ret.push(tag_list[i].replace(/"/, ''));
2141: }
2142: }
2143: return ret;
2144: }
2145:
2146: function saveScrollPosition (resource) {
2147: var tag_list = getTagList();
2148:
2149: // we dont always want to jump to the first block
2150: // 170 is roughly above the "Problem Editing" header. we just want to save if the user scrolled down further than this
2151: if(\$(window).scrollTop() > 170){
2152: if(tag_list != null){
2153: var result;
2154: for(var i = 0; i < tag_list.length; i++){
2155: if(isElementInViewport(tag_list[i])){
2156: result += tag_list[i]+';';
2157: }
2158: }
2159: sessionStorage.setItem('anchor_'+resource, result);
2160: }
2161: } else {
2162: // we dont need to save zero, just delete the item to leave everything tidy
2163: sessionStorage.removeItem('anchor_'+resource);
2164: }
2165: }
2166:
2167: function restoreScrollPosition(resource){
2168:
2169: var elem = sessionStorage.getItem('anchor_'+resource);
2170: if(elem != null){
2171: var tag_list = elem.split(';');
2172: var elem_list;
2173:
2174: for(var i = 0; i < tag_list.length; i++){
2175: elem_list = document.getElementsByName(tag_list[i]);
2176:
2177: if(elem_list.length > 0){
2178: elem = elem_list[0];
2179: break;
2180: }
2181: }
2182: elem.scrollIntoView();
2183: }
2184: }
2185:
2186: function isElementInViewport(el) {
2187:
2188: // change to last element instead of first
2189: var elem = document.getElementsByName(el);
2190: var rect = elem[0].getBoundingClientRect();
2191:
2192: return (
2193: rect.top >= 0 &&
2194: rect.left >= 0 &&
2195: rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
2196: rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
2197: );
2198: }
2199:
2200: function autosize(depth){
2201: var cmInst = window['cm'+depth];
2202: var fitsizeButton = document.getElementById('fitsize'+depth);
2203:
2204: // is fixed size, switching to dynamic
2205: if (sessionStorage.getItem("autosized_"+depth) == null) {
2206: cmInst.setSize("","auto");
2207: fitsizeButton.value = "@{[&mt('Fixed size')]}";
2208: sessionStorage.setItem("autosized_"+depth, "yes");
2209:
2210: // is dynamic size, switching to fixed
2211: } else {
2212: cmInst.setSize("","300px");
2213: fitsizeButton.value = "@{[&mt('Dynamic size')]}";
2214: sessionStorage.removeItem("autosized_"+depth);
2215: }
2216: }
2217:
1.1248 raeburn 2218: $browse_or_search
1.1205 golterma 2219:
2220: // ]]>
2221: </script>
2222: COLORFULEDIT
2223: }
2224:
2225: sub xmleditor_js {
2226: return <<XMLEDIT
2227: <script type="text/javascript" src="/adm/jQuery/addons/jquery-scrolltofixed.js"></script>
2228: <script type="text/javascript">
2229: // <![CDATA[>
2230:
2231: function saveScrollPosition (resource) {
2232:
2233: var scrollPos = \$(window).scrollTop();
2234: sessionStorage.setItem(resource,scrollPos);
2235: }
2236:
2237: function restoreScrollPosition(resource){
2238:
2239: var scrollPos = sessionStorage.getItem(resource);
2240: \$(window).scrollTop(scrollPos);
2241: }
2242:
2243: // unless internet explorer
2244: if (!(window.navigator.appName == "Microsoft Internet Explorer" && (document.documentMode || document.compatMode))){
2245:
2246: \$(document).ready(function() {
2247: \$(".LC_edit_actionbar").scrollToFixed(\{zIndex: 100\});
2248: });
2249: }
2250:
2251: // inserts text at cursor position into codemirror (xml editor only)
2252: function insertText(text){
2253: cm.focus();
2254: var curPos = cm.getCursor();
2255: cm.replaceRange(text.replace(/ESCAPEDSCRIPT/g,'script'), {line: curPos.line,ch: curPos.ch});
2256: }
2257: // ]]>
2258: </script>
2259: XMLEDIT
2260: }
2261:
2262: sub insert_folding_button {
2263: my $curDepth = $Apache::lonxml::curdepth;
2264: my $lastresource = $env{'request.ambiguous'};
2265:
2266: return "<input type=\"button\" id=\"folding_btn_$curDepth\"
2267: value=\"".&mt('Hide')."\" onclick=\"fold_box('$curDepth','$lastresource')\">";
2268: }
2269:
1.1248 raeburn 2270: sub crsauthor_url {
2271: my ($url) = @_;
2272: if ($url eq '') {
2273: $url = $ENV{'REQUEST_URI'};
2274: }
2275: my ($cnum,$cdom);
2276: if ($env{'request.course.id'}) {
2277: my ($audom,$auname) = ($url =~ m{^/priv/($match_domain)/($match_name)/});
2278: if ($audom ne '' && $auname ne '') {
2279: if (($env{'course.'.$env{'request.course.id'}.'.num'} eq $auname) &&
2280: ($env{'course.'.$env{'request.course.id'}.'.domain'} eq $audom)) {
2281: $cnum = $auname;
2282: $cdom = $audom;
2283: }
2284: }
2285: }
2286: return ($cnum,$cdom);
2287: }
2288:
2289: sub import_crsauthor_form {
1.1400 raeburn 2290: my ($firstselectname,$secondselectname,$onchangefirst,$only,$suffix,$disabled) = @_;
1.1248 raeburn 2291: return (0) unless ($env{'request.course.id'});
2292: my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
2293: my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
2294: my $crshome = $env{'course.'.$env{'request.course.id'}.'.home'};
2295: return (0) unless (($cnum ne '') && ($cdom ne ''));
2296: my @ids=&Apache::lonnet::current_machine_ids();
1.1400 raeburn 2297: my ($output,$is_home,$toppath,%subdirs,%files,%selimport_menus,$include,$exclude);
1.1402 raeburn 2298:
1.1248 raeburn 2299: if (grep(/^\Q$crshome\E$/,@ids)) {
2300: $is_home = 1;
2301: }
1.1400 raeburn 2302: $toppath = "/priv/$cdom/$cnum";
2303: my $nonemptydir = 1;
2304: my $js_only;
2305: if ($only) {
2306: map { $include->{$_} = 1; } split(/\s*,\s*/,$only);
2307: $js_only = join(',',map { &js_escape($_); } sort(keys(%{$include})));
2308: }
2309: $exclude = &Apache::lonnet::priv_exclude();
1.1402 raeburn 2310: &Apache::lonnet::recursedirs($is_home,1,$include,$exclude,1,0,$toppath,'',\%subdirs,\%files);
1.1400 raeburn 2311: my $numdirs = scalar(keys(%files));
1.1248 raeburn 2312: my %lt = &Apache::lonlocal::texthash (
2313: fnam => 'Filename',
2314: dire => 'Directory',
1.1400 raeburn 2315: se => 'Select',
1.1248 raeburn 2316: );
1.1450 raeburn 2317: $output = '<label>'.$lt{'dire'}.': '.
1.1400 raeburn 2318: '<select id="'.$firstselectname.'" name="'.$firstselectname.'" '.
1.1402 raeburn 2319: 'onchange="populateCrsSelects(this.form,'."'$firstselectname','$secondselectname',1,'$js_only',0,1,0,0,0".');">'.
1.1400 raeburn 2320: '<option value="" selected="selected">'.$lt{'se'}.'</option>';
1.1402 raeburn 2321: if ($files{'/'}) {
2322: $output .= '<option value="/">/</option>'."\n";
2323: }
1.1400 raeburn 2324: foreach my $key (sort { lc($a) cmp lc($b) } (keys(%files))) {
1.1402 raeburn 2325: next if ($key eq '/');
1.1400 raeburn 2326: $output .= '<option value="'.$key.'">'.$key.'</option>'."\n";
2327: }
1.1450 raeburn 2328: $output .= '</select></label><br /><label>'."\n".
1.1402 raeburn 2329: $lt{'fnam'}.': <select id="'.$secondselectname.'" name="'.$secondselectname.'">'."\n".
1.1400 raeburn 2330: '<option value="" selected="selected"></option>'."\n".
1.1450 raeburn 2331: '</select></label>'."\n".
1.1402 raeburn 2332: '<input type="hidden" id="crsres_include_'.$suffix.'" value="'.$only.'" />';
1.1400 raeburn 2333: return ($numdirs,$output);
2334: }
2335:
2336: sub show_crsfiles_js {
2337: my $excluderef = &Apache::lonnet::priv_exclude();
2338: my $se = &js_escape(&mt('Select'));
2339: my $exclude;
2340: if (ref($excluderef) eq 'HASH') {
2341: $exclude = join(',', map { &js_escape($_); } sort(keys(%{$excluderef})));
2342: }
2343: my $js = <<"END";
2344:
2345:
1.1402 raeburn 2346: function populateCrsSelects (form,dirsel,filesel,exc,include,setdir,setfile,recurse,nonemptydir,addtopdir) {
1.1400 raeburn 2347: var relpath = '';
2348: if ((setfile) && (dirsel != null) && (dirsel != 'undefined') && (dirsel != '')) {
2349: var currdir = form.elements[dirsel].options[form.elements[dirsel].selectedIndex].value;
2350: if (currdir == '') {
2351: if ((filesel != null) && (filesel != 'undefined') && (filesel != '')) {
2352: selelem = form.elements[filesel];
2353: var j, numfiles = selelem.options.length -1;
2354: if (numfiles >=0) {
2355: for (j = numfiles; j >= 0; j--) {
2356: selelem.remove(j);
2357: }
2358: }
2359: if (selelem.options.length == 0) {
2360: selelem.options[selelem.options.length] = new Option('','');
2361: selelem.selectedIndex = 0;
1.1248 raeburn 2362: }
2363: }
1.1400 raeburn 2364: return;
2365: } else {
2366: relpath = encodeURIComponent(form.elements[dirsel].options[form.elements[dirsel].selectedIndex].value);
1.1248 raeburn 2367: }
2368: }
1.1400 raeburn 2369: var http = new XMLHttpRequest();
2370: var url = "/adm/courseauthor";
2371: var crsrole = "$env{'request.role'}";
2372: var exclude = '';
2373: if (exc) {
2374: exclude = '$exclude';
2375: }
1.1402 raeburn 2376: var params = "role=course&files=1&rec="+recurse+"&nonempty="+nonemptydir+"&exc="+exclude+"&inc="+include+"&addtop="+addtopdir+"&path="+relpath;
1.1400 raeburn 2377: http.open("POST", url, true);
2378: http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
2379: http.onreadystatechange = function() {
2380: if (http.readyState == 4 && http.status == 200) {
2381: var data = JSON.parse(http.responseText);
2382: var selelem;
2383: if ((setdir) && (dirsel != null) && (dirsel != 'undefined') && (dirsel != '')) {
2384: if (Array.isArray(data.dirs)) {
2385: selelem = form.elements[dirsel];
2386: var i, numdirs = selelem.options.length -1;
2387: if (numdirs >=0) {
2388: for (i = numdirs; i >= 0; i--) {
2389: selelem.remove(i);
2390: }
2391: }
2392: var len = data.dirs.length;
2393: if (len) {
1.1402 raeburn 2394: selelem.options[selelem.options.length] = new Option('$se','');
1.1400 raeburn 2395: var j;
2396: for (j = 0; j < len; j++) {
2397: selelem.options[selelem.options.length] = new Option(data.dirs[j],data.dirs[j]);
2398: }
2399: selelem.selectedIndex = 0;
2400: }
2401: if (!setfile) {
2402: if ((filesel != null) && (filesel != 'undefined') && (filesel != '')) {
2403: selelem = form.elements[filesel];
2404: var j, numfiles = selelem.options.length -1;
2405: if (numfiles >=0) {
2406: for (j = numfiles; j >= 0; j--) {
2407: selelem.remove(j);
2408: }
2409: }
2410: if (selelem.options.length == 0) {
2411: selelem.options[selelem.options.length] = new Option('','');
2412: selelem.selectedIndex = 0;
2413: }
2414: }
2415: }
2416: }
2417: }
2418: if ((setfile) && (filesel != null) && (filesel != 'undefined') && (filesel != '')) {
2419: selelem = form.elements[filesel];
2420: var i, numfiles = selelem.options.length -1;
2421: if (numfiles >=0) {
2422: for (i = numfiles; i >= 0; i--) {
2423: selelem.remove(i);
2424: }
2425: }
2426: var x;
2427: for (x in data.files) {
2428: if (Array.isArray(data.files[x])) {
2429: if (data.files[x].length > 1) {
2430: selelem.options[selelem.options.length] = new Option('$se','');
2431: }
2432: var len = data.files[x].length;
2433: if (len) {
2434: var k;
2435: for (k = 0; k < len; k++) {
2436: selelem.options[selelem.options.length] = new Option(data.files[x][k],data.files[x][k]);
2437: }
2438: selelem.selectedIndex = 0;
2439: }
2440: }
2441: }
2442: if (selelem.options.length == 0) {
2443: selelem.options[selelem.options.length] = new Option('','');
2444: selelem.selectedIndex = 0;
2445: }
1.1248 raeburn 2446: }
2447: }
2448: }
1.1400 raeburn 2449: http.send(params);
1.1248 raeburn 2450: }
1.1400 raeburn 2451: END
1.1248 raeburn 2452: }
2453:
1.1426 raeburn 2454: sub crsauthor_rights {
2455: my ($rightsfile,$path,$docroot,$cnum,$cdom) = @_;
2456: my $sourcerights = "$path/$rightsfile";
2457: my $now = time;
2458: if (!-e $sourcerights) {
2459: my $cid = $cdom.'_'.$cnum;
2460: if (!-e "$docroot/priv/$cdom") {
2461: mkdir("$docroot/priv/$cdom",0755);
2462: }
2463: if (!-e "$docroot/priv/$cdom/$cnum") {
2464: mkdir("$docroot/priv/$cdom/$cnum",0755);
2465: }
2466: if (open(my $fh,">$sourcerights")) {
2467: print $fh <<END;
2468: <accessrule effect="deny" realm="" type="course" role="" />
2469: <accessrule effect="allow" realm="$cid" type="course" role="" />
2470: END
2471: close($fh);
2472: }
2473: }
2474: if (!-e "$sourcerights.meta") {
2475: if (open(my $fh,">$sourcerights.meta")) {
2476: my $author=$env{'environment.firstname'}.' '.
2477: $env{'environment.middlename'}.' '.
2478: $env{'environment.lastname'}.' '.
2479: $env{'environment.generation'};
2480: $author =~ s/\s+$//;
2481: print $fh <<"END";
2482:
2483: <abstract></abstract>
2484: <author>$author</author>
2485: <authorspace>$cnum:$cdom</authorspace>
2486: <copyright>private</copyright>
2487: <creationdate>$now</creationdate>
2488: <customdistributionfile></customdistributionfile>
2489: <dependencies></dependencies>
2490: <domain>$cdom</domain>
2491: <highestgradelevel>0</highestgradelevel>
2492: <keywords></keywords>
1.1445 raeburn 2493: <language>notset</language>
1.1426 raeburn 2494: <lastrevisiondate>$now</lastrevisiondate>
2495: <lowestgradelevel>0</lowestgradelevel>
2496: <mime>rights</mime>
2497: <modifyinguser>$env{'user.name'}:$env{'user.domain'}</modifyinguser>
2498: <notes></notes>
2499: <obsolete></obsolete>
2500: <obsoletereplacement></obsoletereplacement>
2501: <owner>$cnum:$cdom</owner>
2502: <rule>deny:::course,allow:$cid::course</rule>
2503: <sourceavail></sourceavail>
2504: <standards></standards>
2505: <subject></subject>
2506: <title>Course Authoring Rights</title>
2507: END
2508: close($fh);
2509: }
2510: }
2511: return;
2512: }
2513:
1.565 albertel 2514: =pod
2515:
1.1420 raeburn 2516: =item * &iframe_wrapper_headjs()
2517:
1.1425 raeburn 2518: emits javascript containing two global vars to facilitate handling of resizing
2519: by code in iframe_wrapper_resizejs() used when an iframe is present in a page
2520: with standard LON-CAPA menus.
2521:
2522: =cut
2523:
1.1420 raeburn 2524: #
2525: # Where iframe is in use, if window.onload() executes before the custom resize function
2526: # has been defined (jQuery), two global javascript vars (LCnotready and LCresizedef)
2527: # are used to ensure document.ready() triggers a call to resize, so the iframe contents
2528: # do not obscure the Functions menu.
2529: #
2530:
2531: sub iframe_wrapper_headjs {
2532: return <<"ENDJS";
2533: <script type="text/javascript">
2534: // <![CDATA[
2535: var LCnotready = 0;
2536: var LCresizedef = 0;
2537: // ]]>
2538: </script>
2539:
2540: ENDJS
2541:
2542: }
2543:
2544: =pod
2545:
2546: =item * &iframe_wrapper_resizejs()
2547:
1.1425 raeburn 2548: emits javascript used to handle resizing for a page containing
2549: an iframe, to ensure that the iframe does not obscure any
2550: standard LON-CAPA menu items.
2551:
2552: =back
2553:
2554: =cut
2555:
1.1420 raeburn 2556: #
2557: # jQuery to use when iframe is in use and a page resize occurs.
2558: # This script will ensure that the iframe does not obscure any
2559: # standard LON-CAPA inline menus (primary, secondary, and/or
2560: # breadcrumbs and Functions menus. Expects javascript from
2561: # &iframe_wrapper_headjs() to be in head portion of the web page,
2562: # e.g., by inclusion in second arg passed to &start_page().
2563: #
2564:
2565: sub iframe_wrapper_resizejs {
2566: my $offset = 5;
2567: &get_unprocessed_cgi($ENV{'QUERY_STRING'},['inhibitmenu']);
2568: if (($env{'form.inhibitmenu'} eq 'yes') || ($env{'form.only_body'})) {
2569: $offset = 0;
2570: }
2571: return &Apache::lonhtmlcommon::scripttag(<<SCRIPT);
2572: \$(document).ready( function() {
2573: \$(window).unbind('resize').resize(function(){
2574: var header = null;
2575: var offset = $offset;
2576: var height = 0;
2577: var hdrtop = 0;
1.1421 raeburn 2578: if (\$('div.LC_menus_content:first').length) {
2579: if (\$('div.LC_menus_content:first').hasClass ("shown")) {
2580: header = \$('div.LC_menus_content:first');
1.1423 raeburn 2581: offset = 12;
1.1421 raeburn 2582: }
2583: } else if (\$('div.LC_head_subbox:first').length) {
1.1420 raeburn 2584: header = \$('div.LC_head_subbox:first');
2585: offset = 9;
2586: } else {
2587: if (\$('#LC_breadcrumbs').length) {
2588: header = \$('#LC_breadcrumbs');
2589: }
2590: }
2591: if (header != null && header.length) {
2592: height = header.height();
2593: hdrtop = header.position().top;
2594: }
2595: var pos = height + hdrtop + offset;
2596: \$('.LC_iframecontainer').css('top', pos);
2597: });
2598: LCresizedef = 1;
2599: if (LCnotready == 1) {
2600: LCnotready = 0;
2601: \$(window).trigger('resize');
2602: }
2603: });
2604: window.onload = function(){
2605: if (LCresizedef) {
2606: LCnotready = 0;
2607: \$(window).trigger('resize');
2608: } else {
2609: LCnotready = 1;
2610: }
2611: };
2612: SCRIPT
2613:
2614: }
2615:
2616: =pod
2617:
1.256 matthew 2618: =head1 Excel and CSV file utility routines
2619:
2620: =cut
2621:
2622: ###############################################################
2623: ###############################################################
2624:
2625: =pod
2626:
1.1162 raeburn 2627: =over 4
2628:
1.648 raeburn 2629: =item * &csv_translate($text)
1.37 matthew 2630:
1.185 www 2631: Translate $text to allow it to be output as a 'comma separated values'
1.37 matthew 2632: format.
2633:
2634: =cut
2635:
1.180 matthew 2636: ###############################################################
2637: ###############################################################
1.37 matthew 2638: sub csv_translate {
2639: my $text = shift;
2640: $text =~ s/\"/\"\"/g;
1.209 albertel 2641: $text =~ s/\n/ /g;
1.37 matthew 2642: return $text;
2643: }
1.180 matthew 2644:
2645: ###############################################################
2646: ###############################################################
2647:
2648: =pod
2649:
1.648 raeburn 2650: =item * &define_excel_formats()
1.180 matthew 2651:
2652: Define some commonly used Excel cell formats.
2653:
2654: Currently supported formats:
2655:
2656: =over 4
2657:
2658: =item header
2659:
2660: =item bold
2661:
2662: =item h1
2663:
2664: =item h2
2665:
2666: =item h3
2667:
1.256 matthew 2668: =item h4
2669:
2670: =item i
2671:
1.180 matthew 2672: =item date
2673:
2674: =back
2675:
2676: Inputs: $workbook
2677:
2678: Returns: $format, a hash reference.
2679:
1.1057 foxr 2680:
1.180 matthew 2681: =cut
2682:
2683: ###############################################################
2684: ###############################################################
2685: sub define_excel_formats {
2686: my ($workbook) = @_;
2687: my $format;
2688: $format->{'header'} = $workbook->add_format(bold => 1,
2689: bottom => 1,
2690: align => 'center');
2691: $format->{'bold'} = $workbook->add_format(bold=>1);
2692: $format->{'h1'} = $workbook->add_format(bold=>1, size=>18);
2693: $format->{'h2'} = $workbook->add_format(bold=>1, size=>16);
2694: $format->{'h3'} = $workbook->add_format(bold=>1, size=>14);
1.255 matthew 2695: $format->{'h4'} = $workbook->add_format(bold=>1, size=>12);
1.246 matthew 2696: $format->{'i'} = $workbook->add_format(italic=>1);
1.180 matthew 2697: $format->{'date'} = $workbook->add_format(num_format=>
1.207 matthew 2698: 'mm/dd/yyyy hh:mm:ss');
1.180 matthew 2699: return $format;
2700: }
2701:
2702: ###############################################################
2703: ###############################################################
1.113 bowersj2 2704:
2705: =pod
2706:
1.648 raeburn 2707: =item * &create_workbook()
1.255 matthew 2708:
2709: Create an Excel worksheet. If it fails, output message on the
2710: request object and return undefs.
2711:
2712: Inputs: Apache request object
2713:
2714: Returns (undef) on failure,
2715: Excel worksheet object, scalar with filename, and formats
2716: from &Apache::loncommon::define_excel_formats on success
2717:
2718: =cut
2719:
2720: ###############################################################
2721: ###############################################################
2722: sub create_workbook {
2723: my ($r) = @_;
2724: #
2725: # Create the excel spreadsheet
2726: my $filename = '/prtspool/'.
1.258 albertel 2727: $env{'user.name'}.'_'.$env{'user.domain'}.'_'.
1.255 matthew 2728: time.'_'.rand(1000000000).'.xls';
2729: my $workbook = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
2730: if (! defined($workbook)) {
2731: $r->log_error("Error creating excel spreadsheet $filename: $!");
1.928 bisitz 2732: $r->print(
2733: '<p class="LC_error">'
2734: .&mt('Problems occurred in creating the new Excel file.')
2735: .' '.&mt('This error has been logged.')
2736: .' '.&mt('Please alert your LON-CAPA administrator.')
2737: .'</p>'
2738: );
1.255 matthew 2739: return (undef);
2740: }
2741: #
1.1014 foxr 2742: $workbook->set_tempdir(LONCAPA::tempdir());
1.255 matthew 2743: #
2744: my $format = &Apache::loncommon::define_excel_formats($workbook);
2745: return ($workbook,$filename,$format);
2746: }
2747:
2748: ###############################################################
2749: ###############################################################
2750:
2751: =pod
2752:
1.648 raeburn 2753: =item * &create_text_file()
1.113 bowersj2 2754:
1.542 raeburn 2755: Create a file to write to and eventually make available to the user.
1.256 matthew 2756: If file creation fails, outputs an error message on the request object and
2757: return undefs.
1.113 bowersj2 2758:
1.256 matthew 2759: Inputs: Apache request object, and file suffix
1.113 bowersj2 2760:
1.256 matthew 2761: Returns (undef) on failure,
2762: Filehandle and filename on success.
1.113 bowersj2 2763:
2764: =cut
2765:
1.256 matthew 2766: ###############################################################
2767: ###############################################################
2768: sub create_text_file {
2769: my ($r,$suffix) = @_;
2770: if (! defined($suffix)) { $suffix = 'txt'; };
2771: my $fh;
2772: my $filename = '/prtspool/'.
1.258 albertel 2773: $env{'user.name'}.'_'.$env{'user.domain'}.'_'.
1.256 matthew 2774: time.'_'.rand(1000000000).'.'.$suffix;
2775: $fh = Apache::File->new('>/home/httpd'.$filename);
2776: if (! defined($fh)) {
2777: $r->log_error("Couldn't open $filename for output $!");
1.928 bisitz 2778: $r->print(
2779: '<p class="LC_error">'
2780: .&mt('Problems occurred in creating the output file.')
2781: .' '.&mt('This error has been logged.')
2782: .' '.&mt('Please alert your LON-CAPA administrator.')
2783: .'</p>'
2784: );
1.113 bowersj2 2785: }
1.256 matthew 2786: return ($fh,$filename)
1.113 bowersj2 2787: }
2788:
2789:
1.256 matthew 2790: =pod
1.113 bowersj2 2791:
2792: =back
2793:
2794: =cut
1.37 matthew 2795:
2796: ###############################################################
1.33 matthew 2797: ## Home server <option> list generating code ##
2798: ###############################################################
1.35 matthew 2799:
1.169 www 2800: # ------------------------------------------
2801:
2802: sub domain_select {
1.1471 ! raeburn 2803: my ($name,$value,$multiple,$incdoms,$excdoms,$id)=@_;
1.1289 raeburn 2804: my @possdoms;
2805: if (ref($incdoms) eq 'ARRAY') {
2806: @possdoms = @{$incdoms};
2807: } else {
2808: @possdoms = &Apache::lonnet::all_domains();
2809: }
2810:
1.169 www 2811: my %domains=map {
1.514 albertel 2812: $_ => $_.' '. &Apache::lonnet::domain($_,'description')
1.1289 raeburn 2813: } @possdoms;
2814:
2815: if ((ref($excdoms) eq 'ARRAY') && (@{$excdoms} > 0)) {
2816: foreach my $dom (@{$excdoms}) {
2817: delete($domains{$dom});
2818: }
2819: }
2820:
1.169 www 2821: if ($multiple) {
2822: $domains{''}=&mt('Any domain');
1.550 albertel 2823: $domains{'select_form_order'} = [sort {lc($a) cmp lc($b) } (keys(%domains))];
1.1471 ! raeburn 2824: return &multiple_select_form($name,$value,4,\%domains,undef,$id);
1.169 www 2825: } else {
1.550 albertel 2826: $domains{'select_form_order'} = [sort {lc($a) cmp lc($b) } (keys(%domains))];
1.1471 ! raeburn 2827: return &select_form($name,$value,\%domains,'','',$id);
1.169 www 2828: }
2829: }
2830:
1.282 albertel 2831: #-------------------------------------------
2832:
2833: =pod
2834:
1.519 raeburn 2835: =head1 Routines for form select boxes
2836:
2837: =over 4
2838:
1.1471 ! raeburn 2839: =item * &multiple_select_form($name,$value,$size,$hash,$order,$id)
1.282 albertel 2840:
2841: Returns a string containing a <select> element int multiple mode
2842:
2843:
2844: Args:
2845: $name - name of the <select> element
1.506 raeburn 2846: $value - scalar or array ref of values that should already be selected
1.282 albertel 2847: $size - number of rows long the select element is
1.283 albertel 2848: $hash - the elements should be 'option' => 'shown text'
1.282 albertel 2849: (shown text should already have been &mt())
1.506 raeburn 2850: $order - (optional) array ref of the order to show the elements in
1.1471 ! raeburn 2851: $id = (optional) id for <select> element
1.283 albertel 2852:
1.282 albertel 2853: =cut
2854:
2855: #-------------------------------------------
1.169 www 2856: sub multiple_select_form {
1.1471 ! raeburn 2857: my ($name,$value,$size,$hash,$order,$id)=@_;
1.169 www 2858: my %selected = map { $_ => 1 } ref($value)?@{$value}:($value);
2859: my $output='';
1.191 matthew 2860: if (! defined($size)) {
2861: $size = 4;
1.283 albertel 2862: if (scalar(keys(%$hash))<4) {
2863: $size = scalar(keys(%$hash));
1.191 matthew 2864: }
2865: }
1.1471 ! raeburn 2866: if ($id ne '') {
! 2867: $id = ' id="'.$id.'"';
! 2868: }
! 2869: $output.="\n".'<select name="'.$name.'" size="'.$size.'" multiple="multiple"'.$id.'>';
1.501 banghart 2870: my @order;
1.506 raeburn 2871: if (ref($order) eq 'ARRAY') {
2872: @order = @{$order};
2873: } else {
2874: @order = sort(keys(%$hash));
1.501 banghart 2875: }
2876: if (exists($$hash{'select_form_order'})) {
2877: @order = @{$$hash{'select_form_order'}};
2878: }
2879:
1.284 albertel 2880: foreach my $key (@order) {
1.356 albertel 2881: $output.='<option value="'.&HTML::Entities::encode($key,'"<>&').'" ';
1.284 albertel 2882: $output.='selected="selected" ' if ($selected{$key});
2883: $output.='>'.$hash->{$key}."</option>\n";
1.169 www 2884: }
2885: $output.="</select>\n";
2886: return $output;
2887: }
2888:
1.88 www 2889: #-------------------------------------------
2890:
2891: =pod
2892:
1.1471 ! raeburn 2893: =item * &select_form($defdom,$name,$hashref,$onchange,$readonly,$id)
1.88 www 2894:
2895: Returns a string containing a <select name='$name' size='1'> form to
1.970 raeburn 2896: allow a user to select options from a ref to a hash containing:
2897: option_name => displayed text. An optional $onchange can include
1.1254 raeburn 2898: a javascript onchange item, e.g., onchange="this.form.submit();".
2899: An optional arg -- $readonly -- if true will cause the select form
2900: to be disabled, e.g., for the case where an instructor has a section-
1.1471 ! raeburn 2901: specific role, and is viewing/modifying parameters. An optional arg
! 2902: -- $id -- will be used as the id attribute of the select element.
1.970 raeburn 2903:
1.88 www 2904: See lonrights.pm for an example invocation and use.
2905:
2906: =cut
2907:
2908: #-------------------------------------------
2909: sub select_form {
1.1471 ! raeburn 2910: my ($def,$name,$hashref,$onchange,$readonly,$id) = @_;
1.970 raeburn 2911: return unless (ref($hashref) eq 'HASH');
2912: if ($onchange) {
2913: $onchange = ' onchange="'.$onchange.'"';
2914: }
1.1228 raeburn 2915: my $disabled;
2916: if ($readonly) {
2917: $disabled = ' disabled="disabled"';
2918: }
1.1471 ! raeburn 2919: if ($id ne '') {
! 2920: $id = ' id="'.$id.'"';
! 2921: }
! 2922: my $selectform = "<select name=\"$name\" size=\"1\"$onchange$disabled$id>\n";
1.128 albertel 2923: my @keys;
1.970 raeburn 2924: if (exists($hashref->{'select_form_order'})) {
2925: @keys=@{$hashref->{'select_form_order'}};
1.128 albertel 2926: } else {
1.970 raeburn 2927: @keys=sort(keys(%{$hashref}));
1.128 albertel 2928: }
1.356 albertel 2929: foreach my $key (@keys) {
2930: $selectform.=
2931: '<option value="'.&HTML::Entities::encode($key,'"<>&').'" '.
2932: ($key eq $def ? 'selected="selected" ' : '').
1.970 raeburn 2933: ">".$hashref->{$key}."</option>\n";
1.88 www 2934: }
2935: $selectform.="</select>";
2936: return $selectform;
2937: }
2938:
1.475 www 2939: # For display filters
2940:
2941: sub display_filter {
1.1074 raeburn 2942: my ($context) = @_;
1.475 www 2943: if (!$env{'form.show'}) { $env{'form.show'}=10; }
1.477 www 2944: if (!$env{'form.displayfilter'}) { $env{'form.displayfilter'}='currentfolder'; }
1.1074 raeburn 2945: my $phraseinput = 'hidden';
2946: my $includeinput = 'hidden';
2947: my ($checked,$includetypestext);
2948: if ($env{'form.displayfilter'} eq 'containing') {
2949: $phraseinput = 'text';
2950: if ($context eq 'parmslog') {
2951: $includeinput = 'checkbox';
2952: if ($env{'form.includetypes'}) {
2953: $checked = ' checked="checked"';
2954: }
2955: $includetypestext = &mt('Include parameter types');
2956: }
2957: } else {
2958: $includetypestext = ' ';
2959: }
2960: my ($additional,$secondid,$thirdid);
2961: if ($context eq 'parmslog') {
2962: $additional =
2963: '<label><input type="'.$includeinput.'" name="includetypes"'.
2964: $checked.' name="includetypes" value="1" id="includetypes" />'.
2965: ' <span id="includetypestext">'.$includetypestext.'</span>'.
2966: '</label>';
2967: $secondid = 'includetypes';
2968: $thirdid = 'includetypestext';
2969: }
2970: my $onchange = "javascript:toggleHistoryOptions(this,'containingphrase','$context',
2971: '$secondid','$thirdid')";
2972: return '<span class="LC_nobreak"><label>'.&mt('Records: [_1]',
1.1471 ! raeburn 2973: &Apache::lonmeta::selectbox('show',$env{'form.show'},'','',undef,
1.475 www 2974: (&mt('all'),10,20,50,100,1000,10000))).
1.714 bisitz 2975: '</label></span> <span class="LC_nobreak">'.
1.1074 raeburn 2976: &mt('Filter: [_1]',
1.477 www 2977: &select_form($env{'form.displayfilter'},
2978: 'displayfilter',
1.970 raeburn 2979: {'currentfolder' => 'Current folder/page',
1.477 www 2980: 'containing' => 'Containing phrase',
1.1074 raeburn 2981: 'none' => 'None'},$onchange)).' '.
2982: '<input type="'.$phraseinput.'" name="containingphrase" id="containingphrase" size="30" value="'.
2983: &HTML::Entities::encode($env{'form.containingphrase'}).
2984: '" />'.$additional;
2985: }
2986:
2987: sub display_filter_js {
2988: my $includetext = &mt('Include parameter types');
2989: return <<"ENDJS";
2990:
2991: function toggleHistoryOptions(setter,firstid,context,secondid,thirdid) {
2992: var firstType = 'hidden';
2993: if (setter.options[setter.selectedIndex].value == 'containing') {
2994: firstType = 'text';
2995: }
2996: firstObject = document.getElementById(firstid);
2997: if (typeof(firstObject) == 'object') {
2998: if (firstObject.type != firstType) {
2999: changeInputType(firstObject,firstType);
3000: }
3001: }
3002: if (context == 'parmslog') {
3003: var secondType = 'hidden';
3004: if (firstType == 'text') {
3005: secondType = 'checkbox';
3006: }
3007: secondObject = document.getElementById(secondid);
3008: if (typeof(secondObject) == 'object') {
3009: if (secondObject.type != secondType) {
3010: changeInputType(secondObject,secondType);
3011: }
3012: }
3013: var textItem = document.getElementById(thirdid);
3014: var currtext = textItem.innerHTML;
3015: var newtext;
3016: if (firstType == 'text') {
3017: newtext = '$includetext';
3018: } else {
3019: newtext = ' ';
3020: }
3021: if (currtext != newtext) {
3022: textItem.innerHTML = newtext;
3023: }
3024: }
3025: return;
3026: }
3027:
3028: function changeInputType(oldObject,newType) {
3029: var newObject = document.createElement('input');
3030: newObject.type = newType;
3031: if (oldObject.size) {
3032: newObject.size = oldObject.size;
3033: }
3034: if (oldObject.value) {
3035: newObject.value = oldObject.value;
3036: }
3037: if (oldObject.name) {
3038: newObject.name = oldObject.name;
3039: }
3040: if (oldObject.id) {
3041: newObject.id = oldObject.id;
3042: }
3043: oldObject.parentNode.replaceChild(newObject,oldObject);
3044: return;
3045: }
3046:
3047: ENDJS
1.475 www 3048: }
3049:
1.167 www 3050: sub gradeleveldescription {
3051: my $gradelevel=shift;
3052: my %gradelevels=(0 => 'Not specified',
3053: 1 => 'Grade 1',
3054: 2 => 'Grade 2',
3055: 3 => 'Grade 3',
3056: 4 => 'Grade 4',
3057: 5 => 'Grade 5',
3058: 6 => 'Grade 6',
3059: 7 => 'Grade 7',
3060: 8 => 'Grade 8',
3061: 9 => 'Grade 9',
3062: 10 => 'Grade 10',
3063: 11 => 'Grade 11',
3064: 12 => 'Grade 12',
3065: 13 => 'Grade 13',
3066: 14 => '100 Level',
3067: 15 => '200 Level',
3068: 16 => '300 Level',
3069: 17 => '400 Level',
3070: 18 => 'Graduate Level');
3071: return &mt($gradelevels{$gradelevel});
3072: }
3073:
1.163 www 3074: sub select_level_form {
1.1471 ! raeburn 3075: my ($deflevel,$name,$id)=@_;
! 3076: if ($id ne '') {
! 3077: $id = ' id="'.$id.'"';
! 3078: }
1.163 www 3079: unless ($deflevel) { $deflevel=0; }
1.1471 ! raeburn 3080: my $selectform = "<select name=\"$name\" size=\"1\"$id>\n";
1.167 www 3081: for (my $i=0; $i<=18; $i++) {
3082: $selectform.="<option value=\"$i\" ".
1.253 albertel 3083: ($i==$deflevel ? 'selected="selected" ' : '').
1.167 www 3084: ">".&gradeleveldescription($i)."</option>\n";
3085: }
3086: $selectform.="</select>";
3087: return $selectform;
1.163 www 3088: }
1.167 www 3089:
1.35 matthew 3090: #-------------------------------------------
3091:
1.45 matthew 3092: =pod
3093:
1.1453 raeburn 3094: =item * &select_dom_form($defdom,$name,$includeempty,$showdomdesc,$onchange,$incdoms,$excdoms,$disabled,$id)
1.35 matthew 3095:
3096: Returns a string containing a <select name='$name' size='1'> form to
3097: allow a user to select the domain to preform an operation in.
3098: See loncreateuser.pm for an example invocation and use.
3099:
1.90 www 3100: If the $includeempty flag is set, it also includes an empty choice ("no domain
3101: selected");
3102:
1.743 raeburn 3103: If the $showdomdesc flag is set, the domain name is followed by the domain description.
3104:
1.910 raeburn 3105: 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.
3106:
1.1121 raeburn 3107: The optional $incdoms is a reference to an array of domains which will be the only available options.
3108:
3109: The optional $excdoms is a reference to an array of domains which will be excluded from the available options.
1.563 raeburn 3110:
1.1256 raeburn 3111: The optional $disabled argument, if true, adds the disabled attribute to the select tag.
3112:
1.1453 raeburn 3113: The option $id argument is the value (if any) to set as the (unique) id attribute for the select tag.
3114:
1.35 matthew 3115: =cut
3116:
3117: #-------------------------------------------
1.34 matthew 3118: sub select_dom_form {
1.1453 raeburn 3119: my ($defdom,$name,$includeempty,$showdomdesc,$onchange,$incdoms,$excdoms,$disabled,$id) = @_;
1.872 raeburn 3120: if ($onchange) {
1.874 raeburn 3121: $onchange = ' onchange="'.$onchange.'"';
1.743 raeburn 3122: }
1.1256 raeburn 3123: if ($disabled) {
3124: $disabled = ' disabled="disabled"';
3125: }
1.1453 raeburn 3126: if ($id ne '') {
3127: $id = ' id="'.$id.'"';
3128: }
1.1121 raeburn 3129: my (@domains,%exclude);
1.910 raeburn 3130: if (ref($incdoms) eq 'ARRAY') {
3131: @domains = sort {lc($a) cmp lc($b)} (@{$incdoms});
3132: } else {
3133: @domains = sort {lc($a) cmp lc($b)} (&Apache::lonnet::all_domains());
3134: }
1.90 www 3135: if ($includeempty) { @domains=('',@domains); }
1.1121 raeburn 3136: if (ref($excdoms) eq 'ARRAY') {
3137: map { $exclude{$_} = 1; } @{$excdoms};
3138: }
1.1453 raeburn 3139: my $selectdomain = "<select name=\"$name\" size=\"1\"$onchange$disabled$id>\n";
1.356 albertel 3140: foreach my $dom (@domains) {
1.1121 raeburn 3141: next if ($exclude{$dom});
1.356 albertel 3142: $selectdomain.="<option value=\"$dom\" ".
1.563 raeburn 3143: ($dom eq $defdom ? 'selected="selected" ' : '').'>'.$dom;
3144: if ($showdomdesc) {
3145: if ($dom ne '') {
3146: my $domdesc = &Apache::lonnet::domain($dom,'description');
3147: if ($domdesc ne '') {
3148: $selectdomain .= ' ('.$domdesc.')';
3149: }
3150: }
3151: }
3152: $selectdomain .= "</option>\n";
1.34 matthew 3153: }
3154: $selectdomain.="</select>";
3155: return $selectdomain;
3156: }
3157:
1.35 matthew 3158: #-------------------------------------------
3159:
1.45 matthew 3160: =pod
3161:
1.648 raeburn 3162: =item * &home_server_form_item($domain,$name,$defaultflag)
1.35 matthew 3163:
1.586 raeburn 3164: input: 4 arguments (two required, two optional) -
3165: $domain - domain of new user
3166: $name - name of form element
3167: $default - Value of 'default' causes a default item to be first
3168: option, and selected by default.
3169: $hide - Value of 'hide' causes hiding of the name of the server,
3170: if 1 server found, or default, if 0 found.
1.594 raeburn 3171: output: returns 2 items:
1.586 raeburn 3172: (a) form element which contains either:
3173: (i) <select name="$name">
3174: <option value="$hostid1">$hostid $servers{$hostid}</option>
3175: <option value="$hostid2">$hostid $servers{$hostid}</option>
3176: </select>
3177: form item if there are multiple library servers in $domain, or
3178: (ii) an <input type="hidden" name="$name" value="$hostid" /> form item
3179: if there is only one library server in $domain.
3180:
3181: (b) number of library servers found.
3182:
3183: See loncreateuser.pm for example of use.
1.35 matthew 3184:
3185: =cut
3186:
3187: #-------------------------------------------
1.586 raeburn 3188: sub home_server_form_item {
3189: my ($domain,$name,$default,$hide) = @_;
1.513 albertel 3190: my %servers = &Apache::lonnet::get_servers($domain,'library');
1.586 raeburn 3191: my $result;
3192: my $numlib = keys(%servers);
3193: if ($numlib > 1) {
3194: $result .= '<select name="'.$name.'" />'."\n";
3195: if ($default) {
1.804 bisitz 3196: $result .= '<option value="default" selected="selected">'.&mt('default').
1.586 raeburn 3197: '</option>'."\n";
3198: }
3199: foreach my $hostid (sort(keys(%servers))) {
3200: $result.= '<option value="'.$hostid.'">'.
3201: $hostid.' '.$servers{$hostid}."</option>\n";
3202: }
3203: $result .= '</select>'."\n";
3204: } elsif ($numlib == 1) {
3205: my $hostid;
3206: foreach my $item (keys(%servers)) {
3207: $hostid = $item;
3208: }
3209: $result .= '<input type="hidden" name="'.$name.'" value="'.
3210: $hostid.'" />';
3211: if (!$hide) {
3212: $result .= $hostid.' '.$servers{$hostid};
3213: }
3214: $result .= "\n";
3215: } elsif ($default) {
3216: $result .= '<input type="hidden" name="'.$name.
3217: '" value="default" />';
3218: if (!$hide) {
3219: $result .= &mt('default');
3220: }
3221: $result .= "\n";
1.33 matthew 3222: }
1.586 raeburn 3223: return ($result,$numlib);
1.33 matthew 3224: }
1.112 bowersj2 3225:
3226: =pod
3227:
1.534 albertel 3228: =back
3229:
1.112 bowersj2 3230: =cut
1.87 matthew 3231:
3232: ###############################################################
1.112 bowersj2 3233: ## Decoding User Agent ##
1.87 matthew 3234: ###############################################################
3235:
3236: =pod
3237:
1.112 bowersj2 3238: =head1 Decoding the User Agent
3239:
3240: =over 4
3241:
3242: =item * &decode_user_agent()
1.87 matthew 3243:
3244: Inputs: $r
3245:
3246: Outputs:
3247:
3248: =over 4
3249:
1.112 bowersj2 3250: =item * $httpbrowser
1.87 matthew 3251:
1.112 bowersj2 3252: =item * $clientbrowser
1.87 matthew 3253:
1.112 bowersj2 3254: =item * $clientversion
1.87 matthew 3255:
1.112 bowersj2 3256: =item * $clientmathml
1.87 matthew 3257:
1.112 bowersj2 3258: =item * $clientunicode
1.87 matthew 3259:
1.112 bowersj2 3260: =item * $clientos
1.87 matthew 3261:
1.1137 raeburn 3262: =item * $clientmobile
3263:
1.1141 raeburn 3264: =item * $clientinfo
3265:
1.1194 raeburn 3266: =item * $clientosversion
3267:
1.87 matthew 3268: =back
3269:
1.157 matthew 3270: =back
3271:
1.87 matthew 3272: =cut
3273:
3274: ###############################################################
3275: ###############################################################
3276: sub decode_user_agent {
1.247 albertel 3277: my ($r)=@_;
1.87 matthew 3278: my @browsertype=split(/\&/,$Apache::lonnet::perlvar{"lonBrowsDet"});
3279: my %mathcap=split(/\&/,$$Apache::lonnet::perlvar{"lonMathML"});
3280: my $httpbrowser=$ENV{"HTTP_USER_AGENT"};
1.247 albertel 3281: if (!$httpbrowser && $r) { $httpbrowser=$r->header_in('User-Agent'); }
1.87 matthew 3282: my $clientbrowser='unknown';
3283: my $clientversion='0';
3284: my $clientmathml='';
3285: my $clientunicode='0';
1.1137 raeburn 3286: my $clientmobile=0;
1.1194 raeburn 3287: my $clientosversion='';
1.87 matthew 3288: for (my $i=0;$i<=$#browsertype;$i++) {
1.1193 raeburn 3289: my ($bname,$match,$notmatch,$vreg,$minv,$univ)=split(/\%/,$browsertype[$i]);
1.87 matthew 3290: if (($httpbrowser=~/$match/i) && ($httpbrowser!~/$notmatch/i)) {
3291: $clientbrowser=$bname;
3292: $httpbrowser=~/$vreg/i;
3293: $clientversion=$1;
3294: $clientmathml=($clientversion>=$minv);
3295: $clientunicode=($clientversion>=$univ);
3296: }
3297: }
3298: my $clientos='unknown';
1.1141 raeburn 3299: my $clientinfo;
1.87 matthew 3300: if (($httpbrowser=~/linux/i) ||
3301: ($httpbrowser=~/unix/i) ||
3302: ($httpbrowser=~/ux/i) ||
3303: ($httpbrowser=~/solaris/i)) { $clientos='unix'; }
3304: if (($httpbrowser=~/vax/i) ||
3305: ($httpbrowser=~/vms/i)) { $clientos='vms'; }
3306: if ($httpbrowser=~/next/i) { $clientos='next'; }
3307: if (($httpbrowser=~/mac/i) ||
3308: ($httpbrowser=~/powerpc/i)) { $clientos='mac'; }
1.1194 raeburn 3309: if ($httpbrowser=~/win/i) {
3310: $clientos='win';
3311: if ($httpbrowser =~/Windows\s+NT\s+(\d+\.\d+)/i) {
3312: $clientosversion = $1;
3313: }
3314: }
1.87 matthew 3315: if ($httpbrowser=~/embed/i) { $clientos='pda'; }
1.1137 raeburn 3316: if ($httpbrowser=~/(Android|iPod|iPad|iPhone|webOS|Blackberry|Windows Phone|Opera m(?:ob|in)|Fennec)/i) {
3317: $clientmobile=lc($1);
3318: }
1.1141 raeburn 3319: if ($httpbrowser=~ m{Firefox/(\d+\.\d+)}) {
3320: $clientinfo = 'firefox-'.$1;
3321: } elsif ($httpbrowser=~ m{chromeframe/(\d+\.\d+)\.}) {
3322: $clientinfo = 'chromeframe-'.$1;
3323: }
1.87 matthew 3324: return ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,
1.1194 raeburn 3325: $clientunicode,$clientos,$clientmobile,$clientinfo,
3326: $clientosversion);
1.87 matthew 3327: }
3328:
1.32 matthew 3329: ###############################################################
3330: ## Authentication changing form generation subroutines ##
3331: ###############################################################
3332: ##
3333: ## All of the authform_xxxxxxx subroutines take their inputs in a
3334: ## hash, and have reasonable default values.
3335: ##
3336: ## formname = the name given in the <form> tag.
1.35 matthew 3337: #-------------------------------------------
3338:
1.45 matthew 3339: =pod
3340:
1.112 bowersj2 3341: =head1 Authentication Routines
3342:
3343: =over 4
3344:
1.648 raeburn 3345: =item * &authform_xxxxxx()
1.35 matthew 3346:
3347: The authform_xxxxxx subroutines provide javascript and html forms which
3348: handle some of the conveniences required for authentication forms.
3349: This is not an optimal method, but it works.
3350:
3351: =over 4
3352:
1.112 bowersj2 3353: =item * authform_header
1.35 matthew 3354:
1.112 bowersj2 3355: =item * authform_authorwarning
1.35 matthew 3356:
1.112 bowersj2 3357: =item * authform_nochange
1.35 matthew 3358:
1.112 bowersj2 3359: =item * authform_kerberos
1.35 matthew 3360:
1.112 bowersj2 3361: =item * authform_internal
1.35 matthew 3362:
1.112 bowersj2 3363: =item * authform_filesystem
1.35 matthew 3364:
1.1310 raeburn 3365: =item * authform_lti
3366:
1.35 matthew 3367: =back
3368:
1.648 raeburn 3369: See loncreateuser.pm for invocation and use examples.
1.157 matthew 3370:
1.35 matthew 3371: =cut
3372:
3373: #-------------------------------------------
1.32 matthew 3374: sub authform_header{
3375: my %in = (
3376: formname => 'cu',
1.80 albertel 3377: kerb_def_dom => '',
1.32 matthew 3378: @_,
3379: );
3380: $in{'formname'} = 'document.' . $in{'formname'};
3381: my $result='';
1.80 albertel 3382:
3383: #---------------------------------------------- Code for upper case translation
3384: my $Javascript_toUpperCase;
3385: unless ($in{kerb_def_dom}) {
3386: $Javascript_toUpperCase =<<"END";
3387: switch (choice) {
3388: case 'krb': currentform.elements[choicearg].value =
3389: currentform.elements[choicearg].value.toUpperCase();
3390: break;
3391: default:
3392: }
3393: END
3394: } else {
3395: $Javascript_toUpperCase = "";
3396: }
3397:
1.165 raeburn 3398: my $radioval = "'nochange'";
1.591 raeburn 3399: if (defined($in{'curr_authtype'})) {
3400: if ($in{'curr_authtype'} ne '') {
3401: $radioval = "'".$in{'curr_authtype'}."arg'";
3402: }
1.174 matthew 3403: }
1.165 raeburn 3404: my $argfield = 'null';
1.591 raeburn 3405: if (defined($in{'mode'})) {
1.165 raeburn 3406: if ($in{'mode'} eq 'modifycourse') {
1.591 raeburn 3407: if (defined($in{'curr_autharg'})) {
3408: if ($in{'curr_autharg'} ne '') {
1.165 raeburn 3409: $argfield = "'$in{'curr_autharg'}'";
3410: }
3411: }
3412: }
3413: }
3414:
1.32 matthew 3415: $result.=<<"END";
3416: var current = new Object();
1.165 raeburn 3417: current.radiovalue = $radioval;
3418: current.argfield = $argfield;
1.32 matthew 3419:
3420: function changed_radio(choice,currentform) {
3421: var choicearg = choice + 'arg';
3422: // If a radio button in changed, we need to change the argfield
3423: if (current.radiovalue != choice) {
3424: current.radiovalue = choice;
3425: if (current.argfield != null) {
3426: currentform.elements[current.argfield].value = '';
3427: }
3428: if (choice == 'nochange') {
3429: current.argfield = null;
3430: } else {
3431: current.argfield = choicearg;
3432: switch(choice) {
3433: case 'krb':
3434: currentform.elements[current.argfield].value =
3435: "$in{'kerb_def_dom'}";
3436: break;
3437: default:
3438: break;
3439: }
3440: }
3441: }
3442: return;
3443: }
1.22 www 3444:
1.32 matthew 3445: function changed_text(choice,currentform) {
3446: var choicearg = choice + 'arg';
3447: if (currentform.elements[choicearg].value !='') {
1.80 albertel 3448: $Javascript_toUpperCase
1.32 matthew 3449: // clear old field
3450: if ((current.argfield != choicearg) && (current.argfield != null)) {
3451: currentform.elements[current.argfield].value = '';
3452: }
3453: current.argfield = choicearg;
3454: }
3455: set_auth_radio_buttons(choice,currentform);
3456: return;
1.20 www 3457: }
1.32 matthew 3458:
3459: function set_auth_radio_buttons(newvalue,currentform) {
1.986 raeburn 3460: var numauthchoices = currentform.login.length;
3461: if (typeof numauthchoices == "undefined") {
3462: return;
3463: }
1.32 matthew 3464: var i=0;
1.986 raeburn 3465: while (i < numauthchoices) {
1.32 matthew 3466: if (currentform.login[i].value == newvalue) { break; }
3467: i++;
3468: }
1.986 raeburn 3469: if (i == numauthchoices) {
1.32 matthew 3470: return;
3471: }
3472: current.radiovalue = newvalue;
3473: currentform.login[i].checked = true;
3474: return;
3475: }
3476: END
3477: return $result;
3478: }
3479:
1.1106 raeburn 3480: sub authform_authorwarning {
1.32 matthew 3481: my $result='';
1.144 matthew 3482: $result='<i>'.
3483: &mt('As a general rule, only authors or co-authors should be '.
3484: 'filesystem authenticated '.
3485: '(which allows access to the server filesystem).')."</i>\n";
1.32 matthew 3486: return $result;
3487: }
3488:
1.1106 raeburn 3489: sub authform_nochange {
1.32 matthew 3490: my %in = (
3491: formname => 'document.cu',
3492: kerb_def_dom => 'MSU.EDU',
3493: @_,
3494: );
1.1106 raeburn 3495: my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.586 raeburn 3496: my $result;
1.1104 raeburn 3497: if (!$authnum) {
1.1105 raeburn 3498: $result = &mt('Under your current role you are not permitted to change login settings for this user');
1.586 raeburn 3499: } else {
3500: $result = '<label>'.&mt('[_1] Do not change login data',
3501: '<input type="radio" name="login" value="nochange" '.
3502: 'checked="checked" onclick="'.
1.281 albertel 3503: "javascript:changed_radio('nochange',$in{'formname'});".'" />').
3504: '</label>';
1.586 raeburn 3505: }
1.32 matthew 3506: return $result;
3507: }
3508:
1.591 raeburn 3509: sub authform_kerberos {
1.32 matthew 3510: my %in = (
3511: formname => 'document.cu',
3512: kerb_def_dom => 'MSU.EDU',
1.80 albertel 3513: kerb_def_auth => 'krb4',
1.32 matthew 3514: @_,
3515: );
1.586 raeburn 3516: my ($check4,$check5,$krbcheck,$krbarg,$krbver,$result,$authtype,
1.1259 raeburn 3517: $autharg,$jscall,$disabled);
1.1106 raeburn 3518: my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.80 albertel 3519: if ($in{'kerb_def_auth'} eq 'krb5') {
1.772 bisitz 3520: $check5 = ' checked="checked"';
1.80 albertel 3521: } else {
1.772 bisitz 3522: $check4 = ' checked="checked"';
1.80 albertel 3523: }
1.1259 raeburn 3524: if ($in{'readonly'}) {
3525: $disabled = ' disabled="disabled"';
3526: }
1.165 raeburn 3527: $krbarg = $in{'kerb_def_dom'};
1.591 raeburn 3528: if (defined($in{'curr_authtype'})) {
3529: if ($in{'curr_authtype'} eq 'krb') {
1.772 bisitz 3530: $krbcheck = ' checked="checked"';
1.623 raeburn 3531: if (defined($in{'mode'})) {
3532: if ($in{'mode'} eq 'modifyuser') {
3533: $krbcheck = '';
3534: }
3535: }
1.591 raeburn 3536: if (defined($in{'curr_kerb_ver'})) {
3537: if ($in{'curr_krb_ver'} eq '5') {
1.772 bisitz 3538: $check5 = ' checked="checked"';
1.591 raeburn 3539: $check4 = '';
3540: } else {
1.772 bisitz 3541: $check4 = ' checked="checked"';
1.591 raeburn 3542: $check5 = '';
3543: }
1.586 raeburn 3544: }
1.591 raeburn 3545: if (defined($in{'curr_autharg'})) {
1.165 raeburn 3546: $krbarg = $in{'curr_autharg'};
3547: }
1.586 raeburn 3548: if (!$can_assign{'krb4'} && !$can_assign{'krb5'}) {
1.591 raeburn 3549: if (defined($in{'curr_autharg'})) {
1.586 raeburn 3550: $result =
3551: &mt('Currently Kerberos authenticated with domain [_1] Version [_2].',
3552: $in{'curr_autharg'},$krbver);
3553: } else {
3554: $result =
3555: &mt('Currently Kerberos authenticated, Version [_1].',$krbver);
3556: }
3557: return $result;
3558: }
3559: }
3560: } else {
3561: if ($authnum == 1) {
1.784 bisitz 3562: $authtype = '<input type="hidden" name="login" value="krb" />';
1.165 raeburn 3563: }
3564: }
1.586 raeburn 3565: if (!$can_assign{'krb4'} && !$can_assign{'krb5'}) {
3566: return;
1.587 raeburn 3567: } elsif ($authtype eq '') {
1.591 raeburn 3568: if (defined($in{'mode'})) {
1.587 raeburn 3569: if ($in{'mode'} eq 'modifycourse') {
3570: if ($authnum == 1) {
1.1259 raeburn 3571: $authtype = '<input type="radio" name="login" value="krb"'.$disabled.' />';
1.587 raeburn 3572: }
3573: }
3574: }
1.586 raeburn 3575: }
3576: $jscall = "javascript:changed_radio('krb',$in{'formname'});";
3577: if ($authtype eq '') {
3578: $authtype = '<input type="radio" name="login" value="krb" '.
3579: 'onclick="'.$jscall.'" onchange="'.$jscall.'"'.
1.1259 raeburn 3580: $krbcheck.$disabled.' />';
1.586 raeburn 3581: }
3582: if (($can_assign{'krb4'} && $can_assign{'krb5'}) ||
1.1106 raeburn 3583: ($can_assign{'krb4'} && !$can_assign{'krb5'} &&
1.586 raeburn 3584: $in{'curr_authtype'} eq 'krb5') ||
1.1106 raeburn 3585: (!$can_assign{'krb4'} && $can_assign{'krb5'} &&
1.586 raeburn 3586: $in{'curr_authtype'} eq 'krb4')) {
3587: $result .= &mt
1.144 matthew 3588: ('[_1] Kerberos authenticated with domain [_2] '.
1.281 albertel 3589: '[_3] Version 4 [_4] Version 5 [_5]',
1.586 raeburn 3590: '<label>'.$authtype,
1.281 albertel 3591: '</label><input type="text" size="10" name="krbarg" '.
1.165 raeburn 3592: 'value="'.$krbarg.'" '.
1.1259 raeburn 3593: 'onchange="'.$jscall.'"'.$disabled.' />',
3594: '<label><input type="radio" name="krbver" value="4" '.$check4.$disabled.' />',
3595: '</label><label><input type="radio" name="krbver" value="5" '.$check5.$disabled.' />',
1.281 albertel 3596: '</label>');
1.586 raeburn 3597: } elsif ($can_assign{'krb4'}) {
3598: $result .= &mt
3599: ('[_1] Kerberos authenticated with domain [_2] '.
3600: '[_3] Version 4 [_4]',
3601: '<label>'.$authtype,
3602: '</label><input type="text" size="10" name="krbarg" '.
3603: 'value="'.$krbarg.'" '.
1.1259 raeburn 3604: 'onchange="'.$jscall.'"'.$disabled.' />',
1.586 raeburn 3605: '<label><input type="hidden" name="krbver" value="4" />',
3606: '</label>');
3607: } elsif ($can_assign{'krb5'}) {
3608: $result .= &mt
3609: ('[_1] Kerberos authenticated with domain [_2] '.
3610: '[_3] Version 5 [_4]',
3611: '<label>'.$authtype,
3612: '</label><input type="text" size="10" name="krbarg" '.
3613: 'value="'.$krbarg.'" '.
1.1259 raeburn 3614: 'onchange="'.$jscall.'"'.$disabled.' />',
1.586 raeburn 3615: '<label><input type="hidden" name="krbver" value="5" />',
3616: '</label>');
3617: }
1.32 matthew 3618: return $result;
3619: }
3620:
1.1106 raeburn 3621: sub authform_internal {
1.586 raeburn 3622: my %in = (
1.32 matthew 3623: formname => 'document.cu',
3624: kerb_def_dom => 'MSU.EDU',
3625: @_,
3626: );
1.1259 raeburn 3627: my ($intcheck,$intarg,$result,$authtype,$autharg,$jscall,$disabled);
1.1106 raeburn 3628: my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.1259 raeburn 3629: if ($in{'readonly'}) {
3630: $disabled = ' disabled="disabled"';
3631: }
1.591 raeburn 3632: if (defined($in{'curr_authtype'})) {
3633: if ($in{'curr_authtype'} eq 'int') {
1.586 raeburn 3634: if ($can_assign{'int'}) {
1.772 bisitz 3635: $intcheck = 'checked="checked" ';
1.623 raeburn 3636: if (defined($in{'mode'})) {
3637: if ($in{'mode'} eq 'modifyuser') {
3638: $intcheck = '';
3639: }
3640: }
1.591 raeburn 3641: if (defined($in{'curr_autharg'})) {
1.586 raeburn 3642: $intarg = $in{'curr_autharg'};
3643: }
3644: } else {
3645: $result = &mt('Currently internally authenticated.');
3646: return $result;
1.165 raeburn 3647: }
3648: }
1.586 raeburn 3649: } else {
3650: if ($authnum == 1) {
1.784 bisitz 3651: $authtype = '<input type="hidden" name="login" value="int" />';
1.586 raeburn 3652: }
3653: }
3654: if (!$can_assign{'int'}) {
3655: return;
1.587 raeburn 3656: } elsif ($authtype eq '') {
1.591 raeburn 3657: if (defined($in{'mode'})) {
1.587 raeburn 3658: if ($in{'mode'} eq 'modifycourse') {
3659: if ($authnum == 1) {
1.1259 raeburn 3660: $authtype = '<input type="radio" name="login" value="int"'.$disabled.' />';
1.587 raeburn 3661: }
3662: }
3663: }
1.165 raeburn 3664: }
1.586 raeburn 3665: $jscall = "javascript:changed_radio('int',$in{'formname'});";
3666: if ($authtype eq '') {
3667: $authtype = '<input type="radio" name="login" value="int" '.$intcheck.
1.1259 raeburn 3668: ' onchange="'.$jscall.'" onclick="'.$jscall.'"'.$disabled.' />';
1.586 raeburn 3669: }
1.605 bisitz 3670: $autharg = '<input type="password" size="10" name="intarg" value="'.
1.1259 raeburn 3671: $intarg.'" onchange="'.$jscall.'"'.$disabled.' />';
1.586 raeburn 3672: $result = &mt
1.144 matthew 3673: ('[_1] Internally authenticated (with initial password [_2])',
1.586 raeburn 3674: '<label>'.$authtype,'</label>'.$autharg);
1.1259 raeburn 3675: $result.='<label><input type="checkbox" name="visible" onclick="if (this.checked) { this.form.intarg.type='."'text'".' } else { this.form.intarg.type='."'password'".' }"'.$disabled.' />'.&mt('Visible input').'</label>';
1.32 matthew 3676: return $result;
3677: }
3678:
1.1104 raeburn 3679: sub authform_local {
1.32 matthew 3680: my %in = (
3681: formname => 'document.cu',
3682: kerb_def_dom => 'MSU.EDU',
3683: @_,
3684: );
1.1259 raeburn 3685: my ($loccheck,$locarg,$result,$authtype,$autharg,$jscall,$disabled);
1.1106 raeburn 3686: my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.1259 raeburn 3687: if ($in{'readonly'}) {
3688: $disabled = ' disabled="disabled"';
3689: }
1.591 raeburn 3690: if (defined($in{'curr_authtype'})) {
3691: if ($in{'curr_authtype'} eq 'loc') {
1.586 raeburn 3692: if ($can_assign{'loc'}) {
1.772 bisitz 3693: $loccheck = 'checked="checked" ';
1.623 raeburn 3694: if (defined($in{'mode'})) {
3695: if ($in{'mode'} eq 'modifyuser') {
3696: $loccheck = '';
3697: }
3698: }
1.591 raeburn 3699: if (defined($in{'curr_autharg'})) {
1.586 raeburn 3700: $locarg = $in{'curr_autharg'};
3701: }
3702: } else {
3703: $result = &mt('Currently using local (institutional) authentication.');
3704: return $result;
1.165 raeburn 3705: }
3706: }
1.586 raeburn 3707: } else {
3708: if ($authnum == 1) {
1.784 bisitz 3709: $authtype = '<input type="hidden" name="login" value="loc" />';
1.586 raeburn 3710: }
3711: }
3712: if (!$can_assign{'loc'}) {
3713: return;
1.587 raeburn 3714: } elsif ($authtype eq '') {
1.591 raeburn 3715: if (defined($in{'mode'})) {
1.587 raeburn 3716: if ($in{'mode'} eq 'modifycourse') {
3717: if ($authnum == 1) {
1.1259 raeburn 3718: $authtype = '<input type="radio" name="login" value="loc"'.$disabled.' />';
1.587 raeburn 3719: }
3720: }
3721: }
1.165 raeburn 3722: }
1.586 raeburn 3723: $jscall = "javascript:changed_radio('loc',$in{'formname'});";
3724: if ($authtype eq '') {
3725: $authtype = '<input type="radio" name="login" value="loc" '.
3726: $loccheck.' onchange="'.$jscall.'" onclick="'.
1.1259 raeburn 3727: $jscall.'"'.$disabled.' />';
1.586 raeburn 3728: }
3729: $autharg = '<input type="text" size="10" name="locarg" value="'.
1.1259 raeburn 3730: $locarg.'" onchange="'.$jscall.'"'.$disabled.' />';
1.586 raeburn 3731: $result = &mt('[_1] Local Authentication with argument [_2]',
3732: '<label>'.$authtype,'</label>'.$autharg);
1.32 matthew 3733: return $result;
3734: }
3735:
1.1106 raeburn 3736: sub authform_filesystem {
1.32 matthew 3737: my %in = (
3738: formname => 'document.cu',
3739: kerb_def_dom => 'MSU.EDU',
3740: @_,
3741: );
1.1259 raeburn 3742: my ($fsyscheck,$result,$authtype,$autharg,$jscall,$disabled);
1.1106 raeburn 3743: my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.1259 raeburn 3744: if ($in{'readonly'}) {
3745: $disabled = ' disabled="disabled"';
3746: }
1.591 raeburn 3747: if (defined($in{'curr_authtype'})) {
3748: if ($in{'curr_authtype'} eq 'fsys') {
1.586 raeburn 3749: if ($can_assign{'fsys'}) {
1.772 bisitz 3750: $fsyscheck = 'checked="checked" ';
1.623 raeburn 3751: if (defined($in{'mode'})) {
3752: if ($in{'mode'} eq 'modifyuser') {
3753: $fsyscheck = '';
3754: }
3755: }
1.586 raeburn 3756: } else {
3757: $result = &mt('Currently Filesystem Authenticated.');
3758: return $result;
1.1259 raeburn 3759: }
1.586 raeburn 3760: }
3761: } else {
3762: if ($authnum == 1) {
1.784 bisitz 3763: $authtype = '<input type="hidden" name="login" value="fsys" />';
1.586 raeburn 3764: }
3765: }
3766: if (!$can_assign{'fsys'}) {
3767: return;
1.587 raeburn 3768: } elsif ($authtype eq '') {
1.591 raeburn 3769: if (defined($in{'mode'})) {
1.587 raeburn 3770: if ($in{'mode'} eq 'modifycourse') {
3771: if ($authnum == 1) {
1.1259 raeburn 3772: $authtype = '<input type="radio" name="login" value="fsys"'.$disabled.' />';
1.587 raeburn 3773: }
3774: }
3775: }
1.586 raeburn 3776: }
3777: $jscall = "javascript:changed_radio('fsys',$in{'formname'});";
3778: if ($authtype eq '') {
3779: $authtype = '<input type="radio" name="login" value="fsys" '.
3780: $fsyscheck.' onchange="'.$jscall.'" onclick="'.
1.1259 raeburn 3781: $jscall.'"'.$disabled.' />';
1.586 raeburn 3782: }
1.1310 raeburn 3783: $autharg = '<input type="password" size="10" name="fsysarg" value=""'.
1.1259 raeburn 3784: ' onchange="'.$jscall.'"'.$disabled.' />';
1.586 raeburn 3785: $result = &mt
1.144 matthew 3786: ('[_1] Filesystem Authenticated (with initial password [_2])',
1.1310 raeburn 3787: '<label>'.$authtype,'</label>'.$autharg);
3788: return $result;
3789: }
3790:
3791: sub authform_lti {
3792: my %in = (
3793: formname => 'document.cu',
3794: kerb_def_dom => 'MSU.EDU',
3795: @_,
3796: );
3797: my ($lticheck,$result,$authtype,$autharg,$jscall,$disabled);
3798: my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
3799: if ($in{'readonly'}) {
3800: $disabled = ' disabled="disabled"';
3801: }
3802: if (defined($in{'curr_authtype'})) {
3803: if ($in{'curr_authtype'} eq 'lti') {
3804: if ($can_assign{'lti'}) {
3805: $lticheck = 'checked="checked" ';
3806: if (defined($in{'mode'})) {
3807: if ($in{'mode'} eq 'modifyuser') {
3808: $lticheck = '';
3809: }
3810: }
3811: } else {
3812: $result = &mt('Currently LTI Authenticated.');
3813: return $result;
3814: }
3815: }
3816: } else {
3817: if ($authnum == 1) {
3818: $authtype = '<input type="hidden" name="login" value="lti" />';
3819: }
3820: }
3821: if (!$can_assign{'lti'}) {
3822: return;
3823: } elsif ($authtype eq '') {
3824: if (defined($in{'mode'})) {
3825: if ($in{'mode'} eq 'modifycourse') {
3826: if ($authnum == 1) {
3827: $authtype = '<input type="radio" name="login" value="lti"'.$disabled.' />';
3828: }
3829: }
3830: }
3831: }
3832: $jscall = "javascript:changed_radio('lti',$in{'formname'});";
3833: if (($authtype eq '') && (($in{'mode'} eq 'modifycourse') || ($in{'curr_authtype'} ne 'lti'))) {
3834: $authtype = '<input type="radio" name="login" value="lti" '.
3835: $lticheck.' onchange="'.$jscall.'" onclick="'.
3836: $jscall.'"'.$disabled.' />';
3837: }
3838: $autharg = '<input type="hidden" name="ltiarg" value="" />';
3839: if ($authtype) {
3840: $result = &mt('[_1] LTI Authenticated',
3841: '<label>'.$authtype.'</label>'.$autharg);
3842: } else {
3843: $result = '<b>'.&mt('LTI Authenticated').'</b>'.
3844: $autharg;
3845: }
1.32 matthew 3846: return $result;
3847: }
3848:
1.586 raeburn 3849: sub get_assignable_auth {
3850: my ($dom) = @_;
3851: if ($dom eq '') {
3852: $dom = $env{'request.role.domain'};
3853: }
3854: my %can_assign = (
3855: krb4 => 1,
3856: krb5 => 1,
3857: int => 1,
3858: loc => 1,
1.1310 raeburn 3859: lti => 1,
1.586 raeburn 3860: );
3861: my %domconfig = &Apache::lonnet::get_dom('configuration',['usercreation'],$dom);
3862: if (ref($domconfig{'usercreation'}) eq 'HASH') {
3863: if (ref($domconfig{'usercreation'}{'authtypes'}) eq 'HASH') {
3864: my $authhash = $domconfig{'usercreation'}{'authtypes'};
3865: my $context;
3866: if ($env{'request.role'} =~ /^au/) {
3867: $context = 'author';
1.1259 raeburn 3868: } elsif ($env{'request.role'} =~ /^(dc|dh)/) {
1.586 raeburn 3869: $context = 'domain';
3870: } elsif ($env{'request.course.id'}) {
3871: $context = 'course';
3872: }
3873: if ($context) {
3874: if (ref($authhash->{$context}) eq 'HASH') {
3875: %can_assign = %{$authhash->{$context}};
3876: }
3877: }
3878: }
3879: }
3880: my $authnum = 0;
3881: foreach my $key (keys(%can_assign)) {
3882: if ($can_assign{$key}) {
3883: $authnum ++;
3884: }
3885: }
3886: if ($can_assign{'krb4'} && $can_assign{'krb5'}) {
3887: $authnum --;
3888: }
3889: return ($authnum,%can_assign);
3890: }
3891:
1.1331 raeburn 3892: sub check_passwd_rules {
3893: my ($domain,$plainpass) = @_;
3894: my %passwdconf = &Apache::lonnet::get_passwdconf($domain);
3895: my ($min,$max,@chars,@brokerule,$warning);
1.1333 raeburn 3896: $min = $Apache::lonnet::passwdmin;
1.1331 raeburn 3897: if (ref($passwdconf{'chars'}) eq 'ARRAY') {
3898: if ($passwdconf{'min'} =~ /^\d+$/) {
1.1333 raeburn 3899: if ($passwdconf{'min'} > $min) {
3900: $min = $passwdconf{'min'};
3901: }
1.1331 raeburn 3902: }
3903: if ($passwdconf{'max'} =~ /^\d+$/) {
3904: $max = $passwdconf{'max'};
3905: }
3906: @chars = @{$passwdconf{'chars'}};
3907: }
3908: if (($min) && (length($plainpass) < $min)) {
3909: push(@brokerule,'min');
3910: }
3911: if (($max) && (length($plainpass) > $max)) {
3912: push(@brokerule,'max');
3913: }
3914: if (@chars) {
3915: my %rules;
3916: map { $rules{$_} = 1; } @chars;
3917: if ($rules{'uc'}) {
3918: unless ($plainpass =~ /[A-Z]/) {
3919: push(@brokerule,'uc');
3920: }
3921: }
3922: if ($rules{'lc'}) {
1.1332 raeburn 3923: unless ($plainpass =~ /[a-z]/) {
1.1331 raeburn 3924: push(@brokerule,'lc');
3925: }
3926: }
3927: if ($rules{'num'}) {
3928: unless ($plainpass =~ /\d/) {
3929: push(@brokerule,'num');
3930: }
3931: }
3932: if ($rules{'spec'}) {
3933: unless ($plainpass =~ /[!"#$%&'()*+,\-.\/:;<=>?@[\\\]^_`{|}~]/) {
3934: push(@brokerule,'spec');
3935: }
3936: }
3937: }
3938: if (@brokerule) {
3939: my %rulenames = &Apache::lonlocal::texthash(
3940: uc => 'At least one upper case letter',
3941: lc => 'At least one lower case letter',
3942: num => 'At least one number',
3943: spec => 'At least one non-alphanumeric',
3944: );
3945: $rulenames{'uc'} .= ': ABCDEFGHIJKLMNOPQRSTUVWXYZ';
3946: $rulenames{'lc'} .= ': abcdefghijklmnopqrstuvwxyz';
3947: $rulenames{'num'} .= ': 0123456789';
3948: $rulenames{'spec'} .= ': !"\#$%&\'()*+,-./:;<=>?@[\]^_\`{|}~';
3949: $rulenames{'min'} = &mt('Minimum password length: [_1]',$min);
3950: $rulenames{'max'} = &mt('Maximum password length: [_1]',$max);
3951: $warning = &mt('Password did not satisfy the following:').'<ul>';
1.1336 raeburn 3952: foreach my $rule ('min','max','uc','lc','num','spec') {
1.1331 raeburn 3953: if (grep(/^$rule$/,@brokerule)) {
3954: $warning .= '<li>'.$rulenames{$rule}.'</li>';
3955: }
3956: }
3957: $warning .= '</ul>';
3958: }
1.1332 raeburn 3959: if (wantarray) {
3960: return @brokerule;
3961: }
1.1331 raeburn 3962: return $warning;
3963: }
3964:
1.1376 raeburn 3965: sub passwd_validation_js {
1.1377 raeburn 3966: my ($currpasswdval,$domain,$context,$id) = @_;
3967: my (%passwdconf,$alertmsg);
3968: if ($context eq 'linkprot') {
3969: my %domconfig = &Apache::lonnet::get_dom('configuration',['ltisec'],$domain);
3970: if (ref($domconfig{'ltisec'}) eq 'HASH') {
3971: if (ref($domconfig{'ltisec'}{'rules'}) eq 'HASH') {
3972: %passwdconf = %{$domconfig{'ltisec'}{'rules'}};
3973: }
3974: }
3975: if ($id eq 'add') {
3976: $alertmsg = &mt('Secret for added launcher did not satisfy requirement(s):').'\n\n';
3977: } elsif ($id =~ /^\d+$/) {
3978: my $pos = $id+1;
3979: $alertmsg = &mt('Secret for launcher [_1] did not satisfy requirement(s):','#'.$pos).'\n\n';
3980: } else {
3981: $alertmsg = &mt('A secret did not satisfy requirement(s):').'\n\n';
3982: }
1.1434 raeburn 3983: } elsif ($context eq 'ltitools') {
3984: my %domconfig = &Apache::lonnet::get_dom('configuration',['toolsec'],$domain);
3985: if (ref($domconfig{'toolsec'}) eq 'HASH') {
3986: if (ref($domconfig{'toolsec'}{'rules'}) eq 'HASH') {
3987: %passwdconf = %{$domconfig{'toolsec'}{'rules'}};
3988: }
3989: }
3990: if ($id eq 'add') {
3991: $alertmsg = &mt('Secret for added external tool did not satisfy requirement(s):').'\n\n';
3992: } elsif ($id =~ /^\d+$/) {
3993: my $pos = $id+1;
3994: $alertmsg = &mt('Secret for external tool [_1] did not satisfy requirement(s):','#'.$pos).'\n\n';
3995: } else {
3996: $alertmsg = &mt('A secret did not satisfy requirement(s):').'\n\n';
3997: }
1.1377 raeburn 3998: } else {
3999: %passwdconf = &Apache::lonnet::get_passwdconf($domain);
4000: $alertmsg = &mt('Initial password did not satisfy requirement(s):').'\n\n';
4001: }
1.1376 raeburn 4002: my ($min,$max,@chars,$numrules,$intargjs,%alert);
4003: $numrules = 0;
4004: $min = $Apache::lonnet::passwdmin;
4005: if (ref($passwdconf{'chars'}) eq 'ARRAY') {
4006: if ($passwdconf{'min'} =~ /^\d+$/) {
4007: if ($passwdconf{'min'} > $min) {
4008: $min = $passwdconf{'min'};
4009: }
4010: }
4011: if ($passwdconf{'max'} =~ /^\d+$/) {
4012: $max = $passwdconf{'max'};
4013: $numrules ++;
4014: }
4015: @chars = @{$passwdconf{'chars'}};
4016: if (@chars) {
4017: $numrules ++;
4018: }
4019: }
4020: if ($min > 0) {
4021: $numrules ++;
4022: }
4023: if (($min > 0) || ($max ne '') || (@chars > 0)) {
4024: if ($min) {
4025: $alert{'min'} = &mt('minimum [quant,_1,character]',$min).'\n';
4026: }
4027: if ($max) {
4028: $alert{'max'} = &mt('maximum [quant,_1,character]',$max).'\n';
4029: }
4030: my (@charalerts,@charrules);
4031: if (@chars) {
4032: if (grep(/^uc$/,@chars)) {
4033: push(@charalerts,&mt('contain at least one upper case letter'));
4034: push(@charrules,'uc');
4035: }
4036: if (grep(/^lc$/,@chars)) {
4037: push(@charalerts,&mt('contain at least one lower case letter'));
4038: push(@charrules,'lc');
4039: }
4040: if (grep(/^num$/,@chars)) {
4041: push(@charalerts,&mt('contain at least one number'));
4042: push(@charrules,'num');
4043: }
4044: if (grep(/^spec$/,@chars)) {
4045: push(@charalerts,&mt('contain at least one non-alphanumeric'));
4046: push(@charrules,'spec');
4047: }
4048: }
4049: $intargjs = qq| var rulesmsg = '';\n|.
4050: qq| var currpwval = $currpasswdval;\n|;
4051: if ($min) {
4052: $intargjs .= qq|
4053: if (currpwval.length < $min) {
4054: rulesmsg += ' - $alert{min}';
4055: }
4056: |;
4057: }
4058: if ($max) {
4059: $intargjs .= qq|
4060: if (currpwval.length > $max) {
4061: rulesmsg += ' - $alert{max}';
4062: }
4063: |;
4064: }
4065: if (@chars > 0) {
4066: my $charrulestr = '"'.join('","',@charrules).'"';
4067: my $charalertstr = '"'.join('","',@charalerts).'"';
4068: $intargjs .= qq| var brokerules = new Array();\n|.
4069: qq| var charrules = new Array($charrulestr);\n|.
4070: qq| var charalerts = new Array($charalertstr);\n|;
4071: my %rules;
4072: map { $rules{$_} = 1; } @chars;
4073: if ($rules{'uc'}) {
4074: $intargjs .= qq|
4075: var ucRegExp = /[A-Z]/;
4076: if (!ucRegExp.test(currpwval)) {
4077: brokerules.push('uc');
4078: }
4079: |;
4080: }
4081: if ($rules{'lc'}) {
4082: $intargjs .= qq|
4083: var lcRegExp = /[a-z]/;
4084: if (!lcRegExp.test(currpwval)) {
4085: brokerules.push('lc');
4086: }
4087: |;
4088: }
4089: if ($rules{'num'}) {
4090: $intargjs .= qq|
4091: var numRegExp = /[0-9]/;
4092: if (!numRegExp.test(currpwval)) {
4093: brokerules.push('num');
4094: }
4095: |;
4096: }
4097: if ($rules{'spec'}) {
4098: $intargjs .= q|
4099: var specRegExp = /[!"#$%&'()*+,\-.\/:;<=>?@[\\^\]_`{\|}~]/;
4100: if (!specRegExp.test(currpwval)) {
4101: brokerules.push('spec');
4102: }
4103: |;
4104: }
4105: $intargjs .= qq|
4106: if (brokerules.length > 0) {
4107: for (var i=0; i<brokerules.length; i++) {
4108: for (var j=0; j<charrules.length; j++) {
4109: if (brokerules[i] == charrules[j]) {
4110: rulesmsg += ' - '+charalerts[j]+'\\n';
4111: break;
4112: }
4113: }
4114: }
4115: }
4116: |;
4117: }
4118: $intargjs .= qq|
4119: if (rulesmsg != '') {
4120: rulesmsg = '$alertmsg'+rulesmsg;
4121: alert(rulesmsg);
4122: return false;
4123: }
4124: |;
4125: }
4126: return ($numrules,$intargjs);
4127: }
4128:
1.80 albertel 4129: ###############################################################
4130: ## Get Kerberos Defaults for Domain ##
4131: ###############################################################
4132: ##
4133: ## Returns default kerberos version and an associated argument
4134: ## as listed in file domain.tab. If not listed, provides
4135: ## appropriate default domain and kerberos version.
4136: ##
4137: #-------------------------------------------
4138:
4139: =pod
4140:
1.648 raeburn 4141: =item * &get_kerberos_defaults()
1.80 albertel 4142:
4143: get_kerberos_defaults($target_domain) returns the default kerberos
1.641 raeburn 4144: version and domain. If not found, it defaults to version 4 and the
4145: domain of the server.
1.80 albertel 4146:
1.648 raeburn 4147: =over 4
4148:
1.80 albertel 4149: ($def_version, $def_krb_domain) = &get_kerberos_defaults($target_domain);
4150:
1.648 raeburn 4151: =back
4152:
4153: =back
4154:
1.80 albertel 4155: =cut
4156:
4157: #-------------------------------------------
4158: sub get_kerberos_defaults {
4159: my $domain=shift;
1.641 raeburn 4160: my ($krbdef,$krbdefdom);
4161: my %domdefaults = &Apache::lonnet::get_domain_defaults($domain);
4162: if (($domdefaults{'auth_def'} =~/^krb(4|5)$/) && ($domdefaults{'auth_arg_def'} ne '')) {
4163: $krbdef = $domdefaults{'auth_def'};
4164: $krbdefdom = $domdefaults{'auth_arg_def'};
4165: } else {
1.80 albertel 4166: $ENV{'SERVER_NAME'}=~/(\w+\.\w+)$/;
4167: my $krbdefdom=$1;
4168: $krbdefdom=~tr/a-z/A-Z/;
4169: $krbdef = "krb4";
4170: }
4171: return ($krbdef,$krbdefdom);
4172: }
1.112 bowersj2 4173:
1.32 matthew 4174:
1.46 matthew 4175: ###############################################################
4176: ## Thesaurus Functions ##
4177: ###############################################################
1.20 www 4178:
1.46 matthew 4179: =pod
1.20 www 4180:
1.112 bowersj2 4181: =head1 Thesaurus Functions
4182:
4183: =over 4
4184:
1.648 raeburn 4185: =item * &initialize_keywords()
1.46 matthew 4186:
4187: Initializes the package variable %Keywords if it is empty. Uses the
4188: package variable $thesaurus_db_file.
4189:
4190: =cut
4191:
4192: ###################################################
4193:
4194: sub initialize_keywords {
4195: return 1 if (scalar keys(%Keywords));
4196: # If we are here, %Keywords is empty, so fill it up
4197: # Make sure the file we need exists...
4198: if (! -e $thesaurus_db_file) {
4199: &Apache::lonnet::logthis("Attempt to access $thesaurus_db_file".
4200: " failed because it does not exist");
4201: return 0;
4202: }
4203: # Set up the hash as a database
4204: my %thesaurus_db;
4205: if (! tie(%thesaurus_db,'GDBM_File',
1.53 albertel 4206: $thesaurus_db_file,&GDBM_READER(),0640)){
1.46 matthew 4207: &Apache::lonnet::logthis("Could not tie \%thesaurus_db to ".
4208: $thesaurus_db_file);
4209: return 0;
4210: }
4211: # Get the average number of appearances of a word.
4212: my $avecount = $thesaurus_db{'average.count'};
4213: # Put keywords (those that appear > average) into %Keywords
4214: while (my ($word,$data)=each (%thesaurus_db)) {
4215: my ($count,undef) = split /:/,$data;
4216: $Keywords{$word}++ if ($count > $avecount);
4217: }
4218: untie %thesaurus_db;
4219: # Remove special values from %Keywords.
1.356 albertel 4220: foreach my $value ('total.count','average.count') {
4221: delete($Keywords{$value}) if (exists($Keywords{$value}));
1.586 raeburn 4222: }
1.46 matthew 4223: return 1;
4224: }
4225:
4226: ###################################################
4227:
4228: =pod
4229:
1.648 raeburn 4230: =item * &keyword($word)
1.46 matthew 4231:
4232: Returns true if $word is a keyword. A keyword is a word that appears more
4233: than the average number of times in the thesaurus database. Calls
4234: &initialize_keywords
4235:
4236: =cut
4237:
4238: ###################################################
1.20 www 4239:
4240: sub keyword {
1.46 matthew 4241: return if (!&initialize_keywords());
4242: my $word=lc(shift());
4243: $word=~s/\W//g;
4244: return exists($Keywords{$word});
1.20 www 4245: }
1.46 matthew 4246:
4247: ###############################################################
4248:
4249: =pod
1.20 www 4250:
1.648 raeburn 4251: =item * &get_related_words()
1.46 matthew 4252:
1.160 matthew 4253: Look up a word in the thesaurus. Takes a scalar argument and returns
1.46 matthew 4254: an array of words. If the keyword is not in the thesaurus, an empty array
4255: will be returned. The order of the words returned is determined by the
4256: database which holds them.
4257:
4258: Uses global $thesaurus_db_file.
4259:
1.1057 foxr 4260:
1.46 matthew 4261: =cut
4262:
4263: ###############################################################
4264: sub get_related_words {
4265: my $keyword = shift;
4266: my %thesaurus_db;
4267: if (! -e $thesaurus_db_file) {
4268: &Apache::lonnet::logthis("Attempt to access $thesaurus_db_file ".
4269: "failed because the file does not exist");
4270: return ();
4271: }
4272: if (! tie(%thesaurus_db,'GDBM_File',
1.53 albertel 4273: $thesaurus_db_file,&GDBM_READER(),0640)){
1.46 matthew 4274: return ();
4275: }
4276: my @Words=();
1.429 www 4277: my $count=0;
1.46 matthew 4278: if (exists($thesaurus_db{$keyword})) {
1.356 albertel 4279: # The first element is the number of times
4280: # the word appears. We do not need it now.
1.429 www 4281: my (undef,@RelatedWords) = (split(/:/,$thesaurus_db{$keyword}));
4282: my (undef,$mostfrequentcount)=split(/\,/,$RelatedWords[0]);
4283: my $threshold=$mostfrequentcount/10;
4284: foreach my $possibleword (@RelatedWords) {
4285: my ($word,$wordcount)=split(/\,/,$possibleword);
4286: if ($wordcount>$threshold) {
4287: push(@Words,$word);
4288: $count++;
4289: if ($count>10) { last; }
4290: }
1.20 www 4291: }
4292: }
1.46 matthew 4293: untie %thesaurus_db;
4294: return @Words;
1.14 harris41 4295: }
1.1090 foxr 4296: ###############################################################
4297: #
4298: # Spell checking
4299: #
4300:
4301: =pod
4302:
1.1142 raeburn 4303: =back
4304:
1.1090 foxr 4305: =head1 Spell checking
4306:
4307: =over 4
4308:
4309: =item * &check_spelling($wordlist $language)
4310:
4311: Takes a string containing words and feeds it to an external
4312: spellcheck program via a pipeline. Returns a string containing
4313: them mis-spelled words.
4314:
4315: Parameters:
4316:
4317: =over 4
4318:
4319: =item - $wordlist
4320:
4321: String that will be fed into the spellcheck program.
4322:
4323: =item - $language
4324:
4325: Language string that specifies the language for which the spell
4326: check will be performed.
4327:
4328: =back
4329:
4330: =back
4331:
4332: Note: This sub assumes that aspell is installed.
4333:
4334:
4335: =cut
4336:
1.46 matthew 4337:
1.1090 foxr 4338: sub check_spelling {
4339: my ($wordlist, $language) = @_;
1.1091 foxr 4340: my @misspellings;
4341:
4342: # Generate the speller and set the langauge.
4343: # if explicitly selected:
1.1090 foxr 4344:
1.1091 foxr 4345: my $speller = Text::Aspell->new;
1.1090 foxr 4346: if ($language) {
1.1091 foxr 4347: $speller->set_option('lang', $language);
1.1090 foxr 4348: }
4349:
1.1091 foxr 4350: # Turn the word list into an array of words by splittingon whitespace
1.1090 foxr 4351:
1.1091 foxr 4352: my @words = split(/\s+/, $wordlist);
1.1090 foxr 4353:
1.1091 foxr 4354: foreach my $word (@words) {
4355: if(! $speller->check($word)) {
4356: push(@misspellings, $word);
1.1090 foxr 4357: }
4358: }
1.1091 foxr 4359: return join(' ', @misspellings);
4360:
1.1090 foxr 4361: }
4362:
1.61 www 4363: # -------------------------------------------------------------- Plaintext name
1.81 albertel 4364: =pod
4365:
1.112 bowersj2 4366: =head1 User Name Functions
4367:
4368: =over 4
4369:
1.648 raeburn 4370: =item * &plainname($uname,$udom,$first)
1.81 albertel 4371:
1.112 bowersj2 4372: Takes a users logon name and returns it as a string in
1.226 albertel 4373: "first middle last generation" form
4374: if $first is set to 'lastname' then it returns it as
4375: 'lastname generation, firstname middlename' if their is a lastname
1.81 albertel 4376:
4377: =cut
1.61 www 4378:
1.295 www 4379:
1.81 albertel 4380: ###############################################################
1.61 www 4381: sub plainname {
1.226 albertel 4382: my ($uname,$udom,$first)=@_;
1.537 albertel 4383: return if (!defined($uname) || !defined($udom));
1.295 www 4384: my %names=&getnames($uname,$udom);
1.226 albertel 4385: my $name=&Apache::lonnet::format_name($names{'firstname'},
4386: $names{'middlename'},
4387: $names{'lastname'},
4388: $names{'generation'},$first);
4389: $name=~s/^\s+//;
1.62 www 4390: $name=~s/\s+$//;
4391: $name=~s/\s+/ /g;
1.353 albertel 4392: if ($name !~ /\S/) { $name=$uname.':'.$udom; }
1.62 www 4393: return $name;
1.61 www 4394: }
1.66 www 4395:
4396: # -------------------------------------------------------------------- Nickname
1.81 albertel 4397: =pod
4398:
1.648 raeburn 4399: =item * &nickname($uname,$udom)
1.81 albertel 4400:
4401: Gets a users name and returns it as a string as
4402:
4403: ""nickname""
1.66 www 4404:
1.81 albertel 4405: if the user has a nickname or
4406:
4407: "first middle last generation"
4408:
4409: if the user does not
4410:
4411: =cut
1.66 www 4412:
4413: sub nickname {
4414: my ($uname,$udom)=@_;
1.537 albertel 4415: return if (!defined($uname) || !defined($udom));
1.295 www 4416: my %names=&getnames($uname,$udom);
1.68 albertel 4417: my $name=$names{'nickname'};
1.66 www 4418: if ($name) {
4419: $name='"'.$name.'"';
4420: } else {
4421: $name=$names{'firstname'}.' '.$names{'middlename'}.' '.
4422: $names{'lastname'}.' '.$names{'generation'};
4423: $name=~s/\s+$//;
4424: $name=~s/\s+/ /g;
4425: }
4426: return $name;
4427: }
4428:
1.295 www 4429: sub getnames {
4430: my ($uname,$udom)=@_;
1.537 albertel 4431: return if (!defined($uname) || !defined($udom));
1.433 albertel 4432: if ($udom eq 'public' && $uname eq 'public') {
4433: return ('lastname' => &mt('Public'));
4434: }
1.295 www 4435: my $id=$uname.':'.$udom;
4436: my ($names,$cached)=&Apache::lonnet::is_cached_new('namescache',$id);
4437: if ($cached) {
4438: return %{$names};
4439: } else {
4440: my %loadnames=&Apache::lonnet::get('environment',
4441: ['firstname','middlename','lastname','generation','nickname'],
4442: $udom,$uname);
4443: &Apache::lonnet::do_cache_new('namescache',$id,\%loadnames);
4444: return %loadnames;
4445: }
4446: }
1.61 www 4447:
1.542 raeburn 4448: # -------------------------------------------------------------------- getemails
1.648 raeburn 4449:
1.542 raeburn 4450: =pod
4451:
1.648 raeburn 4452: =item * &getemails($uname,$udom)
1.542 raeburn 4453:
4454: Gets a user's email information and returns it as a hash with keys:
4455: notification, critnotification, permanentemail
4456:
4457: For notification and critnotification, values are comma-separated lists
1.648 raeburn 4458: of e-mail addresses; for permanentemail, value is a single e-mail address.
1.542 raeburn 4459:
1.648 raeburn 4460:
1.542 raeburn 4461: =cut
4462:
1.648 raeburn 4463:
1.466 albertel 4464: sub getemails {
4465: my ($uname,$udom)=@_;
4466: if ($udom eq 'public' && $uname eq 'public') {
4467: return;
4468: }
1.467 www 4469: if (!$udom) { $udom=$env{'user.domain'}; }
4470: if (!$uname) { $uname=$env{'user.name'}; }
1.466 albertel 4471: my $id=$uname.':'.$udom;
4472: my ($names,$cached)=&Apache::lonnet::is_cached_new('emailscache',$id);
4473: if ($cached) {
4474: return %{$names};
4475: } else {
4476: my %loadnames=&Apache::lonnet::get('environment',
4477: ['notification','critnotification',
4478: 'permanentemail'],
4479: $udom,$uname);
4480: &Apache::lonnet::do_cache_new('emailscache',$id,\%loadnames);
4481: return %loadnames;
4482: }
4483: }
4484:
1.551 albertel 4485: sub flush_email_cache {
4486: my ($uname,$udom)=@_;
4487: if (!$udom) { $udom =$env{'user.domain'}; }
4488: if (!$uname) { $uname=$env{'user.name'}; }
4489: return if ($udom eq 'public' && $uname eq 'public');
4490: my $id=$uname.':'.$udom;
4491: &Apache::lonnet::devalidate_cache_new('emailscache',$id);
4492: }
4493:
1.728 raeburn 4494: # -------------------------------------------------------------------- getlangs
4495:
4496: =pod
4497:
4498: =item * &getlangs($uname,$udom)
4499:
4500: Gets a user's language preference and returns it as a hash with key:
4501: language.
4502:
4503: =cut
4504:
4505:
4506: sub getlangs {
4507: my ($uname,$udom) = @_;
4508: if (!$udom) { $udom =$env{'user.domain'}; }
4509: if (!$uname) { $uname=$env{'user.name'}; }
4510: my $id=$uname.':'.$udom;
4511: my ($langs,$cached)=&Apache::lonnet::is_cached_new('userlangs',$id);
4512: if ($cached) {
4513: return %{$langs};
4514: } else {
4515: my %loadlangs=&Apache::lonnet::get('environment',['languages'],
4516: $udom,$uname);
4517: &Apache::lonnet::do_cache_new('userlangs',$id,\%loadlangs);
4518: return %loadlangs;
4519: }
4520: }
4521:
4522: sub flush_langs_cache {
4523: my ($uname,$udom)=@_;
4524: if (!$udom) { $udom =$env{'user.domain'}; }
4525: if (!$uname) { $uname=$env{'user.name'}; }
4526: return if ($udom eq 'public' && $uname eq 'public');
4527: my $id=$uname.':'.$udom;
4528: &Apache::lonnet::devalidate_cache_new('userlangs',$id);
4529: }
4530:
1.61 www 4531: # ------------------------------------------------------------------ Screenname
1.81 albertel 4532:
4533: =pod
4534:
1.648 raeburn 4535: =item * &screenname($uname,$udom)
1.81 albertel 4536:
4537: Gets a users screenname and returns it as a string
4538:
4539: =cut
1.61 www 4540:
4541: sub screenname {
4542: my ($uname,$udom)=@_;
1.258 albertel 4543: if ($uname eq $env{'user.name'} &&
4544: $udom eq $env{'user.domain'}) {return $env{'environment.screenname'};}
1.212 albertel 4545: my %names=&Apache::lonnet::get('environment',['screenname'],$udom,$uname);
1.68 albertel 4546: return $names{'screenname'};
1.62 www 4547: }
4548:
1.212 albertel 4549:
1.802 bisitz 4550: # ------------------------------------------------------------- Confirm Wrapper
4551: =pod
4552:
1.1142 raeburn 4553: =item * &confirmwrapper($message)
1.802 bisitz 4554:
4555: Wrap messages about completion of operation in box
4556:
4557: =cut
4558:
4559: sub confirmwrapper {
4560: my ($message)=@_;
4561: if ($message) {
4562: return "\n".'<div class="LC_confirm_box">'."\n"
4563: .$message."\n"
4564: .'</div>'."\n";
4565: } else {
4566: return $message;
4567: }
4568: }
4569:
1.62 www 4570: # ------------------------------------------------------------- Message Wrapper
4571:
4572: sub messagewrapper {
1.369 www 4573: my ($link,$username,$domain,$subject,$text)=@_;
1.62 www 4574: return
1.441 albertel 4575: '<a href="/adm/email?compose=individual&'.
4576: 'recname='.$username.'&recdom='.$domain.
4577: '&subject='.&escape($subject).'&text='.&escape($text).'" '.
1.200 matthew 4578: 'title="'.&mt('Send message').'">'.$link.'</a>';
1.74 www 4579: }
1.802 bisitz 4580:
1.74 www 4581: # --------------------------------------------------------------- Notes Wrapper
4582:
4583: sub noteswrapper {
4584: my ($link,$un,$do)=@_;
4585: return
1.896 amueller 4586: "<a href='/adm/email?recordftf=retrieve&recname=$un&recdom=$do'>$link</a>";
1.62 www 4587: }
1.802 bisitz 4588:
1.62 www 4589: # ------------------------------------------------------------- Aboutme Wrapper
4590:
4591: sub aboutmewrapper {
1.1070 raeburn 4592: my ($link,$username,$domain,$target,$class)=@_;
1.447 raeburn 4593: if (!defined($username) && !defined($domain)) {
4594: return;
4595: }
1.1096 raeburn 4596: return '<a href="/adm/'.$domain.'/'.$username.'/aboutme"'.
1.1070 raeburn 4597: ($target?' target="'.$target.'"':'').($class?' class="'.$class.'"':'').' title="'.&mt("View this user's personal information page").'">'.$link.'</a>';
1.62 www 4598: }
4599:
4600: # ------------------------------------------------------------ Syllabus Wrapper
4601:
4602: sub syllabuswrapper {
1.707 bisitz 4603: my ($linktext,$coursedir,$domain)=@_;
1.208 matthew 4604: return qq{<a href="/public/$domain/$coursedir/syllabus">$linktext</a>};
1.61 www 4605: }
1.14 harris41 4606:
1.1397 raeburn 4607: # -----------------------------------------------------------------------------
4608:
1.1396 raeburn 4609: sub aboutme_on {
4610: my ($uname,$udom)=@_;
4611: unless ($uname) { $uname=$env{'user.name'}; }
4612: unless ($udom) { $udom=$env{'user.domain'}; }
4613: return if ($udom eq 'public' && $uname eq 'public');
4614: my $hashkey=$uname.':'.$udom;
4615: my ($aboutme,$cached)=&Apache::lonnet::is_cached_new('aboutme',$hashkey);
4616: if ($cached) {
4617: return $aboutme;
4618: }
4619: $aboutme = &Apache::lonnet::usertools_access($uname,$udom,'aboutme');
4620: &Apache::lonnet::do_cache_new('aboutme',$hashkey,$aboutme,3600);
4621: return $aboutme;
4622: }
4623:
4624: sub devalidate_aboutme_cache {
4625: my ($uname,$udom)=@_;
4626: if (!$udom) { $udom =$env{'user.domain'}; }
4627: if (!$uname) { $uname=$env{'user.name'}; }
4628: return if ($udom eq 'public' && $uname eq 'public');
4629: my $id=$uname.':'.$udom;
4630: &Apache::lonnet::devalidate_cache_new('aboutme',$id);
4631: }
4632:
1.208 matthew 4633: sub track_student_link {
1.887 raeburn 4634: my ($linktext,$sname,$sdom,$target,$start,$only_body) = @_;
1.268 albertel 4635: my $link ="/adm/trackstudent?";
1.208 matthew 4636: my $title = 'View recent activity';
4637: if (defined($sname) && $sname !~ /^\s*$/ &&
4638: defined($sdom) && $sdom !~ /^\s*$/) {
1.268 albertel 4639: $link .= "selected_student=$sname:$sdom";
1.208 matthew 4640: $title .= ' of this student';
1.268 albertel 4641: }
1.208 matthew 4642: if (defined($target) && $target !~ /^\s*$/) {
4643: $target = qq{target="$target"};
4644: } else {
4645: $target = '';
4646: }
1.268 albertel 4647: if ($start) { $link.='&start='.$start; }
1.887 raeburn 4648: if ($only_body) { $link .= '&only_body=1'; }
1.554 albertel 4649: $title = &mt($title);
4650: $linktext = &mt($linktext);
1.448 albertel 4651: return qq{<a href="$link" title="$title" $target>$linktext</a>}.
4652: &help_open_topic('View_recent_activity');
1.208 matthew 4653: }
4654:
1.781 raeburn 4655: sub slot_reservations_link {
4656: my ($linktext,$sname,$sdom,$target) = @_;
4657: my $link ="/adm/slotrequest?command=showresv&origin=aboutme";
4658: my $title = 'View slot reservation history';
4659: if (defined($sname) && $sname !~ /^\s*$/ &&
4660: defined($sdom) && $sdom !~ /^\s*$/) {
4661: $link .= "&uname=$sname&udom=$sdom";
4662: $title .= ' of this student';
4663: }
4664: if (defined($target) && $target !~ /^\s*$/) {
4665: $target = qq{target="$target"};
4666: } else {
4667: $target = '';
4668: }
4669: $title = &mt($title);
4670: $linktext = &mt($linktext);
4671: return qq{<a href="$link" title="$title" $target>$linktext</a>};
4672: # FIXME uncomment when help item created: &help_open_topic('Slot_Reservation_History');
4673:
4674: }
4675:
1.508 www 4676: # ===================================================== Display a student photo
4677:
4678:
1.509 albertel 4679: sub student_image_tag {
1.508 www 4680: my ($domain,$user)=@_;
4681: my $imgsrc=&Apache::lonnet::studentphoto($domain,$user,'jpg');
4682: if (($imgsrc) && ($imgsrc ne '/adm/lonKaputt/lonlogo_broken.gif')) {
4683: return '<img src="'.$imgsrc.'" align="right" />';
4684: } else {
4685: return '';
4686: }
4687: }
4688:
1.112 bowersj2 4689: =pod
4690:
4691: =back
4692:
4693: =head1 Access .tab File Data
4694:
4695: =over 4
4696:
1.648 raeburn 4697: =item * &languageids()
1.112 bowersj2 4698:
4699: returns list of all language ids
4700:
4701: =cut
4702:
1.14 harris41 4703: sub languageids {
1.16 harris41 4704: return sort(keys(%language));
1.14 harris41 4705: }
4706:
1.112 bowersj2 4707: =pod
4708:
1.648 raeburn 4709: =item * &languagedescription()
1.112 bowersj2 4710:
4711: returns description of a specified language id
4712:
4713: =cut
4714:
1.14 harris41 4715: sub languagedescription {
1.125 www 4716: my $code=shift;
4717: return ($supported_language{$code}?'* ':'').
4718: $language{$code}.
1.126 www 4719: ($supported_language{$code}?' ('.&mt('interface available').')':'');
1.145 www 4720: }
4721:
1.1048 foxr 4722: =pod
4723:
4724: =item * &plainlanguagedescription
4725:
4726: Returns both the plain language description (e.g. 'Creoles and Pidgins, English-based (Other)')
4727: and the language character encoding (e.g. ISO) separated by a ' - ' string.
4728:
4729: =cut
4730:
1.145 www 4731: sub plainlanguagedescription {
4732: my $code=shift;
4733: return $language{$code};
4734: }
4735:
1.1048 foxr 4736: =pod
4737:
4738: =item * &supportedlanguagecode
4739:
4740: Returns the supported language code (e.g. sptutf maps to pt) given a language
4741: code.
4742:
4743: =cut
4744:
1.145 www 4745: sub supportedlanguagecode {
4746: my $code=shift;
4747: return $supported_language{$code};
1.97 www 4748: }
4749:
1.112 bowersj2 4750: =pod
4751:
1.1048 foxr 4752: =item * &latexlanguage()
4753:
4754: Given a language key code returns the correspondnig language to use
4755: to select the correct hyphenation on LaTeX printouts. This is undef if there
4756: is no supported hyphenation for the language code.
4757:
4758: =cut
4759:
4760: sub latexlanguage {
4761: my $code = shift;
4762: return $latex_language{$code};
4763: }
4764:
4765: =pod
4766:
4767: =item * &latexhyphenation()
4768:
4769: Same as above but what's supplied is the language as it might be stored
4770: in the metadata.
4771:
4772: =cut
4773:
4774: sub latexhyphenation {
4775: my $key = shift;
4776: return $latex_language_bykey{$key};
4777: }
4778:
4779: =pod
4780:
1.648 raeburn 4781: =item * ©rightids()
1.112 bowersj2 4782:
4783: returns list of all copyrights
4784:
4785: =cut
4786:
4787: sub copyrightids {
4788: return sort(keys(%cprtag));
4789: }
4790:
4791: =pod
4792:
1.648 raeburn 4793: =item * ©rightdescription()
1.112 bowersj2 4794:
4795: returns description of a specified copyright id
4796:
4797: =cut
4798:
4799: sub copyrightdescription {
1.166 www 4800: return &mt($cprtag{shift(@_)});
1.112 bowersj2 4801: }
1.197 matthew 4802:
4803: =pod
4804:
1.648 raeburn 4805: =item * &source_copyrightids()
1.192 taceyjo1 4806:
4807: returns list of all source copyrights
4808:
4809: =cut
4810:
4811: sub source_copyrightids {
4812: return sort(keys(%scprtag));
4813: }
4814:
4815: =pod
4816:
1.648 raeburn 4817: =item * &source_copyrightdescription()
1.192 taceyjo1 4818:
4819: returns description of a specified source copyright id
4820:
4821: =cut
4822:
4823: sub source_copyrightdescription {
4824: return &mt($scprtag{shift(@_)});
4825: }
1.112 bowersj2 4826:
4827: =pod
4828:
1.648 raeburn 4829: =item * &filecategories()
1.112 bowersj2 4830:
4831: returns list of all file categories
4832:
4833: =cut
4834:
4835: sub filecategories {
4836: return sort(keys(%category_extensions));
4837: }
4838:
4839: =pod
4840:
1.648 raeburn 4841: =item * &filecategorytypes()
1.112 bowersj2 4842:
4843: returns list of file types belonging to a given file
4844: category
4845:
4846: =cut
4847:
4848: sub filecategorytypes {
1.356 albertel 4849: my ($cat) = @_;
1.1248 raeburn 4850: if (ref($category_extensions{lc($cat)}) eq 'ARRAY') {
4851: return @{$category_extensions{lc($cat)}};
4852: } else {
4853: return ();
4854: }
1.112 bowersj2 4855: }
4856:
4857: =pod
4858:
1.648 raeburn 4859: =item * &fileembstyle()
1.112 bowersj2 4860:
4861: returns embedding style for a specified file type
4862:
4863: =cut
4864:
4865: sub fileembstyle {
4866: return $fe{lc(shift(@_))};
1.169 www 4867: }
4868:
1.351 www 4869: sub filemimetype {
4870: return $fm{lc(shift(@_))};
4871: }
4872:
1.169 www 4873:
4874: sub filecategoryselect {
1.1471 ! raeburn 4875: my ($name,$value,$id)=@_;
1.189 matthew 4876: return &select_form($value,$name,
1.1471 ! raeburn 4877: {'' => &mt('Any category'), map { $_,$_ } sort(keys(%category_extensions))},
! 4878: '','',$id);
1.112 bowersj2 4879: }
4880:
4881: =pod
4882:
1.648 raeburn 4883: =item * &filedescription()
1.112 bowersj2 4884:
4885: returns description for a specified file type
4886:
4887: =cut
4888:
4889: sub filedescription {
1.188 matthew 4890: my $file_description = $fd{lc(shift())};
4891: $file_description =~ s:([\[\]]):~$1:g;
4892: return &mt($file_description);
1.112 bowersj2 4893: }
4894:
4895: =pod
4896:
1.648 raeburn 4897: =item * &filedescriptionex()
1.112 bowersj2 4898:
4899: returns description for a specified file type with
4900: extra formatting
4901:
4902: =cut
4903:
4904: sub filedescriptionex {
4905: my $ex=shift;
1.188 matthew 4906: my $file_description = $fd{lc($ex)};
4907: $file_description =~ s:([\[\]]):~$1:g;
4908: return '.'.$ex.' '.&mt($file_description);
1.112 bowersj2 4909: }
4910:
4911: # End of .tab access
4912: =pod
4913:
4914: =back
4915:
4916: =cut
4917:
4918: # ------------------------------------------------------------------ File Types
4919: sub fileextensions {
4920: return sort(keys(%fe));
4921: }
4922:
1.97 www 4923: # ----------------------------------------------------------- Display Languages
4924: # returns a hash with all desired display languages
4925: #
4926:
4927: sub display_languages {
4928: my %languages=();
1.695 raeburn 4929: foreach my $lang (&Apache::lonlocal::preferred_languages()) {
1.356 albertel 4930: $languages{$lang}=1;
1.97 www 4931: }
4932: &get_unprocessed_cgi($ENV{'QUERY_STRING'},['displaylanguage']);
1.258 albertel 4933: if ($env{'form.displaylanguage'}) {
1.356 albertel 4934: foreach my $lang (split(/\s*(\,|\;|\:)\s*/,$env{'form.displaylanguage'})) {
4935: $languages{$lang}=1;
1.97 www 4936: }
4937: }
4938: return %languages;
1.14 harris41 4939: }
4940:
1.582 albertel 4941: sub languages {
4942: my ($possible_langs) = @_;
1.695 raeburn 4943: my @preferred_langs = &Apache::lonlocal::preferred_languages();
1.582 albertel 4944: if (!ref($possible_langs)) {
4945: if( wantarray ) {
4946: return @preferred_langs;
4947: } else {
4948: return $preferred_langs[0];
4949: }
4950: }
4951: my %possibilities = map { $_ => 1 } (@$possible_langs);
4952: my @preferred_possibilities;
4953: foreach my $preferred_lang (@preferred_langs) {
4954: if (exists($possibilities{$preferred_lang})) {
4955: push(@preferred_possibilities, $preferred_lang);
4956: }
4957: }
4958: if( wantarray ) {
4959: return @preferred_possibilities;
4960: }
4961: return $preferred_possibilities[0];
4962: }
4963:
1.742 raeburn 4964: sub user_lang {
4965: my ($touname,$toudom,$fromcid) = @_;
4966: my @userlangs;
4967: if (($fromcid ne '') && ($env{'course.'.$fromcid.'.languages'} ne '')) {
4968: @userlangs=(@userlangs,split(/\s*(\,|\;|\:)\s*/,
4969: $env{'course.'.$fromcid.'.languages'}));
4970: } else {
4971: my %langhash = &getlangs($touname,$toudom);
4972: if ($langhash{'languages'} ne '') {
4973: @userlangs = split(/\s*(\,|\;|\:)\s*/,$langhash{'languages'});
4974: } else {
4975: my %domdefs = &Apache::lonnet::get_domain_defaults($toudom);
4976: if ($domdefs{'lang_def'} ne '') {
4977: @userlangs = ($domdefs{'lang_def'});
4978: }
4979: }
4980: }
4981: my @languages=&Apache::lonlocal::get_genlanguages(@userlangs);
4982: my $user_lh = Apache::localize->get_handle(@languages);
4983: return $user_lh;
4984: }
4985:
4986:
1.112 bowersj2 4987: ###############################################################
4988: ## Student Answer Attempts ##
4989: ###############################################################
4990:
4991: =pod
4992:
4993: =head1 Alternate Problem Views
4994:
4995: =over 4
4996:
1.648 raeburn 4997: =item * &get_previous_attempt($symb, $username, $domain, $course,
1.1199 raeburn 4998: $getattempt, $regexp, $gradesub, $usec, $identifier)
1.112 bowersj2 4999:
5000: Return string with previous attempt on problem. Arguments:
5001:
5002: =over 4
5003:
5004: =item * $symb: Problem, including path
5005:
5006: =item * $username: username of the desired student
5007:
5008: =item * $domain: domain of the desired student
1.14 harris41 5009:
1.112 bowersj2 5010: =item * $course: Course ID
1.14 harris41 5011:
1.112 bowersj2 5012: =item * $getattempt: Leave blank for all attempts, otherwise put
5013: something
1.14 harris41 5014:
1.112 bowersj2 5015: =item * $regexp: if string matches this regexp, the string will be
5016: sent to $gradesub
1.14 harris41 5017:
1.112 bowersj2 5018: =item * $gradesub: routine that processes the string if it matches $regexp
1.14 harris41 5019:
1.1199 raeburn 5020: =item * $usec: section of the desired student
5021:
5022: =item * $identifier: counter for student (multiple students one problem) or
5023: problem (one student; whole sequence).
5024:
1.112 bowersj2 5025: =back
1.14 harris41 5026:
1.112 bowersj2 5027: The output string is a table containing all desired attempts, if any.
1.16 harris41 5028:
1.112 bowersj2 5029: =cut
1.1 albertel 5030:
5031: sub get_previous_attempt {
1.1199 raeburn 5032: my ($symb,$username,$domain,$course,$getattempt,$regexp,$gradesub,$usec,$identifier)=@_;
1.1 albertel 5033: my $prevattempts='';
1.43 ng 5034: no strict 'refs';
1.1 albertel 5035: if ($symb) {
1.3 albertel 5036: my (%returnhash)=
5037: &Apache::lonnet::restore($symb,$course,$domain,$username);
1.1 albertel 5038: if ($returnhash{'version'}) {
5039: my %lasthash=();
5040: my $version;
5041: for ($version=1;$version<=$returnhash{'version'};$version++) {
1.1212 raeburn 5042: foreach my $key (reverse(sort(split(/\:/,$returnhash{$version.':keys'})))) {
5043: if ($key =~ /\.rawrndseed$/) {
5044: my ($id) = ($key =~ /^(.+)\.rawrndseed$/);
5045: $lasthash{$id.'.rndseed'} = $returnhash{$version.':'.$key};
5046: } else {
5047: $lasthash{$key}=$returnhash{$version.':'.$key};
5048: }
1.19 harris41 5049: }
1.1 albertel 5050: }
1.596 albertel 5051: $prevattempts=&start_data_table().&start_data_table_header_row();
5052: $prevattempts.='<th>'.&mt('History').'</th>';
1.1199 raeburn 5053: my (%typeparts,%lasthidden,%regraded,%hidestatus);
1.945 raeburn 5054: my $showsurv=&Apache::lonnet::allowed('vas',$env{'request.course.id'});
1.356 albertel 5055: foreach my $key (sort(keys(%lasthash))) {
5056: my ($ign,@parts) = split(/\./,$key);
1.41 ng 5057: if ($#parts > 0) {
1.31 albertel 5058: my $data=$parts[-1];
1.989 raeburn 5059: next if ($data eq 'foilorder');
1.31 albertel 5060: pop(@parts);
1.1010 www 5061: $prevattempts.='<th>'.&mt('Part ').join('.',@parts).'<br />'.$data.' </th>';
1.945 raeburn 5062: if ($data eq 'type') {
5063: unless ($showsurv) {
5064: my $id = join(',',@parts);
5065: $typeparts{$ign.'.'.$id} = $lasthash{$key};
1.978 raeburn 5066: if (($lasthash{$key} eq 'anonsurvey') || ($lasthash{$key} eq 'anonsurveycred')) {
5067: $lasthidden{$ign.'.'.$id} = 1;
5068: }
1.945 raeburn 5069: }
1.1199 raeburn 5070: if ($identifier ne '') {
5071: my $id = join(',',@parts);
5072: if (&Apache::lonnet::EXT("resource.$id.problemstatus",$symb,
5073: $domain,$username,$usec,undef,$course) =~ /^no/) {
5074: $hidestatus{$ign.'.'.$id} = 1;
5075: }
5076: }
5077: } elsif ($data eq 'regrader') {
5078: if (($identifier ne '') && (@parts)) {
1.1200 raeburn 5079: my $id = join(',',@parts);
5080: $regraded{$ign.'.'.$id} = 1;
1.1199 raeburn 5081: }
1.1010 www 5082: }
1.31 albertel 5083: } else {
1.41 ng 5084: if ($#parts == 0) {
5085: $prevattempts.='<th>'.$parts[0].'</th>';
5086: } else {
5087: $prevattempts.='<th>'.$ign.'</th>';
5088: }
1.31 albertel 5089: }
1.16 harris41 5090: }
1.596 albertel 5091: $prevattempts.=&end_data_table_header_row();
1.40 ng 5092: if ($getattempt eq '') {
1.1199 raeburn 5093: my (%solved,%resets,%probstatus);
1.1200 raeburn 5094: if (($identifier ne '') && (keys(%regraded) > 0)) {
5095: for ($version=1;$version<=$returnhash{'version'};$version++) {
5096: foreach my $id (keys(%regraded)) {
5097: if (($returnhash{$version.':'.$id.'.regrader'}) &&
5098: ($returnhash{$version.':'.$id.'.tries'} eq '') &&
5099: ($returnhash{$version.':'.$id.'.award'} eq '')) {
5100: push(@{$resets{$id}},$version);
1.1199 raeburn 5101: }
5102: }
5103: }
1.1200 raeburn 5104: }
5105: for ($version=1;$version<=$returnhash{'version'};$version++) {
1.1199 raeburn 5106: my (@hidden,@unsolved);
1.945 raeburn 5107: if (%typeparts) {
5108: foreach my $id (keys(%typeparts)) {
1.1199 raeburn 5109: if (($returnhash{$version.':'.$id.'.type'} eq 'anonsurvey') ||
5110: ($returnhash{$version.':'.$id.'.type'} eq 'anonsurveycred')) {
1.945 raeburn 5111: push(@hidden,$id);
1.1199 raeburn 5112: } elsif ($identifier ne '') {
5113: unless (($returnhash{$version.':'.$id.'.type'} eq 'survey') ||
5114: ($returnhash{$version.':'.$id.'.type'} eq 'surveycred') ||
5115: ($hidestatus{$id})) {
1.1200 raeburn 5116: next if ((ref($resets{$id}) eq 'ARRAY') && grep(/^\Q$version\E$/,@{$resets{$id}}));
1.1199 raeburn 5117: if ($returnhash{$version.':'.$id.'.solved'} eq 'correct_by_student') {
5118: push(@{$solved{$id}},$version);
5119: } elsif (($returnhash{$version.':'.$id.'.solved'} ne '') &&
5120: (ref($solved{$id}) eq 'ARRAY')) {
5121: my $skip;
5122: if (ref($resets{$id}) eq 'ARRAY') {
5123: foreach my $reset (@{$resets{$id}}) {
5124: if ($reset > $solved{$id}[-1]) {
5125: $skip=1;
5126: last;
5127: }
5128: }
5129: }
5130: unless ($skip) {
5131: my ($ign,$partslist) = split(/\./,$id,2);
5132: push(@unsolved,$partslist);
5133: }
5134: }
5135: }
1.945 raeburn 5136: }
5137: }
5138: }
5139: $prevattempts.=&start_data_table_row().
1.1199 raeburn 5140: '<td>'.&mt('Transaction [_1]',$version);
5141: if (@unsolved) {
5142: $prevattempts .= '<span class="LC_nobreak"><label>'.
5143: '<input type="checkbox" name="HIDE'.$identifier.'" value="'.$version.':'.join('_',@unsolved).'" />'.
5144: &mt('Hide').'</label></span>';
5145: }
5146: $prevattempts .= '</td>';
1.945 raeburn 5147: if (@hidden) {
5148: foreach my $key (sort(keys(%lasthash))) {
1.989 raeburn 5149: next if ($key =~ /\.foilorder$/);
1.945 raeburn 5150: my $hide;
5151: foreach my $id (@hidden) {
5152: if ($key =~ /^\Q$id\E/) {
5153: $hide = 1;
5154: last;
5155: }
5156: }
5157: if ($hide) {
5158: my ($id,$data) = ($key =~ /^(.+)\.([^.]+)$/);
5159: if (($data eq 'award') || ($data eq 'awarddetail')) {
5160: my $value = &format_previous_attempt_value($key,
5161: $returnhash{$version.':'.$key});
1.1173 kruse 5162: $prevattempts.='<td>'.$value.' </td>';
1.945 raeburn 5163: } else {
5164: $prevattempts.='<td> </td>';
5165: }
5166: } else {
5167: if ($key =~ /\./) {
1.1212 raeburn 5168: my $value = $returnhash{$version.':'.$key};
5169: if ($key =~ /\.rndseed$/) {
5170: my ($id) = ($key =~ /^(.+)\.[^.]+$/);
5171: if (exists($returnhash{$version.':'.$id.'.rawrndseed'})) {
5172: $value = $returnhash{$version.':'.$id.'.rawrndseed'};
5173: }
5174: }
5175: $prevattempts.='<td>'.&format_previous_attempt_value($key,$value).
5176: ' </td>';
1.945 raeburn 5177: } else {
5178: $prevattempts.='<td> </td>';
5179: }
5180: }
5181: }
5182: } else {
5183: foreach my $key (sort(keys(%lasthash))) {
1.989 raeburn 5184: next if ($key =~ /\.foilorder$/);
1.1212 raeburn 5185: my $value = $returnhash{$version.':'.$key};
5186: if ($key =~ /\.rndseed$/) {
5187: my ($id) = ($key =~ /^(.+)\.[^.]+$/);
5188: if (exists($returnhash{$version.':'.$id.'.rawrndseed'})) {
5189: $value = $returnhash{$version.':'.$id.'.rawrndseed'};
5190: }
5191: }
5192: $prevattempts.='<td>'.&format_previous_attempt_value($key,$value).
5193: ' </td>';
1.945 raeburn 5194: }
5195: }
5196: $prevattempts.=&end_data_table_row();
1.40 ng 5197: }
1.1 albertel 5198: }
1.945 raeburn 5199: my @currhidden = keys(%lasthidden);
1.596 albertel 5200: $prevattempts.=&start_data_table_row().'<td>'.&mt('Current').'</td>';
1.356 albertel 5201: foreach my $key (sort(keys(%lasthash))) {
1.989 raeburn 5202: next if ($key =~ /\.foilorder$/);
1.945 raeburn 5203: if (%typeparts) {
5204: my $hidden;
5205: foreach my $id (@currhidden) {
5206: if ($key =~ /^\Q$id\E/) {
5207: $hidden = 1;
5208: last;
5209: }
5210: }
5211: if ($hidden) {
5212: my ($id,$data) = ($key =~ /^(.+)\.([^.]+)$/);
5213: if (($data eq 'award') || ($data eq 'awarddetail')) {
5214: my $value = &format_previous_attempt_value($key,$lasthash{$key});
5215: if ($key =~/$regexp$/ && (defined &$gradesub)) {
5216: $value = &$gradesub($value);
5217: }
1.1173 kruse 5218: $prevattempts.='<td>'. $value.' </td>';
1.945 raeburn 5219: } else {
5220: $prevattempts.='<td> </td>';
5221: }
5222: } else {
5223: my $value = &format_previous_attempt_value($key,$lasthash{$key});
5224: if ($key =~/$regexp$/ && (defined &$gradesub)) {
5225: $value = &$gradesub($value);
5226: }
1.1173 kruse 5227: $prevattempts.='<td>'.$value.' </td>';
1.945 raeburn 5228: }
5229: } else {
5230: my $value = &format_previous_attempt_value($key,$lasthash{$key});
5231: if ($key =~/$regexp$/ && (defined &$gradesub)) {
5232: $value = &$gradesub($value);
5233: }
1.1173 kruse 5234: $prevattempts.='<td>'.$value.' </td>';
1.945 raeburn 5235: }
1.16 harris41 5236: }
1.596 albertel 5237: $prevattempts.= &end_data_table_row().&end_data_table();
1.1 albertel 5238: } else {
1.1305 raeburn 5239: my $msg;
5240: if ($symb =~ /ext\.tool$/) {
5241: $msg = &mt('No grade passed back.');
5242: } else {
5243: $msg = &mt('Nothing submitted - no attempts.');
5244: }
1.596 albertel 5245: $prevattempts=
5246: &start_data_table().&start_data_table_row().
1.1305 raeburn 5247: '<td>'.$msg.'</td>'.
1.596 albertel 5248: &end_data_table_row().&end_data_table();
1.1 albertel 5249: }
5250: } else {
1.596 albertel 5251: $prevattempts=
5252: &start_data_table().&start_data_table_row().
5253: '<td>'.&mt('No data.').'</td>'.
5254: &end_data_table_row().&end_data_table();
1.1 albertel 5255: }
1.10 albertel 5256: }
5257:
1.581 albertel 5258: sub format_previous_attempt_value {
5259: my ($key,$value) = @_;
1.1011 www 5260: if (($key =~ /timestamp/) || ($key=~/duedate/)) {
1.1173 kruse 5261: $value = &Apache::lonlocal::locallocaltime($value);
1.581 albertel 5262: } elsif (ref($value) eq 'ARRAY') {
1.1173 kruse 5263: $value = &HTML::Entities::encode('('.join(', ', @{ $value }).')','"<>&');
1.988 raeburn 5264: } elsif ($key =~ /answerstring$/) {
5265: my %answers = &Apache::lonnet::str2hash($value);
1.1173 kruse 5266: my @answer = %answers;
5267: %answers = map {&HTML::Entities::encode($_, '"<>&')} @answer;
1.988 raeburn 5268: my @anskeys = sort(keys(%answers));
5269: if (@anskeys == 1) {
5270: my $answer = $answers{$anskeys[0]};
1.1001 raeburn 5271: if ($answer =~ m{\0}) {
5272: $answer =~ s{\0}{,}g;
1.988 raeburn 5273: }
5274: my $tag_internal_answer_name = 'INTERNAL';
5275: if ($anskeys[0] eq $tag_internal_answer_name) {
5276: $value = $answer;
5277: } else {
5278: $value = $anskeys[0].'='.$answer;
5279: }
5280: } else {
5281: foreach my $ans (@anskeys) {
5282: my $answer = $answers{$ans};
1.1001 raeburn 5283: if ($answer =~ m{\0}) {
5284: $answer =~ s{\0}{,}g;
1.988 raeburn 5285: }
5286: $value .= $ans.'='.$answer.'<br />';;
5287: }
5288: }
1.581 albertel 5289: } else {
1.1173 kruse 5290: $value = &HTML::Entities::encode(&unescape($value), '"<>&');
1.581 albertel 5291: }
5292: return $value;
5293: }
5294:
5295:
1.107 albertel 5296: sub relative_to_absolute {
5297: my ($url,$output)=@_;
5298: my $parser=HTML::TokeParser->new(\$output);
5299: my $token;
5300: my $thisdir=$url;
5301: my @rlinks=();
5302: while ($token=$parser->get_token) {
5303: if ($token->[0] eq 'S') {
5304: if ($token->[1] eq 'a') {
5305: if ($token->[2]->{'href'}) {
5306: $rlinks[$#rlinks+1]=$token->[2]->{'href'};
5307: }
5308: } elsif ($token->[1] eq 'img' || $token->[1] eq 'embed' ) {
5309: $rlinks[$#rlinks+1]=$token->[2]->{'src'};
5310: } elsif ($token->[1] eq 'base') {
5311: $thisdir=$token->[2]->{'href'};
5312: }
5313: }
5314: }
5315: $thisdir=~s-/[^/]*$--;
1.356 albertel 5316: foreach my $link (@rlinks) {
1.726 raeburn 5317: unless (($link=~/^https?\:\/\//i) ||
1.356 albertel 5318: ($link=~/^\//) ||
5319: ($link=~/^javascript:/i) ||
5320: ($link=~/^mailto:/i) ||
5321: ($link=~/^\#/)) {
5322: my $newlocation=&Apache::lonnet::hreflocation($thisdir,$link);
5323: $output=~s/(\"|\'|\=\s*)\Q$link\E(\"|\'|\s|\>)/$1$newlocation$2/;
1.107 albertel 5324: }
5325: }
5326: # -------------------------------------------------- Deal with Applet codebases
5327: $output=~s/(\<applet[^\>]+)(codebase\=[^\S\>]+)*([^\>]*)\>/$1.($2?$2:' codebase="'.$thisdir.'"').$3.'>'/gei;
5328: return $output;
5329: }
5330:
1.112 bowersj2 5331: =pod
5332:
1.648 raeburn 5333: =item * &get_student_view()
1.112 bowersj2 5334:
5335: show a snapshot of what student was looking at
5336:
5337: =cut
5338:
1.10 albertel 5339: sub get_student_view {
1.186 albertel 5340: my ($symb,$username,$domain,$courseid,$target,$moreenv) = @_;
1.114 www 5341: my ($map,$id,$feedurl) = &Apache::lonnet::decode_symb($symb);
1.186 albertel 5342: my (%form);
1.10 albertel 5343: my @elements=('symb','courseid','domain','username');
5344: foreach my $element (@elements) {
1.186 albertel 5345: $form{'grade_'.$element}=eval '$'.$element #'
1.10 albertel 5346: }
1.186 albertel 5347: if (defined($moreenv)) {
5348: %form=(%form,%{$moreenv});
5349: }
1.236 albertel 5350: if (defined($target)) { $form{'grade_target'} = $target; }
1.107 albertel 5351: $feedurl=&Apache::lonnet::clutter($feedurl);
1.1306 raeburn 5352: if (($feedurl =~ /ext\.tool$/) && ($target eq 'tex')) {
5353: $feedurl =~ s{^/adm/wrapper}{};
5354: }
1.650 www 5355: my ($userview,$response)=&Apache::lonnet::ssi_body($feedurl,%form);
1.11 albertel 5356: $userview=~s/\<body[^\>]*\>//gi;
5357: $userview=~s/\<\/body\>//gi;
5358: $userview=~s/\<html\>//gi;
5359: $userview=~s/\<\/html\>//gi;
5360: $userview=~s/\<head\>//gi;
5361: $userview=~s/\<\/head\>//gi;
1.1460 raeburn 5362: $userview=~s/\Q<div class="LC_landmark" role="main"\E/<div class="LC_landmark"/;
1.11 albertel 5363: $userview=~s/action\s*\=/would_be_action\=/gi;
1.107 albertel 5364: $userview=&relative_to_absolute($feedurl,$userview);
1.650 www 5365: if (wantarray) {
5366: return ($userview,$response);
5367: } else {
5368: return $userview;
5369: }
5370: }
5371:
5372: sub get_student_view_with_retries {
5373: my ($symb,$retries,$username,$domain,$courseid,$target,$moreenv) = @_;
5374:
5375: my $ok = 0; # True if we got a good response.
5376: my $content;
5377: my $response;
5378:
5379: # Try to get the student_view done. within the retries count:
5380:
5381: do {
5382: ($content, $response) = &get_student_view($symb,$username,$domain,$courseid,$target,$moreenv);
5383: $ok = $response->is_success;
5384: if (!$ok) {
5385: &Apache::lonnet::logthis("Failed get_student_view_with_retries on $symb: ".$response->is_success.', '.$response->code.', '.$response->message);
5386: }
5387: $retries--;
5388: } while (!$ok && ($retries > 0));
5389:
5390: if (!$ok) {
5391: $content = ''; # On error return an empty content.
5392: }
1.651 www 5393: if (wantarray) {
5394: return ($content, $response);
5395: } else {
5396: return $content;
5397: }
1.11 albertel 5398: }
5399:
1.1349 raeburn 5400: sub css_links {
5401: my ($currsymb,$level) = @_;
5402: my ($links,@symbs,%cssrefs,%httpref);
5403: if ($level eq 'map') {
5404: my $navmap = Apache::lonnavmaps::navmap->new();
5405: if (ref($navmap)) {
5406: my ($map,undef,$url)=&Apache::lonnet::decode_symb($currsymb);
5407: my @resources = $navmap->retrieveResources($map,sub { $_[0]->is_problem() },0,0);
5408: foreach my $res (@resources) {
5409: if (ref($res) && $res->symb()) {
5410: push(@symbs,$res->symb());
5411: }
5412: }
5413: }
5414: } else {
5415: @symbs = ($currsymb);
5416: }
5417: foreach my $symb (@symbs) {
5418: my $css_href = &Apache::lonnet::EXT('resource.0.cssfile',$symb);
5419: if ($css_href =~ /\S/) {
5420: unless ($css_href =~ m{https?://}) {
5421: my $url = (&Apache::lonnet::decode_symb($symb))[-1];
5422: my $proburl = &Apache::lonnet::clutter($url);
5423: my ($probdir) = ($proburl =~ m{(.+)/[^/]+$});
5424: unless ($css_href =~ m{^/}) {
5425: $css_href = &Apache::lonnet::hreflocation($probdir,$css_href);
5426: }
5427: if ($css_href =~ m{^/(res|uploaded)/}) {
5428: unless (($httpref{'httpref.'.$css_href}) ||
5429: (&Apache::lonnet::is_on_map($css_href))) {
5430: my $thisurl = $proburl;
5431: if ($env{'httpref.'.$proburl}) {
5432: $thisurl = $env{'httpref.'.$proburl};
5433: }
5434: $httpref{'httpref.'.$css_href} = $thisurl;
5435: }
5436: }
5437: }
5438: $cssrefs{$css_href} = 1;
5439: }
5440: }
5441: if (keys(%httpref)) {
5442: &Apache::lonnet::appenv(\%httpref);
5443: }
5444: if (keys(%cssrefs)) {
5445: foreach my $css_href (keys(%cssrefs)) {
5446: next unless ($css_href =~ m{^(/res/|/uploaded/|https?://)});
5447: $links .= '<link rel="stylesheet" type="text/css" href="'.$css_href.'" />'."\n";
5448: }
5449: }
5450: return $links;
5451: }
5452:
1.112 bowersj2 5453: =pod
5454:
1.648 raeburn 5455: =item * &get_student_answers()
1.112 bowersj2 5456:
5457: show a snapshot of how student was answering problem
5458:
5459: =cut
5460:
1.11 albertel 5461: sub get_student_answers {
1.100 sakharuk 5462: my ($symb,$username,$domain,$courseid,%form) = @_;
1.114 www 5463: my ($map,$id,$feedurl) = &Apache::lonnet::decode_symb($symb);
1.186 albertel 5464: my (%moreenv);
1.11 albertel 5465: my @elements=('symb','courseid','domain','username');
5466: foreach my $element (@elements) {
1.186 albertel 5467: $moreenv{'grade_'.$element}=eval '$'.$element #'
1.10 albertel 5468: }
1.186 albertel 5469: $moreenv{'grade_target'}='answer';
5470: %moreenv=(%form,%moreenv);
1.497 raeburn 5471: $feedurl = &Apache::lonnet::clutter($feedurl);
5472: my $userview=&Apache::lonnet::ssi($feedurl,%moreenv);
1.10 albertel 5473: return $userview;
1.1 albertel 5474: }
1.116 albertel 5475:
5476: =pod
5477:
5478: =item * &submlink()
5479:
1.242 albertel 5480: Inputs: $text $uname $udom $symb $target
1.116 albertel 5481:
5482: Returns: A link to grades.pm such as to see the SUBM view of a student
5483:
5484: =cut
5485:
5486: ###############################################
5487: sub submlink {
1.242 albertel 5488: my ($text,$uname,$udom,$symb,$target)=@_;
1.116 albertel 5489: if (!($uname && $udom)) {
5490: (my $cursymb, my $courseid,$udom,$uname)=
1.463 albertel 5491: &Apache::lonnet::whichuser($symb);
1.116 albertel 5492: if (!$symb) { $symb=$cursymb; }
5493: }
1.254 matthew 5494: if (!$symb) { $symb=&Apache::lonnet::symbread(); }
1.369 www 5495: $symb=&escape($symb);
1.960 bisitz 5496: if ($target) { $target=" target=\"$target\""; }
5497: return
5498: '<a href="/adm/grades?command=submission'.
5499: '&symb='.$symb.
5500: '&student='.$uname.
5501: '&userdom='.$udom.'"'.
5502: $target.'>'.$text.'</a>';
1.242 albertel 5503: }
5504: ##############################################
5505:
5506: =pod
5507:
5508: =item * &pgrdlink()
5509:
5510: Inputs: $text $uname $udom $symb $target
5511:
5512: Returns: A link to grades.pm such as to see the PGRD view of a student
5513:
5514: =cut
5515:
5516: ###############################################
5517: sub pgrdlink {
5518: my $link=&submlink(@_);
5519: $link=~s/(&command=submission)/$1&showgrading=yes/;
5520: return $link;
5521: }
5522: ##############################################
5523:
5524: =pod
5525:
5526: =item * &pprmlink()
5527:
5528: Inputs: $text $uname $udom $symb $target
5529:
5530: Returns: A link to parmset.pm such as to see the PPRM view of a
1.283 albertel 5531: student and a specific resource
1.242 albertel 5532:
5533: =cut
5534:
5535: ###############################################
5536: sub pprmlink {
5537: my ($text,$uname,$udom,$symb,$target)=@_;
5538: if (!($uname && $udom)) {
5539: (my $cursymb, my $courseid,$udom,$uname)=
1.463 albertel 5540: &Apache::lonnet::whichuser($symb);
1.242 albertel 5541: if (!$symb) { $symb=$cursymb; }
5542: }
1.254 matthew 5543: if (!$symb) { $symb=&Apache::lonnet::symbread(); }
1.369 www 5544: $symb=&escape($symb);
1.242 albertel 5545: if ($target) { $target="target=\"$target\""; }
1.595 albertel 5546: return '<a href="/adm/parmset?command=set&'.
5547: 'symb='.$symb.'&uname='.$uname.
5548: '&udom='.$udom.'" '.$target.'>'.$text.'</a>';
1.116 albertel 5549: }
5550: ##############################################
1.37 matthew 5551:
1.112 bowersj2 5552: =pod
5553:
5554: =back
5555:
5556: =cut
5557:
1.37 matthew 5558: ###############################################
1.51 www 5559:
5560:
5561: sub timehash {
1.687 raeburn 5562: my ($thistime) = @_;
5563: my $timezone = &Apache::lonlocal::gettimezone();
5564: my $dt = DateTime->from_epoch(epoch => $thistime)
5565: ->set_time_zone($timezone);
5566: my $wday = $dt->day_of_week();
5567: if ($wday == 7) { $wday = 0; }
5568: return ( 'second' => $dt->second(),
5569: 'minute' => $dt->minute(),
5570: 'hour' => $dt->hour(),
5571: 'day' => $dt->day_of_month(),
5572: 'month' => $dt->month(),
5573: 'year' => $dt->year(),
5574: 'weekday' => $wday,
5575: 'dayyear' => $dt->day_of_year(),
5576: 'dlsav' => $dt->is_dst() );
1.51 www 5577: }
5578:
1.370 www 5579: sub utc_string {
5580: my ($date)=@_;
1.371 www 5581: return strftime("%Y%m%dT%H%M%SZ",gmtime($date));
1.370 www 5582: }
5583:
1.51 www 5584: sub maketime {
5585: my %th=@_;
1.687 raeburn 5586: my ($epoch_time,$timezone,$dt);
5587: $timezone = &Apache::lonlocal::gettimezone();
5588: eval {
5589: $dt = DateTime->new( year => $th{'year'},
5590: month => $th{'month'},
5591: day => $th{'day'},
5592: hour => $th{'hour'},
5593: minute => $th{'minute'},
5594: second => $th{'second'},
5595: time_zone => $timezone,
5596: );
5597: };
5598: if (!$@) {
5599: $epoch_time = $dt->epoch;
5600: if ($epoch_time) {
5601: return $epoch_time;
5602: }
5603: }
1.51 www 5604: return POSIX::mktime(
5605: ($th{'seconds'},$th{'minutes'},$th{'hours'},
1.210 www 5606: $th{'day'},$th{'month'}-1,$th{'year'}-1900,0,0,-1));
1.70 www 5607: }
5608:
5609: #########################################
1.51 www 5610:
5611: sub findallcourses {
1.482 raeburn 5612: my ($roles,$uname,$udom) = @_;
1.355 albertel 5613: my %roles;
5614: if (ref($roles)) { %roles = map { $_ => 1 } @{$roles}; }
1.348 albertel 5615: my %courses;
1.51 www 5616: my $now=time;
1.482 raeburn 5617: if (!defined($uname)) {
5618: $uname = $env{'user.name'};
5619: }
5620: if (!defined($udom)) {
5621: $udom = $env{'user.domain'};
5622: }
5623: if (($uname ne $env{'user.name'}) || ($udom ne $env{'user.domain'})) {
1.1073 raeburn 5624: my %roleshash = &Apache::lonnet::dump('roles',$udom,$uname);
1.482 raeburn 5625: if (!%roles) {
5626: %roles = (
5627: cc => 1,
1.907 raeburn 5628: co => 1,
1.482 raeburn 5629: in => 1,
5630: ep => 1,
5631: ta => 1,
5632: cr => 1,
5633: st => 1,
5634: );
5635: }
5636: foreach my $entry (keys(%roleshash)) {
5637: my ($trole,$tend,$tstart) = split(/_/,$roleshash{$entry});
5638: if ($trole =~ /^cr/) {
5639: next if (!exists($roles{$trole}) && !exists($roles{'cr'}));
5640: } else {
5641: next if (!exists($roles{$trole}));
5642: }
5643: if ($tend) {
5644: next if ($tend < $now);
5645: }
5646: if ($tstart) {
5647: next if ($tstart > $now);
5648: }
1.1058 raeburn 5649: my ($cdom,$cnum,$sec,$cnumpart,$secpart,$role);
1.482 raeburn 5650: (undef,$cdom,$cnumpart,$secpart) = split(/\//,$entry);
1.1058 raeburn 5651: my $value = $trole.'/'.$cdom.'/';
1.482 raeburn 5652: if ($secpart eq '') {
5653: ($cnum,$role) = split(/_/,$cnumpart);
5654: $sec = 'none';
1.1058 raeburn 5655: $value .= $cnum.'/';
1.482 raeburn 5656: } else {
5657: $cnum = $cnumpart;
5658: ($sec,$role) = split(/_/,$secpart);
1.1058 raeburn 5659: $value .= $cnum.'/'.$sec;
5660: }
5661: if (ref($courses{$cdom.'_'.$cnum}{$sec}) eq 'ARRAY') {
5662: unless (grep(/^\Q$value\E$/,@{$courses{$cdom.'_'.$cnum}{$sec}})) {
5663: push(@{$courses{$cdom.'_'.$cnum}{$sec}},$value);
5664: }
5665: } else {
5666: @{$courses{$cdom.'_'.$cnum}{$sec}} = ($value);
1.490 raeburn 5667: }
1.482 raeburn 5668: }
5669: } else {
5670: foreach my $key (keys(%env)) {
1.483 albertel 5671: if ( $key=~m{^user\.role\.(\w+)\./($match_domain)/($match_courseid)/?(\w*)$} ||
5672: $key=~m{^user\.role\.(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_courseid)/?(\w*)$}) {
1.482 raeburn 5673: my ($role,$cdom,$cnum,$sec) = ($1,$2,$3,$4);
5674: next if ($role eq 'ca' || $role eq 'aa');
5675: next if (%roles && !exists($roles{$role}));
5676: my ($starttime,$endtime)=split(/\./,$env{$key});
5677: my $active=1;
5678: if ($starttime) {
5679: if ($now<$starttime) { $active=0; }
5680: }
5681: if ($endtime) {
5682: if ($now>$endtime) { $active=0; }
5683: }
5684: if ($active) {
1.1058 raeburn 5685: my $value = $role.'/'.$cdom.'/'.$cnum.'/';
1.482 raeburn 5686: if ($sec eq '') {
5687: $sec = 'none';
1.1058 raeburn 5688: } else {
5689: $value .= $sec;
5690: }
5691: if (ref($courses{$cdom.'_'.$cnum}{$sec}) eq 'ARRAY') {
5692: unless (grep(/^\Q$value\E$/,@{$courses{$cdom.'_'.$cnum}{$sec}})) {
5693: push(@{$courses{$cdom.'_'.$cnum}{$sec}},$value);
5694: }
5695: } else {
5696: @{$courses{$cdom.'_'.$cnum}{$sec}} = ($value);
1.482 raeburn 5697: }
1.474 raeburn 5698: }
5699: }
1.51 www 5700: }
5701: }
1.474 raeburn 5702: return %courses;
1.51 www 5703: }
1.37 matthew 5704:
1.54 www 5705: ###############################################
1.474 raeburn 5706:
5707: sub blockcheck {
1.1372 raeburn 5708: my ($setters,$activity,$clientip,$uname,$udom,$url,$is_course,$symb,$caller) = @_;
5709: unless (($activity eq 'docs') || ($activity eq 'reinit') || ($activity eq 'alert')) {
5710: my ($has_evb,$check_ipaccess);
5711: my $dom = $env{'user.domain'};
5712: if ($env{'request.course.id'}) {
5713: my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
5714: my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
5715: my $checkrole = "cm./$cdom/$cnum";
5716: my $sec = $env{'request.course.sec'};
5717: if ($sec ne '') {
5718: $checkrole .= "/$sec";
5719: }
5720: if ((&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) &&
5721: ($env{'request.role'} !~ /^st/)) {
5722: $has_evb = 1;
5723: }
5724: unless ($has_evb) {
5725: if (($activity eq 'printout') || ($activity eq 'grades') || ($activity eq 'search') ||
1.1444 raeburn 5726: ($activity eq 'index') || ($activity eq 'boards') || ($activity eq 'groups') ||
5727: ($activity eq 'chat')) {
1.1372 raeburn 5728: if ($udom eq $cdom) {
5729: $check_ipaccess = 1;
5730: }
5731: }
5732: }
1.1375 raeburn 5733: } elsif (($activity eq 'com') || ($activity eq 'port') || ($activity eq 'blogs') ||
5734: ($activity eq 'about') || ($activity eq 'wishlist') || ($activity eq 'passwd')) {
5735: my $checkrole;
5736: if ($env{'request.role.domain'} eq '') {
5737: $checkrole = "cm./$env{'user.domain'}/";
5738: } else {
5739: $checkrole = "cm./$env{'request.role.domain'}/";
5740: }
5741: if (($checkrole) && (&Apache::lonnet::allowed('evb',undef,undef,$checkrole))) {
5742: $has_evb = 1;
5743: }
1.1372 raeburn 5744: }
5745: unless ($has_evb || $check_ipaccess) {
5746: my @machinedoms = &Apache::lonnet::current_machine_domains();
5747: if (($dom eq 'public') && ($activity eq 'port')) {
5748: $dom = $udom;
5749: }
5750: if (($dom ne '') && (grep(/^\Q$dom\E$/,@machinedoms))) {
5751: $check_ipaccess = 1;
5752: } else {
5753: my $lonhost = $Apache::lonnet::perlvar{'lonHostID'};
5754: my $internet_names = &Apache::lonnet::get_internet_names($lonhost);
5755: my $prim = &Apache::lonnet::domain($dom,'primary');
5756: my $intdom = &Apache::lonnet::internet_dom($prim);
5757: if (($intdom ne '') && (ref($internet_names) eq 'ARRAY')) {
5758: if (grep(/^\Q$intdom\E$/,@{$internet_names})) {
5759: $check_ipaccess = 1;
5760: }
5761: }
5762: }
5763: }
5764: if ($check_ipaccess) {
5765: my ($ipaccessref,$cached)=&Apache::lonnet::is_cached_new('ipaccess',$dom);
5766: unless (defined($cached)) {
5767: my %domconfig =
5768: &Apache::lonnet::get_dom('configuration',['ipaccess'],$dom);
5769: $ipaccessref = &Apache::lonnet::do_cache_new('ipaccess',$dom,$domconfig{'ipaccess'},1800);
5770: }
5771: if ((ref($ipaccessref) eq 'HASH') && ($clientip)) {
5772: foreach my $id (keys(%{$ipaccessref})) {
5773: if (ref($ipaccessref->{$id}) eq 'HASH') {
5774: my $range = $ipaccessref->{$id}->{'ip'};
5775: if ($range) {
5776: if (&Apache::lonnet::ip_match($clientip,$range)) {
5777: if (ref($ipaccessref->{$id}->{'commblocks'}) eq 'HASH') {
5778: if ($ipaccessref->{$id}->{'commblocks'}->{$activity} eq 'on') {
5779: return ('','','',$id,$dom);
5780: last;
5781: }
5782: }
5783: }
5784: }
5785: }
5786: }
5787: }
5788: }
1.1373 raeburn 5789: if (($activity eq 'wishlist') || ($activity eq 'annotate')) {
5790: return ();
5791: }
1.1372 raeburn 5792: }
1.1189 raeburn 5793: if (defined($udom) && defined($uname)) {
5794: # If uname and udom are for a course, check for blocks in the course.
5795: if (($is_course) || (&Apache::lonnet::is_course($udom,$uname))) {
5796: my ($startblock,$endblock,$triggerblock) =
1.1347 raeburn 5797: &get_blocks($setters,$activity,$udom,$uname,$url,$symb,$caller);
1.1189 raeburn 5798: return ($startblock,$endblock,$triggerblock);
5799: }
5800: } else {
1.490 raeburn 5801: $udom = $env{'user.domain'};
5802: $uname = $env{'user.name'};
5803: }
5804:
1.502 raeburn 5805: my $startblock = 0;
5806: my $endblock = 0;
1.1062 raeburn 5807: my $triggerblock = '';
1.1373 raeburn 5808: my %live_courses;
5809: unless (($activity eq 'wishlist') || ($activity eq 'annotate')) {
5810: %live_courses = &findallcourses(undef,$uname,$udom);
5811: }
1.474 raeburn 5812:
1.490 raeburn 5813: # If uname is for a user, and activity is course-specific, i.e.,
5814: # boards, chat or groups, check for blocking in current course only.
1.474 raeburn 5815:
1.490 raeburn 5816: if (($activity eq 'boards' || $activity eq 'chat' ||
1.1282 raeburn 5817: $activity eq 'groups' || $activity eq 'printout' ||
1.1444 raeburn 5818: $activity eq 'search' || $activity eq 'index' ||
5819: $activity eq 'reinit' || $activity eq 'alert') &&
1.1189 raeburn 5820: ($env{'request.course.id'})) {
1.490 raeburn 5821: foreach my $key (keys(%live_courses)) {
5822: if ($key ne $env{'request.course.id'}) {
5823: delete($live_courses{$key});
5824: }
5825: }
5826: }
5827:
5828: my $otheruser = 0;
5829: my %own_courses;
5830: if ((($uname ne $env{'user.name'})) || ($udom ne $env{'user.domain'})) {
5831: # Resource belongs to user other than current user.
5832: $otheruser = 1;
5833: # Gather courses for current user
5834: %own_courses =
5835: &findallcourses(undef,$env{'user.name'},$env{'user.domain'});
5836: }
5837:
5838: # Gather active course roles - course coordinator, instructor,
5839: # exam proctor, ta, student, or custom role.
1.474 raeburn 5840:
5841: foreach my $course (keys(%live_courses)) {
1.482 raeburn 5842: my ($cdom,$cnum);
5843: if ((defined($env{'course.'.$course.'.domain'})) && (defined($env{'course.'.$course.'.num'}))) {
5844: $cdom = $env{'course.'.$course.'.domain'};
5845: $cnum = $env{'course.'.$course.'.num'};
5846: } else {
1.490 raeburn 5847: ($cdom,$cnum) = split(/_/,$course);
1.482 raeburn 5848: }
5849: my $no_ownblock = 0;
5850: my $no_userblock = 0;
1.533 raeburn 5851: if ($otheruser && $activity ne 'com') {
1.490 raeburn 5852: # Check if current user has 'evb' priv for this
5853: if (defined($own_courses{$course})) {
5854: foreach my $sec (keys(%{$own_courses{$course}})) {
5855: my $checkrole = 'cm./'.$cdom.'/'.$cnum;
5856: if ($sec ne 'none') {
5857: $checkrole .= '/'.$sec;
5858: }
5859: if (&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) {
5860: $no_ownblock = 1;
5861: last;
5862: }
5863: }
5864: }
5865: # if they have 'evb' priv and are currently not playing student
5866: next if (($no_ownblock) &&
5867: ($env{'request.role'} !~ m{^st\./$cdom/$cnum}));
5868: }
1.474 raeburn 5869: foreach my $sec (keys(%{$live_courses{$course}})) {
1.482 raeburn 5870: my $checkrole = 'cm./'.$cdom.'/'.$cnum;
1.474 raeburn 5871: if ($sec ne 'none') {
1.482 raeburn 5872: $checkrole .= '/'.$sec;
1.474 raeburn 5873: }
1.490 raeburn 5874: if ($otheruser) {
5875: # Resource belongs to user other than current user.
5876: # Assemble privs for that user, and check for 'evb' priv.
1.1058 raeburn 5877: my (%allroles,%userroles);
5878: if (ref($live_courses{$course}{$sec}) eq 'ARRAY') {
5879: foreach my $entry (@{$live_courses{$course}{$sec}}) {
5880: my ($trole,$tdom,$tnum,$tsec);
5881: if ($entry =~ /^cr/) {
5882: ($trole,$tdom,$tnum,$tsec) =
5883: ($entry =~ m|^(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_username)/?(\w*)$|);
5884: } else {
5885: ($trole,$tdom,$tnum,$tsec) = split(/\//,$entry);
5886: }
5887: my ($spec,$area,$trest);
5888: $area = '/'.$tdom.'/'.$tnum;
5889: $trest = $tnum;
5890: if ($tsec ne '') {
5891: $area .= '/'.$tsec;
5892: $trest .= '/'.$tsec;
5893: }
5894: $spec = $trole.'.'.$area;
5895: if ($trole =~ /^cr/) {
5896: &Apache::lonnet::custom_roleprivs(\%allroles,$trole,
5897: $tdom,$spec,$trest,$area);
5898: } else {
5899: &Apache::lonnet::standard_roleprivs(\%allroles,$trole,
5900: $tdom,$spec,$trest,$area);
5901: }
5902: }
1.1276 raeburn 5903: my ($author,$adv,$rar) = &Apache::lonnet::set_userprivs(\%userroles,\%allroles);
1.1058 raeburn 5904: if ($userroles{'user.priv.'.$checkrole} =~ /evb\&([^\:]*)/) {
5905: if ($1) {
5906: $no_userblock = 1;
5907: last;
5908: }
1.486 raeburn 5909: }
5910: }
1.490 raeburn 5911: } else {
5912: # Resource belongs to current user
5913: # Check for 'evb' priv via lonnet::allowed().
1.482 raeburn 5914: if (&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) {
5915: $no_ownblock = 1;
5916: last;
5917: }
1.474 raeburn 5918: }
5919: }
5920: # if they have the evb priv and are currently not playing student
1.482 raeburn 5921: next if (($no_ownblock) &&
1.491 albertel 5922: ($env{'request.role'} !~ m{^st\./\Q$cdom\E/\Q$cnum\E}));
1.482 raeburn 5923: next if ($no_userblock);
1.474 raeburn 5924:
1.1303 raeburn 5925: # Retrieve blocking times and identity of blocker for course
1.490 raeburn 5926: # of specified user, unless user has 'evb' privilege.
1.1284 raeburn 5927:
1.1062 raeburn 5928: my ($start,$end,$trigger) =
1.1347 raeburn 5929: &get_blocks($setters,$activity,$cdom,$cnum,$url,$symb,$caller);
1.502 raeburn 5930: if (($start != 0) &&
5931: (($startblock == 0) || ($startblock > $start))) {
5932: $startblock = $start;
1.1062 raeburn 5933: if ($trigger ne '') {
5934: $triggerblock = $trigger;
5935: }
1.502 raeburn 5936: }
5937: if (($end != 0) &&
5938: (($endblock == 0) || ($endblock < $end))) {
5939: $endblock = $end;
1.1062 raeburn 5940: if ($trigger ne '') {
5941: $triggerblock = $trigger;
5942: }
1.502 raeburn 5943: }
1.490 raeburn 5944: }
1.1062 raeburn 5945: return ($startblock,$endblock,$triggerblock);
1.490 raeburn 5946: }
5947:
5948: sub get_blocks {
1.1347 raeburn 5949: my ($setters,$activity,$cdom,$cnum,$url,$symb,$caller) = @_;
1.490 raeburn 5950: my $startblock = 0;
5951: my $endblock = 0;
1.1062 raeburn 5952: my $triggerblock = '';
1.490 raeburn 5953: my $course = $cdom.'_'.$cnum;
5954: $setters->{$course} = {};
5955: $setters->{$course}{'staff'} = [];
5956: $setters->{$course}{'times'} = [];
1.1062 raeburn 5957: $setters->{$course}{'triggers'} = [];
5958: my (@blockers,%triggered);
5959: my $now = time;
5960: my %commblocks = &Apache::lonnet::get_comm_blocks($cdom,$cnum);
5961: if ($activity eq 'docs') {
1.1348 raeburn 5962: my ($blocked,$nosymbcache,$noenccheck);
1.1347 raeburn 5963: if (($caller eq 'blockedaccess') || ($caller eq 'blockingstatus')) {
5964: $blocked = 1;
5965: $nosymbcache = 1;
1.1348 raeburn 5966: $noenccheck = 1;
1.1347 raeburn 5967: }
1.1348 raeburn 5968: @blockers = &Apache::lonnet::has_comm_blocking('bre',$symb,$url,$nosymbcache,$noenccheck,$blocked,\%commblocks);
1.1062 raeburn 5969: foreach my $block (@blockers) {
5970: if ($block =~ /^firstaccess____(.+)$/) {
5971: my $item = $1;
5972: my $type = 'map';
5973: my $timersymb = $item;
5974: if ($item eq 'course') {
5975: $type = 'course';
5976: } elsif ($item =~ /___\d+___/) {
5977: $type = 'resource';
5978: } else {
5979: $timersymb = &Apache::lonnet::symbread($item);
5980: }
5981: my $start = $env{'course.'.$cdom.'_'.$cnum.'.firstaccess.'.$timersymb};
5982: my $end = $start + $env{'course.'.$cdom.'_'.$cnum.'.timerinterval.'.$timersymb};
5983: $triggered{$block} = {
5984: start => $start,
5985: end => $end,
5986: type => $type,
5987: };
5988: }
5989: }
5990: } else {
5991: foreach my $block (keys(%commblocks)) {
5992: if ($block =~ m/^(\d+)____(\d+)$/) {
5993: my ($start,$end) = ($1,$2);
5994: if ($start <= time && $end >= time) {
5995: if (ref($commblocks{$block}) eq 'HASH') {
5996: if (ref($commblocks{$block}{'blocks'}) eq 'HASH') {
5997: if ($commblocks{$block}{'blocks'}{$activity} eq 'on') {
5998: unless(grep(/^\Q$block\E$/,@blockers)) {
5999: push(@blockers,$block);
6000: }
6001: }
6002: }
6003: }
6004: }
6005: } elsif ($block =~ /^firstaccess____(.+)$/) {
6006: my $item = $1;
6007: my $timersymb = $item;
6008: my $type = 'map';
6009: if ($item eq 'course') {
6010: $type = 'course';
6011: } elsif ($item =~ /___\d+___/) {
6012: $type = 'resource';
6013: } else {
6014: $timersymb = &Apache::lonnet::symbread($item);
6015: }
6016: my $start = $env{'course.'.$cdom.'_'.$cnum.'.firstaccess.'.$timersymb};
6017: my $end = $start + $env{'course.'.$cdom.'_'.$cnum.'.timerinterval.'.$timersymb};
6018: if ($start && $end) {
6019: if (($start <= time) && ($end >= time)) {
1.1281 raeburn 6020: if (ref($commblocks{$block}) eq 'HASH') {
6021: if (ref($commblocks{$block}{'blocks'}) eq 'HASH') {
6022: if ($commblocks{$block}{'blocks'}{$activity} eq 'on') {
6023: unless(grep(/^\Q$block\E$/,@blockers)) {
6024: push(@blockers,$block);
6025: $triggered{$block} = {
6026: start => $start,
6027: end => $end,
6028: type => $type,
6029: };
6030: }
6031: }
6032: }
1.1062 raeburn 6033: }
6034: }
1.490 raeburn 6035: }
1.1062 raeburn 6036: }
6037: }
6038: }
6039: foreach my $blocker (@blockers) {
6040: my ($staff_name,$staff_dom,$title,$blocks) =
6041: &parse_block_record($commblocks{$blocker});
6042: push(@{$$setters{$course}{'staff'}},[$staff_name,$staff_dom]);
6043: my ($start,$end,$triggertype);
6044: if ($blocker =~ m/^(\d+)____(\d+)$/) {
6045: ($start,$end) = ($1,$2);
6046: } elsif (ref($triggered{$blocker}) eq 'HASH') {
6047: $start = $triggered{$blocker}{'start'};
6048: $end = $triggered{$blocker}{'end'};
6049: $triggertype = $triggered{$blocker}{'type'};
6050: }
6051: if ($start) {
6052: push(@{$$setters{$course}{'times'}}, [$start,$end]);
6053: if ($triggertype) {
6054: push(@{$$setters{$course}{'triggers'}},$triggertype);
6055: } else {
6056: push(@{$$setters{$course}{'triggers'}},0);
6057: }
6058: if ( ($startblock == 0) || ($startblock > $start) ) {
6059: $startblock = $start;
6060: if ($triggertype) {
6061: $triggerblock = $blocker;
1.474 raeburn 6062: }
6063: }
1.1062 raeburn 6064: if ( ($endblock == 0) || ($endblock < $end) ) {
6065: $endblock = $end;
6066: if ($triggertype) {
6067: $triggerblock = $blocker;
6068: }
6069: }
1.474 raeburn 6070: }
6071: }
1.1062 raeburn 6072: return ($startblock,$endblock,$triggerblock);
1.474 raeburn 6073: }
6074:
6075: sub parse_block_record {
6076: my ($record) = @_;
6077: my ($setuname,$setudom,$title,$blocks);
6078: if (ref($record) eq 'HASH') {
6079: ($setuname,$setudom) = split(/:/,$record->{'setter'});
6080: $title = &unescape($record->{'event'});
6081: $blocks = $record->{'blocks'};
6082: } else {
6083: my @data = split(/:/,$record,3);
6084: if (scalar(@data) eq 2) {
6085: $title = $data[1];
6086: ($setuname,$setudom) = split(/@/,$data[0]);
6087: } else {
6088: ($setuname,$setudom,$title) = @data;
6089: }
6090: $blocks = { 'com' => 'on' };
6091: }
6092: return ($setuname,$setudom,$title,$blocks);
6093: }
6094:
1.854 kalberla 6095: sub blocking_status {
1.1372 raeburn 6096: my ($activity,$clientip,$uname,$udom,$url,$is_course,$symb,$caller) = @_;
1.1061 raeburn 6097: my %setters;
1.890 droeschl 6098:
1.1061 raeburn 6099: # check for active blocking
1.1372 raeburn 6100: if ($clientip eq '') {
6101: $clientip = &Apache::lonnet::get_requestor_ip();
6102: }
6103: my ($startblock,$endblock,$triggerblock,$by_ip,$blockdom) =
6104: &blockcheck(\%setters,$activity,$clientip,$uname,$udom,$url,$is_course,$symb,$caller);
1.1062 raeburn 6105: my $blocked = 0;
1.1372 raeburn 6106: if (($startblock && $endblock) || ($by_ip)) {
1.1062 raeburn 6107: $blocked = 1;
6108: }
1.890 droeschl 6109:
1.1061 raeburn 6110: # caller just wants to know whether a block is active
6111: if (!wantarray) { return $blocked; }
6112:
6113: # build a link to a popup window containing the details
6114: my $querystring = "?activity=$activity";
1.1351 raeburn 6115: # $uname and $udom decide whose portfolio (or information page) the user is trying to look at
6116: if (($activity eq 'port') || ($activity eq 'about') || ($activity eq 'passwd')) {
1.1232 raeburn 6117: $querystring .= "&udom=$udom" if ($udom =~ /^$match_domain$/);
6118: $querystring .= "&uname=$uname" if ($uname =~ /^$match_username$/);
1.1062 raeburn 6119: } elsif ($activity eq 'docs') {
1.1347 raeburn 6120: my $showurl = &Apache::lonenc::check_encrypt($url);
6121: $querystring .= '&url='.&HTML::Entities::encode($showurl,'\'&"<>');
6122: if ($symb) {
6123: my $showsymb = &Apache::lonenc::check_encrypt($symb);
6124: $querystring .= '&symb='.&HTML::Entities::encode($showsymb,'\'&"<>');
6125: }
1.1062 raeburn 6126: }
1.1061 raeburn 6127:
6128: my $output .= <<'END_MYBLOCK';
6129: function openWindow(url, wdwName, w, h, toolbar,scrollbar) {
6130: var options = "width=" + w + ",height=" + h + ",";
6131: options += "resizable=yes,scrollbars="+scrollbar+",status=no,";
6132: options += "menubar=no,toolbar="+toolbar+",location=no,directories=no";
6133: var newWin = window.open(url, wdwName, options);
6134: newWin.focus();
6135: }
1.890 droeschl 6136: END_MYBLOCK
1.854 kalberla 6137:
1.1061 raeburn 6138: $output = Apache::lonhtmlcommon::scripttag($output);
1.890 droeschl 6139:
1.1061 raeburn 6140: my $popupUrl = "/adm/blockingstatus/$querystring";
1.1062 raeburn 6141: my $text = &mt('Communication Blocked');
1.1217 raeburn 6142: my $class = 'LC_comblock';
1.1062 raeburn 6143: if ($activity eq 'docs') {
6144: $text = &mt('Content Access Blocked');
1.1217 raeburn 6145: $class = '';
1.1063 raeburn 6146: } elsif ($activity eq 'printout') {
6147: $text = &mt('Printing Blocked');
1.1232 raeburn 6148: } elsif ($activity eq 'passwd') {
6149: $text = &mt('Password Changing Blocked');
1.1345 raeburn 6150: } elsif ($activity eq 'grades') {
6151: $text = &mt('Gradebook Blocked');
1.1346 raeburn 6152: } elsif ($activity eq 'search') {
6153: $text = &mt('Search Blocked');
1.1444 raeburn 6154: } elsif ($activity eq 'index') {
6155: $text = &mt('Content Index Blocked');
1.1282 raeburn 6156: } elsif ($activity eq 'alert') {
6157: $text = &mt('Checking Critical Messages Blocked');
6158: } elsif ($activity eq 'reinit') {
6159: $text = &mt('Checking Course Update Blocked');
1.1351 raeburn 6160: } elsif ($activity eq 'about') {
6161: $text = &mt('Access to User Information Pages Blocked');
1.1373 raeburn 6162: } elsif ($activity eq 'wishlist') {
6163: $text = &mt('Access to Stored Links Blocked');
6164: } elsif ($activity eq 'annotate') {
6165: $text = &mt('Access to Annotations Blocked');
1.1062 raeburn 6166: }
1.1061 raeburn 6167: $output .= <<"END_BLOCK";
1.1217 raeburn 6168: <div class='$class'>
1.869 kalberla 6169: <a onclick='openWindow("$popupUrl","Blocking Table",600,300,"no","no");return false;' href='/adm/blockingstatus/$querystring'
1.890 droeschl 6170: title='$text'>
6171: <img class='LC_noBorder LC_middle' title='$text' src='/res/adm/pages/comblock.png' alt='$text'/></a>
1.869 kalberla 6172: <a onclick='openWindow("$popupUrl","Blocking Table",600,300,"no","no");return false;' href='/adm/blockingstatus/$querystring'
1.890 droeschl 6173: title='$text'>$text</a>
1.867 kalberla 6174: </div>
6175:
6176: END_BLOCK
1.474 raeburn 6177:
1.1061 raeburn 6178: return ($blocked, $output);
1.854 kalberla 6179: }
1.490 raeburn 6180:
1.60 matthew 6181: ###############################################
6182:
1.682 raeburn 6183: sub check_ip_acc {
1.1201 raeburn 6184: my ($acc,$clientip)=@_;
1.682 raeburn 6185: &Apache::lonxml::debug("acc is $acc");
6186: if (!defined($acc) || $acc =~ /^\s*$/ || $acc =~/^\s*no\s*$/i) {
6187: return 1;
6188: }
1.1339 raeburn 6189: my ($ip,$allowed);
6190: if (($ENV{'REMOTE_ADDR'} eq '127.0.0.1') ||
6191: ($ENV{'REMOTE_ADDR'} eq &Apache::lonnet::get_host_ip($Apache::lonnet::perlvar{'lonHostID'}))) {
6192: $ip = $env{'request.host'} || $ENV{'REMOTE_ADDR'} || $clientip;
6193: } else {
1.1350 raeburn 6194: my $remote_ip = &Apache::lonnet::get_requestor_ip();
6195: $ip = $remote_ip || $env{'request.host'} || $clientip;
1.1339 raeburn 6196: }
1.682 raeburn 6197:
6198: my $name;
1.1219 raeburn 6199: my %access = (
6200: allowfrom => 1,
6201: denyfrom => 0,
6202: );
6203: my @allows;
6204: my @denies;
6205: foreach my $item (split(',',$acc)) {
6206: $item =~ s/^\s*//;
6207: $item =~ s/\s*$//;
6208: my $pattern;
6209: if ($item =~ /^\!(.+)$/) {
6210: push(@denies,$1);
6211: } else {
6212: push(@allows,$item);
6213: }
6214: }
6215: my $numdenies = scalar(@denies);
6216: my $numallows = scalar(@allows);
6217: my $count = 0;
6218: foreach my $pattern (@denies,@allows) {
6219: $count ++;
6220: my $acctype = 'allowfrom';
6221: if ($count <= $numdenies) {
6222: $acctype = 'denyfrom';
6223: }
1.682 raeburn 6224: if ($pattern =~ /\*$/) {
6225: #35.8.*
6226: $pattern=~s/\*//;
1.1219 raeburn 6227: if ($ip =~ /^\Q$pattern\E/) { $allowed=$access{$acctype}; }
1.682 raeburn 6228: } elsif ($pattern =~ /(\d+\.\d+\.\d+)\.\[(\d+)-(\d+)\]$/) {
6229: #35.8.3.[34-56]
6230: my $low=$2;
6231: my $high=$3;
6232: $pattern=$1;
6233: if ($ip =~ /^\Q$pattern\E/) {
6234: my $last=(split(/\./,$ip))[3];
1.1219 raeburn 6235: if ($last <=$high && $last >=$low) { $allowed=$access{$acctype}; }
1.682 raeburn 6236: }
6237: } elsif ($pattern =~ /^\*/) {
6238: #*.msu.edu
6239: $pattern=~s/\*//;
6240: if (!defined($name)) {
6241: use Socket;
6242: my $netaddr=inet_aton($ip);
6243: ($name)=gethostbyaddr($netaddr,AF_INET);
6244: }
1.1219 raeburn 6245: if ($name =~ /\Q$pattern\E$/i) { $allowed=$access{$acctype}; }
1.682 raeburn 6246: } elsif ($pattern =~ /\d+\.\d+\.\d+\.\d+/) {
6247: #127.0.0.1
1.1219 raeburn 6248: if ($ip =~ /^\Q$pattern\E/) { $allowed=$access{$acctype}; }
1.682 raeburn 6249: } else {
6250: #some.name.com
6251: if (!defined($name)) {
6252: use Socket;
6253: my $netaddr=inet_aton($ip);
6254: ($name)=gethostbyaddr($netaddr,AF_INET);
6255: }
1.1219 raeburn 6256: if ($name =~ /\Q$pattern\E$/i) { $allowed=$access{$acctype}; }
6257: }
6258: if ($allowed =~ /^(0|1)$/) { last; }
6259: }
6260: if ($allowed eq '') {
6261: if ($numdenies && !$numallows) {
6262: $allowed = 1;
6263: } else {
6264: $allowed = 0;
1.682 raeburn 6265: }
6266: }
6267: return $allowed;
6268: }
6269:
6270: ###############################################
6271:
1.60 matthew 6272: =pod
6273:
1.112 bowersj2 6274: =head1 Domain Template Functions
6275:
6276: =over 4
6277:
6278: =item * &determinedomain()
1.60 matthew 6279:
6280: Inputs: $domain (usually will be undef)
6281:
1.63 www 6282: Returns: Determines which domain should be used for designs
1.60 matthew 6283:
6284: =cut
1.54 www 6285:
1.60 matthew 6286: ###############################################
1.63 www 6287: sub determinedomain {
6288: my $domain=shift;
1.531 albertel 6289: if (! $domain) {
1.60 matthew 6290: # Determine domain if we have not been given one
1.893 raeburn 6291: $domain = &Apache::lonnet::default_login_domain();
1.258 albertel 6292: if ($env{'user.domain'}) { $domain=$env{'user.domain'}; }
6293: if ($env{'request.role.domain'}) {
6294: $domain=$env{'request.role.domain'};
1.60 matthew 6295: }
6296: }
1.63 www 6297: return $domain;
6298: }
6299: ###############################################
1.517 raeburn 6300:
1.518 albertel 6301: sub devalidate_domconfig_cache {
6302: my ($udom)=@_;
6303: &Apache::lonnet::devalidate_cache_new('domainconfig',$udom);
6304: }
6305:
6306: # ---------------------- Get domain configuration for a domain
6307: sub get_domainconf {
6308: my ($udom) = @_;
6309: my $cachetime=1800;
6310: my ($result,$cached)=&Apache::lonnet::is_cached_new('domainconfig',$udom);
6311: if (defined($cached)) { return %{$result}; }
6312:
6313: my %domconfig = &Apache::lonnet::get_dom('configuration',
1.948 raeburn 6314: ['login','rolecolors','autoenroll'],$udom);
1.632 raeburn 6315: my (%designhash,%legacy);
1.518 albertel 6316: if (keys(%domconfig) > 0) {
6317: if (ref($domconfig{'login'}) eq 'HASH') {
1.632 raeburn 6318: if (keys(%{$domconfig{'login'}})) {
6319: foreach my $key (keys(%{$domconfig{'login'}})) {
1.699 raeburn 6320: if (ref($domconfig{'login'}{$key}) eq 'HASH') {
1.1208 raeburn 6321: if (($key eq 'loginvia') || ($key eq 'headtag')) {
6322: if (ref($domconfig{'login'}{$key}) eq 'HASH') {
6323: foreach my $hostname (keys(%{$domconfig{'login'}{$key}})) {
6324: if (ref($domconfig{'login'}{$key}{$hostname}) eq 'HASH') {
6325: if ($key eq 'loginvia') {
6326: if ($domconfig{'login'}{'loginvia'}{$hostname}{'server'}) {
6327: my $server = $domconfig{'login'}{'loginvia'}{$hostname}{'server'};
6328: $designhash{$udom.'.login.loginvia'} = $server;
6329: if ($domconfig{'login'}{'loginvia'}{$hostname}{'serverpath'} eq 'custom') {
6330:
6331: $designhash{$udom.'.login.loginvia_'.$hostname} = $server.':'.$domconfig{'login'}{'loginvia'}{$hostname}{'custompath'};
6332: } else {
6333: $designhash{$udom.'.login.loginvia_'.$hostname} = $server.':'.$domconfig{'login'}{'loginvia'}{$hostname}{'serverpath'};
6334: }
1.948 raeburn 6335: }
1.1208 raeburn 6336: } elsif ($key eq 'headtag') {
6337: if ($domconfig{'login'}{'headtag'}{$hostname}{'url'}) {
6338: $designhash{$udom.'.login.headtag_'.$hostname} = $domconfig{'login'}{'headtag'}{$hostname}{'url'};
1.948 raeburn 6339: }
1.946 raeburn 6340: }
1.1208 raeburn 6341: if ($domconfig{'login'}{$key}{$hostname}{'exempt'}) {
6342: $designhash{$udom.'.login.'.$key.'_exempt_'.$hostname} = $domconfig{'login'}{$key}{$hostname}{'exempt'};
6343: }
1.946 raeburn 6344: }
6345: }
6346: }
1.1366 raeburn 6347: } elsif ($key eq 'saml') {
6348: if (ref($domconfig{'login'}{$key}) eq 'HASH') {
6349: foreach my $host (keys(%{$domconfig{'login'}{$key}})) {
6350: if (ref($domconfig{'login'}{$key}{$host}) eq 'HASH') {
6351: $designhash{$udom.'.login.'.$key.'_'.$host} = 1;
1.1386 raeburn 6352: foreach my $item ('text','img','alt','url','title','window','notsso') {
1.1366 raeburn 6353: $designhash{$udom.'.login.'.$key.'_'.$item.'_'.$host} = $domconfig{'login'}{$key}{$host}{$item};
6354: }
6355: }
6356: }
6357: }
1.946 raeburn 6358: } else {
6359: foreach my $img (keys(%{$domconfig{'login'}{$key}})) {
6360: $designhash{$udom.'.login.'.$key.'_'.$img} =
6361: $domconfig{'login'}{$key}{$img};
6362: }
1.699 raeburn 6363: }
6364: } else {
6365: $designhash{$udom.'.login.'.$key}=$domconfig{'login'}{$key};
6366: }
1.632 raeburn 6367: }
6368: } else {
6369: $legacy{'login'} = 1;
1.518 albertel 6370: }
1.632 raeburn 6371: } else {
6372: $legacy{'login'} = 1;
1.518 albertel 6373: }
6374: if (ref($domconfig{'rolecolors'}) eq 'HASH') {
1.632 raeburn 6375: if (keys(%{$domconfig{'rolecolors'}})) {
6376: foreach my $role (keys(%{$domconfig{'rolecolors'}})) {
6377: if (ref($domconfig{'rolecolors'}{$role}) eq 'HASH') {
6378: foreach my $item (keys(%{$domconfig{'rolecolors'}{$role}})) {
6379: $designhash{$udom.'.'.$role.'.'.$item}=$domconfig{'rolecolors'}{$role}{$item};
6380: }
1.518 albertel 6381: }
6382: }
1.632 raeburn 6383: } else {
6384: $legacy{'rolecolors'} = 1;
1.518 albertel 6385: }
1.632 raeburn 6386: } else {
6387: $legacy{'rolecolors'} = 1;
1.518 albertel 6388: }
1.948 raeburn 6389: if (ref($domconfig{'autoenroll'}) eq 'HASH') {
6390: if ($domconfig{'autoenroll'}{'co-owners'}) {
6391: $designhash{$udom.'.autoassign.co-owners'}=$domconfig{'autoenroll'}{'co-owners'};
6392: }
6393: }
1.632 raeburn 6394: if (keys(%legacy) > 0) {
6395: my %legacyhash = &get_legacy_domconf($udom);
6396: foreach my $item (keys(%legacyhash)) {
6397: if ($item =~ /^\Q$udom\E\.login/) {
6398: if ($legacy{'login'}) {
6399: $designhash{$item} = $legacyhash{$item};
6400: }
6401: } else {
6402: if ($legacy{'rolecolors'}) {
6403: $designhash{$item} = $legacyhash{$item};
6404: }
1.518 albertel 6405: }
6406: }
6407: }
1.632 raeburn 6408: } else {
6409: %designhash = &get_legacy_domconf($udom);
1.518 albertel 6410: }
6411: &Apache::lonnet::do_cache_new('domainconfig',$udom,\%designhash,
6412: $cachetime);
6413: return %designhash;
6414: }
6415:
1.632 raeburn 6416: sub get_legacy_domconf {
6417: my ($udom) = @_;
6418: my %legacyhash;
6419: my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors';
6420: my $designfile = $designdir.'/'.$udom.'.tab';
6421: if (-e $designfile) {
1.1317 raeburn 6422: if ( open (my $fh,'<',$designfile) ) {
1.632 raeburn 6423: while (my $line = <$fh>) {
6424: next if ($line =~ /^\#/);
6425: chomp($line);
6426: my ($key,$val)=(split(/\=/,$line));
6427: if ($val) { $legacyhash{$udom.'.'.$key}=$val; }
6428: }
6429: close($fh);
6430: }
6431: }
1.1026 raeburn 6432: if (-e $Apache::lonnet::perlvar{'lonDocRoot'}.'/adm/lonDomLogos/'.$udom.'.gif') {
1.632 raeburn 6433: $legacyhash{$udom.'.login.domlogo'} = "/adm/lonDomLogos/$udom.gif";
6434: }
6435: return %legacyhash;
6436: }
6437:
1.63 www 6438: =pod
6439:
1.112 bowersj2 6440: =item * &domainlogo()
1.63 www 6441:
6442: Inputs: $domain (usually will be undef)
6443:
6444: Returns: A link to a domain logo, if the domain logo exists.
6445: If the domain logo does not exist, a description of the domain.
6446:
6447: =cut
1.112 bowersj2 6448:
1.63 www 6449: ###############################################
6450: sub domainlogo {
1.517 raeburn 6451: my $domain = &determinedomain(shift);
1.518 albertel 6452: my %designhash = &get_domainconf($domain);
1.517 raeburn 6453: # See if there is a logo
6454: if ($designhash{$domain.'.login.domlogo'} ne '') {
1.519 raeburn 6455: my $imgsrc = $designhash{$domain.'.login.domlogo'};
1.538 albertel 6456: if ($imgsrc =~ m{^/(adm|res)/}) {
6457: if ($imgsrc =~ m{^/res/}) {
6458: my $local_name = &Apache::lonnet::filelocation('',$imgsrc);
6459: &Apache::lonnet::repcopy($local_name);
6460: }
6461: $imgsrc = &lonhttpdurl($imgsrc);
1.1374 raeburn 6462: }
6463: my $alttext = $domain;
6464: if ($designhash{$domain.'.login.alttext_domlogo'} ne '') {
6465: $alttext = $designhash{$domain.'.login.alttext_domlogo'};
6466: }
6467: return '<img src="'.$imgsrc.'" alt="'.$alttext.'" id="lclogindomlogo" />';
1.514 albertel 6468: } elsif (defined(&Apache::lonnet::domain($domain,'description'))) {
6469: return &Apache::lonnet::domain($domain,'description');
1.59 www 6470: } else {
1.60 matthew 6471: return '';
1.59 www 6472: }
6473: }
1.63 www 6474: ##############################################
6475:
6476: =pod
6477:
1.112 bowersj2 6478: =item * &designparm()
1.63 www 6479:
6480: Inputs: $which parameter; $domain (usually will be undef)
6481:
6482: Returns: value of designparamter $which
6483:
6484: =cut
1.112 bowersj2 6485:
1.397 albertel 6486:
1.400 albertel 6487: ##############################################
1.397 albertel 6488: sub designparm {
6489: my ($which,$domain)=@_;
6490: if (exists($env{'environment.color.'.$which})) {
1.817 bisitz 6491: return $env{'environment.color.'.$which};
1.96 www 6492: }
1.63 www 6493: $domain=&determinedomain($domain);
1.1016 raeburn 6494: my %domdesign;
6495: unless ($domain eq 'public') {
6496: %domdesign = &get_domainconf($domain);
6497: }
1.520 raeburn 6498: my $output;
1.517 raeburn 6499: if ($domdesign{$domain.'.'.$which} ne '') {
1.817 bisitz 6500: $output = $domdesign{$domain.'.'.$which};
1.63 www 6501: } else {
1.520 raeburn 6502: $output = $defaultdesign{$which};
6503: }
6504: if (($which =~ /^(student|coordinator|author|admin)\.img$/) ||
1.635 raeburn 6505: ($which =~ /login\.(img|logo|domlogo|login)/)) {
1.538 albertel 6506: if ($output =~ m{^/(adm|res)/}) {
1.817 bisitz 6507: if ($output =~ m{^/res/}) {
6508: my $local_name = &Apache::lonnet::filelocation('',$output);
6509: &Apache::lonnet::repcopy($local_name);
6510: }
1.520 raeburn 6511: $output = &lonhttpdurl($output);
6512: }
1.63 www 6513: }
1.520 raeburn 6514: return $output;
1.63 www 6515: }
1.59 www 6516:
1.822 bisitz 6517: ##############################################
6518: =pod
6519:
1.832 bisitz 6520: =item * &authorspace()
6521:
1.1028 raeburn 6522: Inputs: $url (usually will be undef).
1.832 bisitz 6523:
1.1132 raeburn 6524: Returns: Path to Authoring Space containing the resource or
1.1028 raeburn 6525: directory being viewed (or for which action is being taken).
6526: If $url is provided, and begins /priv/<domain>/<uname>
6527: the path will be that portion of the $context argument.
6528: Otherwise the path will be for the author space of the current
6529: user when the current role is author, or for that of the
6530: co-author/assistant co-author space when the current role
6531: is co-author or assistant co-author.
1.832 bisitz 6532:
6533: =cut
6534:
6535: sub authorspace {
1.1028 raeburn 6536: my ($url) = @_;
6537: if ($url ne '') {
6538: if ($url =~ m{^(/priv/$match_domain/$match_username/)}) {
6539: return $1;
6540: }
6541: }
1.832 bisitz 6542: my $caname = '';
1.1024 www 6543: my $cadom = '';
1.1028 raeburn 6544: if ($env{'request.role'} =~ /^(?:ca|aa)/) {
1.1024 www 6545: ($cadom,$caname) =
1.832 bisitz 6546: ($env{'request.role'}=~/($match_domain)\/($match_username)$/);
1.1028 raeburn 6547: } elsif ($env{'request.role'} =~ m{^au\./($match_domain)/}) {
1.832 bisitz 6548: $caname = $env{'user.name'};
1.1024 www 6549: $cadom = $env{'user.domain'};
1.832 bisitz 6550: }
1.1028 raeburn 6551: if (($caname ne '') && ($cadom ne '')) {
6552: return "/priv/$cadom/$caname/";
6553: }
6554: return;
1.832 bisitz 6555: }
6556:
6557: ##############################################
6558: =pod
6559:
1.822 bisitz 6560: =item * &head_subbox()
6561:
6562: Inputs: $content (contains HTML code with page functions, etc.)
6563:
6564: Returns: HTML div with $content
6565: To be included in page header
6566:
6567: =cut
6568:
6569: sub head_subbox {
6570: my ($content)=@_;
6571: my $output =
1.993 raeburn 6572: '<div class="LC_head_subbox">'
1.822 bisitz 6573: .$content
6574: .'</div>'
6575: }
6576:
6577: ##############################################
6578: =pod
6579:
6580: =item * &CSTR_pageheader()
6581:
1.1026 raeburn 6582: Input: (optional) filename from which breadcrumb trail is built.
6583: In most cases no input as needed, as $env{'request.filename'}
6584: is appropriate for use in building the breadcrumb trail.
1.1379 raeburn 6585: frameset flag
6586: If page header is being requested for use in a frameset, then
6587: the second (option) argument -- frameset will be true, and
6588: the target attribute set for links should be target="_parent".
1.1433 raeburn 6589: If $title is supplied as the third arg, that will be used to
1.1407 raeburn 6590: the left of the breadcrumbs tail for the current path.
1.822 bisitz 6591:
6592: Returns: HTML div with CSTR path and recent box
1.1132 raeburn 6593: To be included on Authoring Space pages
1.822 bisitz 6594:
6595: =cut
6596:
6597: sub CSTR_pageheader {
1.1407 raeburn 6598: my ($trailfile,$frameset,$title) = @_;
1.1026 raeburn 6599: if ($trailfile eq '') {
6600: $trailfile = $env{'request.filename'};
6601: }
6602:
6603: # this is for resources; directories have customtitle, and crumbs
6604: # and select recent are created in lonpubdir.pm
6605:
6606: my $londocroot = $Apache::lonnet::perlvar{'lonDocRoot'};
1.1022 www 6607: my ($udom,$uname,$thisdisfn)=
1.1113 raeburn 6608: ($trailfile =~ m{^\Q$londocroot\E/priv/([^/]+)/([^/]+)(?:|/(.*))$});
1.1026 raeburn 6609: my $formaction = "/priv/$udom/$uname/$thisdisfn";
6610: $formaction =~ s{/+}{/}g;
1.822 bisitz 6611:
6612: my $parentpath = '';
6613: my $lastitem = '';
6614: if ($thisdisfn =~ m-(.+/)([^/]*)$-) {
6615: $parentpath = $1;
6616: $lastitem = $2;
6617: } else {
6618: $lastitem = $thisdisfn;
6619: }
1.921 bisitz 6620:
1.1406 raeburn 6621: my $crsauthor;
1.1246 raeburn 6622: if (($env{'request.course.id'}) &&
6623: ($env{'course.'.$env{'request.course.id'}.'.num'} eq $uname) &&
1.1247 raeburn 6624: ($env{'course.'.$env{'request.course.id'}.'.domain'} eq $udom)) {
1.1246 raeburn 6625: $crsauthor = 1;
1.1406 raeburn 6626: if ($title eq '') {
6627: $title = &mt('Course Authoring Space');
6628: }
6629: } elsif ($title eq '') {
1.1246 raeburn 6630: $title = &mt('Authoring Space');
6631: }
6632:
1.1379 raeburn 6633: my ($target,$crumbtarget) = (' target="_top"','_top');
6634: if ($frameset) {
6635: $target = ' target="_parent"';
6636: $crumbtarget = '_parent';
6637: } elsif (($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) {
1.1314 raeburn 6638: $target = '';
6639: $crumbtarget = '';
1.1379 raeburn 6640: } elsif (($env{'request.deeplink.login'}) && ($env{'request.deeplink.target'})) {
1.1378 raeburn 6641: $target = ' target="'.$env{'request.deeplink.target'}.'"';
6642: $crumbtarget = $env{'request.deeplink.target'};
6643: }
1.1313 raeburn 6644:
1.921 bisitz 6645: my $output =
1.1407 raeburn 6646: '<div>'
1.822 bisitz 6647: .&Apache::loncommon::help_open_menu('','',3,'Authoring') #FIXME: Broken? Where is it?
1.1246 raeburn 6648: .'<b>'.$title.'</b> '
1.1314 raeburn 6649: .'<form name="dirs" method="post" action="'.$formaction.'"'.$target.'>'
6650: .&Apache::lonhtmlcommon::crumbs($uname.'/'.$parentpath,$crumbtarget,'/priv/'.$udom,undef,undef);
1.921 bisitz 6651:
6652: if ($lastitem) {
6653: $output .=
6654: '<span class="LC_filename">'
6655: .$lastitem
6656: .'</span>';
6657: }
1.1245 raeburn 6658:
1.1246 raeburn 6659: if ($crsauthor) {
1.1379 raeburn 6660: $output .= '</form>'.&Apache::lonmenu::constspaceform($frameset);
1.1246 raeburn 6661: } else {
6662: $output .=
6663: '<br />'
1.1314 raeburn 6664: #FIXME lonpubdir: &Apache::lonhtmlcommon::crumbs($uname.$thisdisfn.'/',$crumbtarget,'/priv','','+1',1)."</b></tt><br />"
1.1246 raeburn 6665: .&Apache::lonhtmlcommon::select_recent('construct','recent','this.form.action=this.form.recent.value;this.form.submit()')
6666: .'</form>'
1.1379 raeburn 6667: .&Apache::lonmenu::constspaceform($frameset);
1.1246 raeburn 6668: }
1.1407 raeburn 6669: $output .= '</div>';
1.921 bisitz 6670:
6671: return $output;
1.822 bisitz 6672: }
6673:
1.1419 raeburn 6674: ##############################################
6675: =pod
6676:
6677: =item * &nocodemirror()
6678:
6679: Input: None
6680:
6681: Returns: 1 if CodeMirror is deactivated based on
6682: user's preference, or domain default,
6683: if user indicated use of default.
6684:
6685: =cut
6686:
1.1416 raeburn 6687: sub nocodemirror {
6688: my $nocodem = $env{'environment.nocodemirror'};
6689: unless ($nocodem) {
6690: my %domdefs = &Apache::lonnet::get_domain_defaults($env{'user.domain'});
6691: if ($domdefs{'nocodemirror'}) {
6692: $nocodem = 'yes';
6693: }
6694: }
1.1417 raeburn 6695: if ($nocodem eq 'yes') {
6696: return 1;
6697: }
6698: return;
1.1416 raeburn 6699: }
6700:
1.1419 raeburn 6701: ##############################################
6702: =pod
6703:
6704: =item * &permitted_editors()
6705:
1.1422 raeburn 6706: Input: $uri (optional)
1.1419 raeburn 6707:
6708: Returns: %editors hash in which keys are editors
1.1429 raeburn 6709: permitted in current Authoring Space,
6710: or in current course for web pages
6711: created in a course.
6712:
1.1419 raeburn 6713: Value for each key is 1. Possible keys
1.1429 raeburn 6714: are: edit, xml, and daxe.
6715:
6716: For a regular Authoring Space, if no specific
1.1419 raeburn 6717: set of editors has been set for the Author
6718: who owns the Authoring Space, then the
6719: domain default will be used. If no domain
6720: default has been set, then the keys will be
6721: edit and xml.
6722:
1.1429 raeburn 6723: For a course author, or for web pages created
6724: in a course, if no specific set of editors has
6725: been set for the course, then the domain
6726: course default will be used. If no domain
6727: course default has been set, then the keys
6728: will be edit and xml.
6729:
1.1419 raeburn 6730: =cut
6731:
1.1418 raeburn 6732: sub permitted_editors {
1.1422 raeburn 6733: my ($uri) = @_;
1.1429 raeburn 6734: my ($is_author,$is_coauthor,$is_course,$auname,$audom,%editors);
1.1418 raeburn 6735: if ($env{'request.role'} =~ m{^au\./}) {
6736: $is_author = 1;
6737: } elsif ($env{'request.role'} =~ m{^(?:ca|aa)\./($match_domain)/($match_username)}) {
6738: ($audom,$auname) = ($1,$2);
6739: if (($audom ne '') && ($auname ne '')) {
6740: if (($env{'user.domain'} eq $audom) &&
6741: ($env{'user.name'} eq $auname)) {
6742: $is_author = 1;
6743: } else {
6744: $is_coauthor = 1;
6745: }
6746: }
6747: } elsif ($env{'request.course.id'}) {
1.1429 raeburn 6748: my ($cdom,$cnum);
6749: $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
6750: $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
6751: if (($env{'request.editurl'} =~ m{^/priv/\Q$cdom/$cnum\E/}) ||
1.1430 raeburn 6752: ($env{'request.editurl'} =~ m{^/uploaded/\Q$cdom/$cnum\E/(docs|supplemental)/}) ||
6753: ($uri =~ m{^/uploaded/\Q$cdom/$cnum\E/(docs|supplemental)/})) {
1.1429 raeburn 6754: $is_course = 1;
6755: } elsif ($env{'request.editurl'} =~ m{^/priv/($match_domain)/($match_username)/}) {
1.1418 raeburn 6756: ($audom,$auname) = ($1,$2);
6757: } elsif ($env{'request.uri'} =~ m{^/priv/($match_domain)/($match_username)/}) {
6758: ($audom,$auname) = ($1,$2);
1.1422 raeburn 6759: } elsif (($uri eq '/daxesave') &&
1.1429 raeburn 6760: (($env{'form.path'} =~ m{^/daxeopen/priv/\Q$cdom/$cnum\E/}) ||
6761: ($env{'form.path'} =~ m{^/daxeopen/uploaded/\Q$cdom/$cnum\E/(docs|supplemental)/}))) {
6762: $is_course = 1;
6763: } elsif (($uri eq '/daxesave') &&
1.1422 raeburn 6764: ($env{'form.path'} =~ m{^/daxeopen/priv/($match_domain)/($match_username)/})) {
6765: ($audom,$auname) = ($1,$2);
1.1418 raeburn 6766: }
1.1429 raeburn 6767: unless ($is_course) {
6768: if (($audom ne '') && ($auname ne '')) {
6769: if (($env{'user.domain'} eq $audom) &&
6770: ($env{'user.name'} eq $auname)) {
6771: $is_author = 1;
6772: } else {
6773: $is_coauthor = 1;
6774: }
1.1418 raeburn 6775: }
6776: }
6777: }
6778: if ($is_author) {
6779: if (exists($env{'environment.editors'})) {
6780: map { $editors{$_} = 1; } split(/,/,$env{'environment.editors'});
6781: } else {
6782: %editors = ( edit => 1,
6783: xml => 1,
6784: );
6785: }
6786: } elsif ($is_coauthor) {
6787: if (exists($env{"environment.internal.editors./$audom/$auname"})) {
6788: map { $editors{$_} = 1; } split(/,/,$env{"environment.internal.editors./$audom/$auname"});
6789: } else {
6790: %editors = ( edit => 1,
6791: xml => 1,
6792: );
6793: }
1.1429 raeburn 6794: } elsif ($is_course) {
6795: if (exists($env{'course.'.$env{'request.course.id'}.'.internal.crseditors'})) {
6796: map { $editors{$_} = 1; } split(/,/,$env{'course.'.$env{'request.course.id'}.'.internal.crseditors'});
6797: } else {
6798: my %domdefaults = &Apache::lonnet::get_domain_defaults($env{'course.'.$env{'request.course.id'}.'.domain'});
6799: if (exists($domdefaults{'crseditors'})) {
6800: map { $editors{$_} = 1; } split(/,/,$domdefaults{'crseditors'});
6801: } else {
6802: %editors = ( edit => 1,
6803: xml => 1,
6804: );
6805: }
6806: }
1.1418 raeburn 6807: } else {
6808: %editors = ( edit => 1,
6809: xml => 1,
6810: );
6811: }
6812: return %editors;
6813: }
6814:
1.60 matthew 6815: ###############################################
6816: ###############################################
6817:
6818: =pod
6819:
1.112 bowersj2 6820: =back
6821:
1.549 albertel 6822: =head1 HTML Helpers
1.112 bowersj2 6823:
6824: =over 4
6825:
6826: =item * &bodytag()
1.60 matthew 6827:
6828: Returns a uniform header for LON-CAPA web pages.
6829:
6830: Inputs:
6831:
1.112 bowersj2 6832: =over 4
6833:
6834: =item * $title, A title to be displayed on the page.
6835:
6836: =item * $function, the current role (can be undef).
6837:
6838: =item * $addentries, extra parameters for the <body> tag.
6839:
6840: =item * $bodyonly, if defined, only return the <body> tag.
6841:
6842: =item * $domain, if defined, force a given domain.
6843:
6844: =item * $forcereg, if page should register as content page (relevant for
1.86 www 6845: text interface only)
1.60 matthew 6846:
1.814 bisitz 6847: =item * $no_nav_bar, if true, keep the 'what is this' info but remove the
6848: navigational links
1.317 albertel 6849:
1.338 albertel 6850: =item * $bgcolor, used to override the bgcolor on a webpage to a specific value
6851:
1.460 albertel 6852: =item * $args, optional argument valid values are
6853: no_auto_mt_title -> prevents &mt()ing the title arg
1.1274 raeburn 6854: use_absolute -> for external resource or syllabus, this will
6855: contain https://<hostname> if server uses
6856: https (as per hosts.tab), but request is for http
6857: hostname -> hostname, from $r->hostname().
1.460 albertel 6858:
1.1096 raeburn 6859: =item * $advtoolsref, optional argument, ref to an array containing
6860: inlineremote items to be added in "Functions" menu below
6861: breadcrumbs.
6862:
1.1316 raeburn 6863: =item * $ltiscope, optional argument, will be one of: resource, map or
6864: course, if LON-CAPA is in LTI Provider context. Value is
6865: the scope of use, i.e., launch was for access to a single, a map
6866: or the entire course.
6867:
6868: =item * $ltiuri, optional argument, if LON-CAPA is in LTI Provider
6869: context, this will contain the URL for the landing item in
6870: the course, after launch from an LTI Consumer
6871:
1.1318 raeburn 6872: =item * $ltimenu, optional argument, if LON-CAPA is in LTI Provider
6873: context, this will contain a reference to hash of items
6874: to be included in the page header and/or inline menu.
6875:
1.1385 raeburn 6876: =item * $menucoll, optional argument, if specific menu collection is in
6877: effect, either set as the default for the course, or set for
6878: the deeplink paramater for $env{'request.deeplink.login'}
6879: then $menucoll will be the number of that collection.
6880:
6881: =item * $menuref, optional argument, reference to a hash, containing the
6882: menu options included for the menu in effect, based on the
6883: configuration for the numbered menu collection in use.
6884:
6885: =item * $showncrumbsref, reference to a scalar. Calls to lonmenu::innerregister
6886: within &bodytag() can result in calls to lonhtmlcommon::breadcrumbs(),
6887: if so, $showncrumbsref is set there to 1, and will propagate back
6888: via &bodytag() to &start_page(), to prevent lonhtmlcommon::breadcrumbs()
6889: being called a second time.
6890:
1.112 bowersj2 6891: =back
6892:
1.60 matthew 6893: Returns: A uniform header for LON-CAPA web pages.
6894: If $bodyonly is nonzero, a string containing a <body> tag will be returned.
6895: If $bodyonly is undef or zero, an html string containing a <body> tag and
6896: other decorations will be returned.
6897:
6898: =cut
6899:
1.54 www 6900: sub bodytag {
1.831 bisitz 6901: my ($title,$function,$addentries,$bodyonly,$domain,$forcereg,
1.1359 raeburn 6902: $no_nav_bar,$bgcolor,$args,$advtoolsref,$ltiscope,$ltiuri,
1.1385 raeburn 6903: $ltimenu,$menucoll,$menuref,$showncrumbsref)=@_;
1.339 albertel 6904:
1.954 raeburn 6905: my $public;
6906: if ((($env{'user.name'} eq 'public') && ($env{'user.domain'} eq 'public'))
6907: || ($env{'user.name'} eq '') && ($env{'user.domain'} eq '')) {
6908: $public = 1;
6909: }
1.460 albertel 6910: if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
1.1154 raeburn 6911: my $httphost = $args->{'use_absolute'};
1.1274 raeburn 6912: my $hostname = $args->{'hostname'};
1.339 albertel 6913:
1.183 matthew 6914: $function = &get_users_function() if (!$function);
1.339 albertel 6915: my $font = &designparm($function.'.font',$domain);
6916: my $pgbg = $bgcolor || &designparm($function.'.pgbg',$domain);
6917:
1.803 bisitz 6918: my %design = ( 'style' => 'margin-top: 0',
1.535 albertel 6919: 'bgcolor' => $pgbg,
1.339 albertel 6920: 'text' => $font,
6921: 'alink' => &designparm($function.'.alink',$domain),
6922: 'vlink' => &designparm($function.'.vlink',$domain),
6923: 'link' => &designparm($function.'.link',$domain),);
1.438 albertel 6924: @design{keys(%$addentries)} = @$addentries{keys(%$addentries)};
1.339 albertel 6925:
1.63 www 6926: # role and realm
1.1178 raeburn 6927: my ($role,$realm) = split(m{\./},$env{'request.role'},2);
6928: if ($realm) {
6929: $realm = '/'.$realm;
6930: }
1.1357 raeburn 6931: if ($role eq 'ca') {
1.479 albertel 6932: my ($rdom,$rname) = ($realm =~ m{^/($match_domain)/($match_username)$});
1.500 albertel 6933: $realm = &plainname($rname,$rdom);
1.378 raeburn 6934: }
1.55 www 6935: # realm
1.1357 raeburn 6936: my ($cid,$sec);
1.258 albertel 6937: if ($env{'request.course.id'}) {
1.1357 raeburn 6938: $cid = $env{'request.course.id'};
6939: if ($env{'request.course.sec'}) {
6940: $sec = $env{'request.course.sec'};
6941: }
6942: } elsif ($realm =~ m{^/($match_domain)/($match_courseid)(?:|/(\w+))$}) {
6943: if (&Apache::lonnet::is_course($1,$2)) {
6944: $cid = $1.'_'.$2;
6945: $sec = $3;
6946: }
6947: }
6948: if ($cid) {
1.378 raeburn 6949: if ($env{'request.role'} !~ /^cr/) {
6950: $role = &Apache::lonnet::plaintext($role,&course_type());
1.1257 raeburn 6951: } elsif ($role =~ m{^cr/($match_domain)/\1-domainconfig/(\w+)$}) {
1.1269 raeburn 6952: if ($env{'request.role.desc'}) {
6953: $role = $env{'request.role.desc'};
6954: } else {
6955: $role = &mt('Helpdesk[_1]',' '.$2);
6956: }
1.1257 raeburn 6957: } else {
6958: $role = (split(/\//,$role,4))[-1];
1.378 raeburn 6959: }
1.1357 raeburn 6960: if ($sec) {
6961: $role .= (' 'x2).'- '.&mt('section:').' '.$sec;
1.898 raeburn 6962: }
1.1357 raeburn 6963: $realm = $env{'course.'.$cid.'.description'};
1.378 raeburn 6964: } else {
6965: $role = &Apache::lonnet::plaintext($role);
1.54 www 6966: }
1.433 albertel 6967:
1.438 albertel 6968: my $extra_body_attr = &make_attr_string($forcereg,\%design);
1.329 albertel 6969:
1.101 www 6970: # construct main body tag
1.359 albertel 6971: my $bodytag = "<body $extra_body_attr>".
1.1235 raeburn 6972: &Apache::lontexconvert::init_math_support();
1.252 albertel 6973:
1.1131 raeburn 6974: &get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['inhibitmenu']);
6975:
1.1130 raeburn 6976: if (($bodyonly) || ($no_nav_bar) || ($env{'form.inhibitmenu'} eq 'yes')) {
1.60 matthew 6977: return $bodytag;
1.1130 raeburn 6978: }
1.359 albertel 6979:
1.954 raeburn 6980: if ($public) {
1.433 albertel 6981: undef($role);
6982: }
1.1318 raeburn 6983:
1.1359 raeburn 6984: my $showcrstitle = 1;
1.1357 raeburn 6985: if (($cid) && ($env{'request.lti.login'})) {
1.1318 raeburn 6986: if (ref($ltimenu) eq 'HASH') {
6987: unless ($ltimenu->{'role'}) {
6988: undef($role);
6989: }
6990: unless ($ltimenu->{'coursetitle'}) {
1.1359 raeburn 6991: $showcrstitle = 0;
6992: }
6993: }
6994: } elsif (($cid) && ($menucoll)) {
6995: if (ref($menuref) eq 'HASH') {
6996: unless ($menuref->{'role'}) {
6997: undef($role);
6998: }
6999: unless ($menuref->{'crs'}) {
7000: $showcrstitle = 0;
1.1318 raeburn 7001: }
7002: }
7003: }
7004:
1.762 bisitz 7005: my $titleinfo = '<h1>'.$title.'</h1>';
1.359 albertel 7006: #
7007: # Extra info if you are the DC
7008: my $dc_info = '';
1.1359 raeburn 7009: if (($env{'user.adv'}) && ($env{'request.course.id'}) && $showcrstitle &&
1.1357 raeburn 7010: (exists($env{'user.role.dc./'.$env{'course.'.$cid.'.domain'}.'/'}))) {
1.917 raeburn 7011: $dc_info = $cid.' '.$env{'course.'.$cid.'.internal.coursecode'};
1.380 www 7012: $dc_info =~ s/\s+$//;
1.359 albertel 7013: }
7014:
1.1237 raeburn 7015: my $crstype;
1.1357 raeburn 7016: if ($cid) {
7017: $crstype = $env{'course.'.$cid.'.type'};
1.1237 raeburn 7018: } elsif ($args->{'crstype'}) {
7019: $crstype = $args->{'crstype'};
7020: }
7021: if (($crstype eq 'Placement') && (!$env{'request.role.adv'})) {
7022: undef($role);
7023: } else {
1.1242 raeburn 7024: $role = '<span class="LC_nobreak">('.$role.')</span>' if ($role && !$env{'browser.mobile'});
1.1237 raeburn 7025: }
1.853 droeschl 7026:
1.903 droeschl 7027: if ($env{'request.state'} eq 'construct') { $forcereg=1; }
7028:
7029: # if ($env{'request.state'} eq 'construct') {
7030: # $titleinfo = &CSTR_pageheader(); #FIXME: Will be removed once all scripts have their own calls
7031: # }
7032:
1.1440 raeburn 7033: my $need_endlcint;
7034: unless ($args->{'switchserver'}) {
7035: $bodytag .= Apache::lonhtmlcommon::scripttag(
7036: Apache::lonmenu::utilityfunctions($httphost), 'start');
7037: $need_endlcint = 1;
7038: }
1.359 albertel 7039:
1.1427 raeburn 7040: my $collapsible;
1.1423 raeburn 7041: if ($args->{'collapsible_header'} ne '') {
1.1427 raeburn 7042: $collapsible = 1;
7043: my ($menustate,$tiptext,$divclass);
7044: if ($args->{'start_collapsed'}) {
7045: $menustate = 'collapsed';
7046: $tiptext = 'display';
7047: $divclass = 'hidden';
7048: } else {
7049: $menustate = 'expanded';
7050: $tiptext = 'hide';
7051: $divclass = 'shown';
7052: }
7053: my $alttext = &mt('menu state: '.$menustate);
7054: my $tooltip = &mt($tiptext.' standard menus');
1.1421 raeburn 7055: $bodytag .= <<"END";
1.1461 raeburn 7056: <div id="LC_expandingContainer" style="display:inline;" role="navigation">
1.1421 raeburn 7057: <div id="LC_collapsible" class="LC_collapse_trigger" style="position: absolute;top: -5px;left: 0px; z-index:101; display:inline;">
1.1427 raeburn 7058: <a href="#" style="text-decoration:none;"><img class="LC_collapsible_indicator" alt="$alttext" title="$tooltip" src="/res/adm/pages/$menustate.png" style="border:0;margin:0;padding:0;max-width:100%;height:auto" /></a></div>
7059: <div class="LC_menus_content $divclass">
1.1421 raeburn 7060: END
7061: }
1.1318 raeburn 7062: unless ($args->{'no_primary_menu'}) {
1.1369 raeburn 7063: my ($left,$right) = Apache::lonmenu::primary_menu($crstype,$ltimenu,$menucoll,$menuref,
1.1380 raeburn 7064: $args->{'links_disabled'},
1.1421 raeburn 7065: $args->{'links_target'},
1.1427 raeburn 7066: $collapsible);
1.1454 raeburn 7067: my $labeltext = &HTML::Entities::encode(&mt('Primary links'));
1.1318 raeburn 7068: if ($env{'request.noversionuri'} =~ m{^/res/adm/pages/}) {
7069: if ($dc_info) {
7070: $dc_info = qq|<span class="LC_cusr_subheading">$dc_info</span>|;
7071: }
1.1456 raeburn 7072: $bodytag .= qq|<div id="LC_nav_bar" role="navigation" aria-label="$labeltext">$left $role</div>|;
1.1454 raeburn 7073: unless (($realm eq '') && ($dc_info eq '')) {
7074: $bodytag .= qq|<div id="LC_realm" role="complementary"><em>$realm</em> $dc_info</div>|;
7075: }
1.1440 raeburn 7076: if ($need_endlcint) {
7077: $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end');
7078: }
1.1318 raeburn 7079: return $bodytag;
7080: }
1.894 droeschl 7081:
1.1457 raeburn 7082: $bodytag .= '<div class="LC_landmark" style="margin: 3px 0 0 0;" role="navigation" aria-label="'.$labeltext.'">';
1.1318 raeburn 7083: unless ($env{'request.symb'} =~ m/\.page___\d+___/) {
7084: $bodytag .= qq|<div id="LC_nav_bar">$left $role</div>|;
7085: }
1.916 droeschl 7086:
1.1454 raeburn 7087: $bodytag .= $right.'</div>';
1.852 droeschl 7088:
1.1318 raeburn 7089: if ($dc_info) {
7090: $dc_info = &dc_courseid_toggle($dc_info);
7091: }
1.1454 raeburn 7092: unless (($realm eq '') && ($dc_info eq '')) {
1.1457 raeburn 7093: $bodytag .= qq|<div id="LC_realm" role="complementary">$realm $dc_info</div>|;
1.1454 raeburn 7094: }
1.1457 raeburn 7095: $bodytag .= qq|<div style="clear: both; margin: 5px 0 0 0;"></div>|;
1.917 raeburn 7096: }
1.916 droeschl 7097:
1.1169 raeburn 7098: #if directed to not display the secondary menu, don't.
1.1168 raeburn 7099: if ($args->{'no_secondary_menu'}) {
1.1440 raeburn 7100: if ($need_endlcint) {
7101: $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end');
7102: }
1.1168 raeburn 7103: return $bodytag;
7104: }
1.1169 raeburn 7105: #don't show menus for public users
1.954 raeburn 7106: if (!$public){
1.1318 raeburn 7107: unless ($args->{'no_inline_menu'}) {
1.1459 raeburn 7108: $bodytag .= '<div class="LC_landmark" role="navigation" aria-label="Secondary Links">'.
7109: Apache::lonmenu::secondary_menu($httphost,$ltiscope,$ltimenu,
1.1359 raeburn 7110: $args->{'no_primary_menu'},
1.1369 raeburn 7111: $menucoll,$menuref,
1.1380 raeburn 7112: $args->{'links_disabled'},
1.1459 raeburn 7113: $args->{'links_target'}).
1.1460 raeburn 7114: '</div>';
1.1318 raeburn 7115: }
1.903 droeschl 7116: $bodytag .= Apache::lonmenu::serverform();
1.1440 raeburn 7117: if ($need_endlcint) {
7118: $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end');
7119: }
1.920 raeburn 7120: if ($env{'request.state'} eq 'construct') {
1.962 droeschl 7121: $bodytag .= &Apache::lonmenu::innerregister($forcereg,
1.1385 raeburn 7122: $args->{'bread_crumbs'},'','',$hostname,
7123: $ltiscope,$ltiuri,$showncrumbsref);
1.1096 raeburn 7124: } elsif ($forcereg) {
7125: $bodytag .= &Apache::lonmenu::innerregister($forcereg,undef,
1.1385 raeburn 7126: $args->{'group'},$args->{'hide_buttons'},
7127: $hostname,$ltiscope,$ltiuri,$showncrumbsref);
1.1096 raeburn 7128: } else {
1.1459 raeburn 7129: $bodytag .=
1.1096 raeburn 7130: &Apache::lonmenu::prepare_functions($env{'request.noversionuri'},
7131: $forcereg,$args->{'group'},
7132: $args->{'bread_crumbs'},
1.1274 raeburn 7133: $advtoolsref,'',$hostname);
1.920 raeburn 7134: }
1.1440 raeburn 7135: } else {
7136: # this is to separate menu from content when there's no secondary
1.1441 raeburn 7137: # menu. Especially needed for publicly accessible resources.
1.1459 raeburn 7138: $bodytag .= '<hr style="clear:both" role="complementary" />';
1.1440 raeburn 7139: if ($need_endlcint) {
7140: $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end');
7141: }
1.235 raeburn 7142: }
1.1423 raeburn 7143: if ($args->{'collapsible_header'} ne '') {
7144: $bodytag .= $args->{'collapsible_header'}.
7145: '<div id="LC_collapsible_separator"></div>'.
1.1421 raeburn 7146: '</div></div>';
7147: }
1.235 raeburn 7148: return $bodytag;
1.182 matthew 7149: }
7150:
1.917 raeburn 7151: sub dc_courseid_toggle {
7152: my ($dc_info) = @_;
1.980 raeburn 7153: return ' <span id="dccidtext" class="LC_cusr_subheading LC_nobreak">'.
1.1069 raeburn 7154: '<a href="javascript:showCourseID();" class="LC_menubuttons_link">'.
1.917 raeburn 7155: &mt('(More ...)').'</a></span>'.
7156: '<div id="dccid" class="LC_dccid">'.$dc_info.'</div>';
7157: }
7158:
1.330 albertel 7159: sub make_attr_string {
7160: my ($register,$attr_ref) = @_;
7161:
7162: if ($attr_ref && !ref($attr_ref)) {
7163: die("addentries Must be a hash ref ".
7164: join(':',caller(1))." ".
7165: join(':',caller(0))." ");
7166: }
7167:
7168: if ($register) {
1.339 albertel 7169: my ($on_load,$on_unload);
7170: foreach my $key (keys(%{$attr_ref})) {
7171: if (lc($key) eq 'onload') {
7172: $on_load.=$attr_ref->{$key}.';';
7173: delete($attr_ref->{$key});
7174:
7175: } elsif (lc($key) eq 'onunload') {
7176: $on_unload.=$attr_ref->{$key}.';';
7177: delete($attr_ref->{$key});
7178: }
7179: }
1.953 droeschl 7180: $attr_ref->{'onload'} = $on_load;
7181: $attr_ref->{'onunload'}= $on_unload;
1.330 albertel 7182: }
1.339 albertel 7183:
1.330 albertel 7184: my $attr_string;
1.1159 raeburn 7185: foreach my $attr (sort(keys(%$attr_ref))) {
1.330 albertel 7186: $attr_string .= " $attr=\"".$attr_ref->{$attr}.'" ';
7187: }
7188: return $attr_string;
7189: }
7190:
7191:
1.182 matthew 7192: ###############################################
1.251 albertel 7193: ###############################################
7194:
7195: =pod
7196:
7197: =item * &endbodytag()
7198:
7199: Returns a uniform footer for LON-CAPA web pages.
7200:
1.635 raeburn 7201: Inputs: 1 - optional reference to an args hash
7202: If in the hash, key for noredirectlink has a value which evaluates to true,
7203: a 'Continue' link is not displayed if the page contains an
7204: internal redirect in the <head></head> section,
7205: i.e., $env{'internal.head.redirect'} exists
1.251 albertel 7206:
7207: =cut
7208:
7209: sub endbodytag {
1.635 raeburn 7210: my ($args) = @_;
1.1080 raeburn 7211: my $endbodytag;
7212: unless ((ref($args) eq 'HASH') && ($args->{'notbody'})) {
7213: $endbodytag='</body>';
7214: }
1.315 albertel 7215: if ( exists( $env{'internal.head.redirect'} ) ) {
1.635 raeburn 7216: if (!(ref($args) eq 'HASH' && $args->{'noredirectlink'})) {
1.1386 raeburn 7217: my ($endbodyjs,$idattr);
7218: if ($env{'internal.head.to_opener'}) {
7219: my $linkid = 'LC_continue_link';
7220: $idattr = ' id="'.$linkid.'"';
7221: my $redirect_for_js = &js_escape($env{'internal.head.redirect'});
7222: $endbodyjs=<<ENDJS;
7223: <script type="text/javascript">
7224: // <![CDATA[
7225: function ebFunction(evt) {
7226: evt.preventDefault();
7227: var dest = '$redirect_for_js';
7228: if (window.opener != null && !window.opener.closed) {
7229: window.opener.location.href=dest;
7230: window.close();
7231: } else {
7232: window.location.href=dest;
7233: }
7234: return false;
7235: }
7236:
7237: \$(document).ready(function () {
7238: if (document.getElementById('$linkid')) {
7239: var clickelem = document.getElementById('$linkid');
7240: clickelem.addEventListener('click',ebFunction,false);
7241: }
7242: });
7243: // ]]>
7244: </script>
7245: ENDJS
7246: }
1.635 raeburn 7247: $endbodytag=
1.1386 raeburn 7248: "$endbodyjs<br /><a href=\"$env{'internal.head.redirect'}\"$idattr>".
1.635 raeburn 7249: &mt('Continue').'</a>'.
7250: $endbodytag;
7251: }
1.315 albertel 7252: }
1.1411 raeburn 7253: if ((ref($args) eq 'HASH') && ($args->{'dashjs'})) {
7254: $endbodytag = &Apache::lonhtmlcommon::dash_to_minus_js().$endbodytag;
7255: }
1.251 albertel 7256: return $endbodytag;
7257: }
7258:
1.352 albertel 7259: =pod
7260:
7261: =item * &standard_css()
7262:
7263: Returns a style sheet
7264:
7265: Inputs: (all optional)
7266: domain -> force to color decorate a page for a specific
7267: domain
7268: function -> force usage of a specific rolish color scheme
7269: bgcolor -> override the default page bgcolor
7270:
7271: =cut
7272:
1.343 albertel 7273: sub standard_css {
1.345 albertel 7274: my ($function,$domain,$bgcolor) = @_;
1.352 albertel 7275: $function = &get_users_function() if (!$function);
7276: my $tabbg = &designparm($function.'.tabbg', $domain);
7277: my $font = &designparm($function.'.font', $domain);
1.801 tempelho 7278: my $fontmenu = &designparm($function.'.fontmenu', $domain);
1.791 tempelho 7279: #second colour for later usage
1.345 albertel 7280: my $sidebg = &designparm($function.'.sidebg',$domain);
1.382 albertel 7281: my $pgbg_or_bgcolor =
7282: $bgcolor ||
1.352 albertel 7283: &designparm($function.'.pgbg', $domain);
1.382 albertel 7284: my $pgbg = &designparm($function.'.pgbg', $domain);
1.352 albertel 7285: my $alink = &designparm($function.'.alink', $domain);
7286: my $vlink = &designparm($function.'.vlink', $domain);
7287: my $link = &designparm($function.'.link', $domain);
7288:
1.602 albertel 7289: my $sans = 'Verdana,Arial,Helvetica,sans-serif';
1.395 albertel 7290: my $mono = 'monospace';
1.850 bisitz 7291: my $data_table_head = $sidebg;
7292: my $data_table_light = '#FAFAFA';
1.1060 bisitz 7293: my $data_table_dark = '#E0E0E0';
1.470 banghart 7294: my $data_table_darker = '#CCCCCC';
1.349 albertel 7295: my $data_table_highlight = '#FFFF00';
1.352 albertel 7296: my $mail_new = '#FFBB77';
7297: my $mail_new_hover = '#DD9955';
7298: my $mail_read = '#BBBB77';
7299: my $mail_read_hover = '#999944';
7300: my $mail_replied = '#AAAA88';
7301: my $mail_replied_hover = '#888855';
7302: my $mail_other = '#99BBBB';
7303: my $mail_other_hover = '#669999';
1.391 albertel 7304: my $table_header = '#DDDDDD';
1.489 raeburn 7305: my $feedback_link_bg = '#BBBBBB';
1.911 bisitz 7306: my $lg_border_color = '#C8C8C8';
1.952 onken 7307: my $button_hover = '#BF2317';
1.392 albertel 7308:
1.608 albertel 7309: my $border = ($env{'browser.type'} eq 'explorer' ||
1.911 bisitz 7310: $env{'browser.type'} eq 'safari' ) ? '0 2px 0 2px'
7311: : '0 3px 0 4px';
1.448 albertel 7312:
1.523 albertel 7313:
1.343 albertel 7314: return <<END;
1.947 droeschl 7315:
7316: /* needed for iframe to allow 100% height in FF */
7317: body, html {
7318: margin: 0;
7319: padding: 0 0.5%;
7320: height: 99%; /* to avoid scrollbars */
7321: }
7322:
1.795 www 7323: body {
1.911 bisitz 7324: font-family: $sans;
7325: line-height:130%;
7326: font-size:0.83em;
7327: color:$font;
1.1436 raeburn 7328: background-color: $pgbg_or_bgcolor;
1.795 www 7329: }
7330:
1.959 onken 7331: a:focus,
7332: a:focus img {
1.795 www 7333: color: red;
7334: }
1.698 harmsja 7335:
1.911 bisitz 7336: form, .inline {
7337: display: inline;
1.795 www 7338: }
1.721 harmsja 7339:
1.1453 raeburn 7340: .LC_landmark {
7341: margin: 0;
7342: padding: 0;
7343: border: none;
7344: }
7345:
1.1443 raeburn 7346: .LC_visually_hidden:not(:focus):not(:active) {
7347: clip-path: inset(50%);
7348: height: 1px;
7349: overflow: hidden;
7350: position: absolute;
7351: white-space: nowrap;
7352: width: 1px;
7353: display: inline;
7354: }
7355:
1.1453 raeburn 7356: .LC_heading_2 {
7357: font-size: 1.17em;
7358: }
7359:
1.1458 raeburn 7360: .LC_heading_3 {
7361: font-size: 1.0em;
7362: }
7363:
1.1421 raeburn 7364: .LC_menus_content.shown{
1.1428 raeburn 7365: display: block;
1.1421 raeburn 7366: }
7367:
7368: .LC_menus_content.hidden {
7369: display: none;
7370: }
7371:
1.795 www 7372: .LC_right {
1.911 bisitz 7373: text-align:right;
1.795 www 7374: }
7375:
1.1470 raeburn 7376: .LC_left {
7377: text-align:left;
7378: }
7379:
1.1449 raeburn 7380: .LC_center {
7381: text-align:center;
7382: }
7383:
1.795 www 7384: .LC_middle {
1.911 bisitz 7385: vertical-align:middle;
1.795 www 7386: }
1.721 harmsja 7387:
1.1130 raeburn 7388: .LC_floatleft {
7389: float: left;
7390: }
7391:
7392: .LC_floatright {
7393: float: right;
7394: }
7395:
1.911 bisitz 7396: .LC_400Box {
7397: width:400px;
7398: }
1.721 harmsja 7399:
1.1421 raeburn 7400: #LC_collapsible_separator {
7401: border: 1px solid black;
7402: width: 99.9%;
7403: height: 0px;
7404: }
7405:
1.947 droeschl 7406: .LC_iframecontainer {
7407: width: 98%;
7408: margin: 0;
7409: position: fixed;
7410: top: 8.5em;
7411: bottom: 0;
7412: }
7413:
7414: .LC_iframecontainer iframe{
7415: border: none;
7416: width: 100%;
7417: height: 100%;
7418: }
7419:
1.778 bisitz 7420: .LC_filename {
7421: font-family: $mono;
7422: white-space:pre;
1.921 bisitz 7423: font-size: 120%;
1.778 bisitz 7424: }
7425:
7426: .LC_fileicon {
7427: border: none;
7428: height: 1.3em;
7429: vertical-align: text-bottom;
7430: margin-right: 0.3em;
7431: text-decoration:none;
7432: }
7433:
1.1008 www 7434: .LC_setting {
7435: text-decoration:underline;
7436: }
7437:
1.350 albertel 7438: .LC_error {
7439: color: red;
7440: }
1.795 www 7441:
1.1097 bisitz 7442: .LC_warning {
7443: color: darkorange;
7444: }
7445:
1.457 albertel 7446: .LC_diff_removed {
1.733 bisitz 7447: color: red;
1.394 albertel 7448: }
1.532 albertel 7449:
7450: .LC_info,
1.457 albertel 7451: .LC_success,
7452: .LC_diff_added {
1.350 albertel 7453: color: green;
7454: }
1.795 www 7455:
1.802 bisitz 7456: div.LC_confirm_box {
7457: background-color: #FAFAFA;
7458: border: 1px solid $lg_border_color;
7459: margin-right: 0;
7460: padding: 5px;
7461: }
7462:
7463: div.LC_confirm_box .LC_error img,
7464: div.LC_confirm_box .LC_success img {
7465: vertical-align: middle;
7466: }
7467:
1.1242 raeburn 7468: .LC_maxwidth {
7469: max-width: 100%;
7470: height: auto;
7471: }
7472:
1.1243 raeburn 7473: .LC_textsize_mobile {
7474: \@media only screen and (max-device-width: 480px) {
7475: -webkit-text-size-adjust:100%; -moz-text-size-adjust:100%; -ms-text-size-adjust:100%;
7476: }
7477: }
7478:
1.440 albertel 7479: .LC_icon {
1.771 droeschl 7480: border: none;
1.790 droeschl 7481: vertical-align: middle;
1.771 droeschl 7482: }
7483:
1.543 albertel 7484: .LC_docs_spacer {
7485: width: 25px;
7486: height: 1px;
1.771 droeschl 7487: border: none;
1.543 albertel 7488: }
1.346 albertel 7489:
1.532 albertel 7490: .LC_internal_info {
1.735 bisitz 7491: color: #999999;
1.532 albertel 7492: }
7493:
1.794 www 7494: .LC_discussion {
1.1050 www 7495: background: $data_table_dark;
1.911 bisitz 7496: border: 1px solid black;
7497: margin: 2px;
1.794 www 7498: }
7499:
7500: .LC_disc_action_left {
1.1050 www 7501: background: $sidebg;
1.911 bisitz 7502: text-align: left;
1.1050 www 7503: padding: 4px;
7504: margin: 2px;
1.794 www 7505: }
7506:
7507: .LC_disc_action_right {
1.1050 www 7508: background: $sidebg;
1.911 bisitz 7509: text-align: right;
1.1050 www 7510: padding: 4px;
7511: margin: 2px;
1.794 www 7512: }
7513:
7514: .LC_disc_new_item {
1.911 bisitz 7515: background: white;
7516: border: 2px solid red;
1.1050 www 7517: margin: 4px;
7518: padding: 4px;
1.794 www 7519: }
7520:
7521: .LC_disc_old_item {
1.911 bisitz 7522: background: white;
1.1050 www 7523: margin: 4px;
7524: padding: 4px;
1.794 www 7525: }
7526:
1.1468 raeburn 7527: .LC_pastsubmission {
1.458 albertel 7528: border: 1px solid black;
7529: margin: 2px;
1.1468 raeburn 7530: padding: 2px;
1.458 albertel 7531: }
7532:
1.924 bisitz 7533: table#LC_menubuttons {
1.345 albertel 7534: width: 100%;
7535: background: $pgbg;
1.392 albertel 7536: border: 2px;
1.402 albertel 7537: border-collapse: separate;
1.803 bisitz 7538: padding: 0;
1.345 albertel 7539: }
1.392 albertel 7540:
1.801 tempelho 7541: table#LC_title_bar a {
7542: color: $fontmenu;
7543: }
1.836 bisitz 7544:
1.807 droeschl 7545: table#LC_title_bar {
1.819 tempelho 7546: clear: both;
1.836 bisitz 7547: display: none;
1.807 droeschl 7548: }
7549:
1.795 www 7550: table#LC_title_bar,
1.933 droeschl 7551: table.LC_breadcrumbs, /* obsolete? */
1.393 albertel 7552: table#LC_title_bar.LC_with_remote {
1.359 albertel 7553: width: 100%;
1.392 albertel 7554: border-color: $pgbg;
7555: border-style: solid;
7556: border-width: $border;
1.379 albertel 7557: background: $pgbg;
1.801 tempelho 7558: color: $fontmenu;
1.392 albertel 7559: border-collapse: collapse;
1.803 bisitz 7560: padding: 0;
1.819 tempelho 7561: margin: 0;
1.359 albertel 7562: }
1.795 www 7563:
1.933 droeschl 7564: ul.LC_breadcrumb_tools_outerlist {
1.913 droeschl 7565: margin: 0;
7566: padding: 0;
1.933 droeschl 7567: position: relative;
7568: list-style: none;
1.913 droeschl 7569: }
1.933 droeschl 7570: ul.LC_breadcrumb_tools_outerlist li {
1.913 droeschl 7571: display: inline;
7572: }
1.933 droeschl 7573:
7574: .LC_breadcrumb_tools_navigation {
1.913 droeschl 7575: padding: 0;
1.933 droeschl 7576: margin: 0;
7577: float: left;
1.913 droeschl 7578: }
1.933 droeschl 7579: .LC_breadcrumb_tools_tools {
7580: padding: 0;
7581: margin: 0;
1.913 droeschl 7582: float: right;
7583: }
7584:
1.1240 raeburn 7585: .LC_placement_prog {
7586: padding-right: 20px;
7587: font-weight: bold;
7588: font-size: 90%;
7589: }
7590:
1.359 albertel 7591: table#LC_title_bar td {
7592: background: $tabbg;
7593: }
1.795 www 7594:
1.911 bisitz 7595: table#LC_menubuttons img {
1.803 bisitz 7596: border: none;
1.346 albertel 7597: }
1.795 www 7598:
1.842 droeschl 7599: .LC_breadcrumbs_component {
1.911 bisitz 7600: float: right;
7601: margin: 0 1em;
1.357 albertel 7602: }
1.842 droeschl 7603: .LC_breadcrumbs_component img {
1.911 bisitz 7604: vertical-align: middle;
1.777 tempelho 7605: }
1.795 www 7606:
1.1243 raeburn 7607: .LC_breadcrumbs_hoverable {
7608: background: $sidebg;
7609: }
7610:
1.383 albertel 7611: td.LC_table_cell_checkbox {
7612: text-align: center;
7613: }
1.795 www 7614:
7615: .LC_fontsize_small {
1.911 bisitz 7616: font-size: 70%;
1.705 tempelho 7617: }
7618:
1.844 bisitz 7619: #LC_breadcrumbs {
1.911 bisitz 7620: clear:both;
7621: background: $sidebg;
7622: border-bottom: 1px solid $lg_border_color;
7623: line-height: 2.5em;
1.933 droeschl 7624: overflow: hidden;
1.911 bisitz 7625: margin: 0;
7626: padding: 0;
1.995 raeburn 7627: text-align: left;
1.819 tempelho 7628: }
1.862 bisitz 7629:
1.1098 bisitz 7630: .LC_head_subbox, .LC_actionbox {
1.911 bisitz 7631: clear:both;
7632: background: #F8F8F8; /* $sidebg; */
1.915 droeschl 7633: border: 1px solid $sidebg;
1.1098 bisitz 7634: margin: 0 0 10px 0;
1.966 bisitz 7635: padding: 3px;
1.995 raeburn 7636: text-align: left;
1.822 bisitz 7637: }
7638:
1.795 www 7639: .LC_fontsize_medium {
1.911 bisitz 7640: font-size: 85%;
1.705 tempelho 7641: }
7642:
1.795 www 7643: .LC_fontsize_large {
1.911 bisitz 7644: font-size: 120%;
1.705 tempelho 7645: }
7646:
1.346 albertel 7647: .LC_menubuttons_inline_text {
7648: color: $font;
1.698 harmsja 7649: font-size: 90%;
1.701 harmsja 7650: padding-left:3px;
1.346 albertel 7651: }
7652:
1.934 droeschl 7653: .LC_menubuttons_inline_text img{
7654: vertical-align: middle;
7655: }
7656:
1.1051 www 7657: li.LC_menubuttons_inline_text img {
1.951 onken 7658: cursor:pointer;
1.1002 droeschl 7659: text-decoration: none;
1.951 onken 7660: }
7661:
1.526 www 7662: .LC_menubuttons_link {
7663: text-decoration: none;
7664: }
1.795 www 7665:
1.522 albertel 7666: .LC_menubuttons_category {
1.521 www 7667: color: $font;
1.526 www 7668: background: $pgbg;
1.521 www 7669: font-size: larger;
7670: font-weight: bold;
7671: }
7672:
1.346 albertel 7673: td.LC_menubuttons_text {
1.911 bisitz 7674: color: $font;
1.346 albertel 7675: }
1.706 harmsja 7676:
1.346 albertel 7677: .LC_current_location {
7678: background: $tabbg;
7679: }
1.795 www 7680:
1.1286 raeburn 7681: td.LC_zero_height {
7682: line-height: 0;
7683: cellpadding: 0;
7684: }
7685:
1.938 bisitz 7686: table.LC_data_table {
1.347 albertel 7687: border: 1px solid #000000;
1.402 albertel 7688: border-collapse: separate;
1.426 albertel 7689: border-spacing: 1px;
1.610 albertel 7690: background: $pgbg;
1.347 albertel 7691: }
1.795 www 7692:
1.422 albertel 7693: .LC_data_table_dense {
7694: font-size: small;
7695: }
1.795 www 7696:
1.507 raeburn 7697: table.LC_nested_outer {
7698: border: 1px solid #000000;
1.589 raeburn 7699: border-collapse: collapse;
1.803 bisitz 7700: border-spacing: 0;
1.507 raeburn 7701: width: 100%;
7702: }
1.795 www 7703:
1.879 raeburn 7704: table.LC_innerpickbox,
1.507 raeburn 7705: table.LC_nested {
1.803 bisitz 7706: border: none;
1.589 raeburn 7707: border-collapse: collapse;
1.803 bisitz 7708: border-spacing: 0;
1.507 raeburn 7709: width: 100%;
7710: }
1.795 www 7711:
1.911 bisitz 7712: table.LC_data_table tr th,
7713: table.LC_calendar tr th,
1.879 raeburn 7714: table.LC_prior_tries tr th,
7715: table.LC_innerpickbox tr th {
1.349 albertel 7716: font-weight: bold;
7717: background-color: $data_table_head;
1.801 tempelho 7718: color:$fontmenu;
1.701 harmsja 7719: font-size:90%;
1.347 albertel 7720: }
1.795 www 7721:
1.879 raeburn 7722: table.LC_innerpickbox tr th,
7723: table.LC_innerpickbox tr td {
7724: vertical-align: top;
7725: }
7726:
1.711 raeburn 7727: table.LC_data_table tr.LC_info_row > td {
1.735 bisitz 7728: background-color: #CCCCCC;
1.711 raeburn 7729: font-weight: bold;
7730: text-align: left;
7731: }
1.795 www 7732:
1.912 bisitz 7733: table.LC_data_table tr.LC_odd_row > td {
7734: background-color: $data_table_light;
7735: padding: 2px;
7736: vertical-align: top;
7737: }
7738:
1.809 bisitz 7739: table.LC_pick_box tr > td.LC_odd_row {
1.349 albertel 7740: background-color: $data_table_light;
1.912 bisitz 7741: vertical-align: top;
7742: }
7743:
7744: table.LC_data_table tr.LC_even_row > td {
7745: background-color: $data_table_dark;
1.425 albertel 7746: padding: 2px;
1.900 bisitz 7747: vertical-align: top;
1.347 albertel 7748: }
1.795 www 7749:
1.809 bisitz 7750: table.LC_pick_box tr > td.LC_even_row {
1.349 albertel 7751: background-color: $data_table_dark;
1.900 bisitz 7752: vertical-align: top;
1.347 albertel 7753: }
1.795 www 7754:
1.425 albertel 7755: table.LC_data_table tr.LC_data_table_highlight td {
7756: background-color: $data_table_darker;
7757: }
1.795 www 7758:
1.639 raeburn 7759: table.LC_data_table tr td.LC_leftcol_header {
7760: background-color: $data_table_head;
7761: font-weight: bold;
7762: }
1.795 www 7763:
1.451 albertel 7764: table.LC_data_table tr.LC_empty_row td,
1.507 raeburn 7765: table.LC_nested tr.LC_empty_row td {
1.421 albertel 7766: font-weight: bold;
7767: font-style: italic;
7768: text-align: center;
7769: padding: 8px;
1.347 albertel 7770: }
1.795 www 7771:
1.1114 raeburn 7772: table.LC_data_table tr.LC_empty_row td,
7773: table.LC_data_table tr.LC_footer_row td {
1.940 bisitz 7774: background-color: $sidebg;
7775: }
7776:
7777: table.LC_nested tr.LC_empty_row td {
7778: background-color: #FFFFFF;
7779: }
7780:
1.890 droeschl 7781: table.LC_caption {
7782: }
7783:
1.1469 raeburn 7784: caption.LC_caption_prefs {
7785: font-weight: normal;
7786: text-align: left;
7787: padding-bottom: 0.8em;
7788: }
7789:
1.507 raeburn 7790: table.LC_nested tr.LC_empty_row td {
1.465 albertel 7791: padding: 4ex
7792: }
1.795 www 7793:
1.507 raeburn 7794: table.LC_nested_outer tr th {
7795: font-weight: bold;
1.801 tempelho 7796: color:$fontmenu;
1.507 raeburn 7797: background-color: $data_table_head;
1.701 harmsja 7798: font-size: small;
1.507 raeburn 7799: border-bottom: 1px solid #000000;
7800: }
1.795 www 7801:
1.507 raeburn 7802: table.LC_nested_outer tr td.LC_subheader {
7803: background-color: $data_table_head;
7804: font-weight: bold;
7805: font-size: small;
7806: border-bottom: 1px solid #000000;
7807: text-align: right;
1.451 albertel 7808: }
1.795 www 7809:
1.507 raeburn 7810: table.LC_nested tr.LC_info_row td {
1.735 bisitz 7811: background-color: #CCCCCC;
1.451 albertel 7812: font-weight: bold;
7813: font-size: small;
1.507 raeburn 7814: text-align: center;
7815: }
1.795 www 7816:
1.589 raeburn 7817: table.LC_nested tr.LC_info_row td.LC_left_item,
7818: table.LC_nested_outer tr th.LC_left_item {
1.507 raeburn 7819: text-align: left;
1.451 albertel 7820: }
1.795 www 7821:
1.507 raeburn 7822: table.LC_nested td {
1.735 bisitz 7823: background-color: #FFFFFF;
1.451 albertel 7824: font-size: small;
1.507 raeburn 7825: }
1.795 www 7826:
1.507 raeburn 7827: table.LC_nested_outer tr th.LC_right_item,
7828: table.LC_nested tr.LC_info_row td.LC_right_item,
7829: table.LC_nested tr.LC_odd_row td.LC_right_item,
7830: table.LC_nested tr td.LC_right_item {
1.451 albertel 7831: text-align: right;
7832: }
7833:
1.507 raeburn 7834: table.LC_nested tr.LC_odd_row td {
1.735 bisitz 7835: background-color: #EEEEEE;
1.451 albertel 7836: }
7837:
1.473 raeburn 7838: table.LC_createuser {
7839: }
7840:
7841: table.LC_createuser tr.LC_section_row td {
1.701 harmsja 7842: font-size: small;
1.473 raeburn 7843: }
7844:
7845: table.LC_createuser tr.LC_info_row td {
1.735 bisitz 7846: background-color: #CCCCCC;
1.473 raeburn 7847: font-weight: bold;
7848: text-align: center;
7849: }
7850:
1.349 albertel 7851: table.LC_calendar {
7852: border: 1px solid #000000;
7853: border-collapse: collapse;
1.917 raeburn 7854: width: 98%;
1.349 albertel 7855: }
1.795 www 7856:
1.349 albertel 7857: table.LC_calendar_pickdate {
7858: font-size: xx-small;
7859: }
1.795 www 7860:
1.349 albertel 7861: table.LC_calendar tr td {
7862: border: 1px solid #000000;
7863: vertical-align: top;
1.917 raeburn 7864: width: 14%;
1.349 albertel 7865: }
1.795 www 7866:
1.349 albertel 7867: table.LC_calendar tr td.LC_calendar_day_empty {
7868: background-color: $data_table_dark;
7869: }
1.795 www 7870:
1.779 bisitz 7871: table.LC_calendar tr td.LC_calendar_day_current {
7872: background-color: $data_table_highlight;
1.777 tempelho 7873: }
1.795 www 7874:
1.938 bisitz 7875: table.LC_data_table tr td.LC_mail_new {
1.349 albertel 7876: background-color: $mail_new;
7877: }
1.795 www 7878:
1.938 bisitz 7879: table.LC_data_table tr.LC_mail_new:hover {
1.349 albertel 7880: background-color: $mail_new_hover;
7881: }
1.795 www 7882:
1.938 bisitz 7883: table.LC_data_table tr td.LC_mail_read {
1.349 albertel 7884: background-color: $mail_read;
7885: }
1.795 www 7886:
1.938 bisitz 7887: /*
7888: table.LC_data_table tr.LC_mail_read:hover {
1.349 albertel 7889: background-color: $mail_read_hover;
7890: }
1.938 bisitz 7891: */
1.795 www 7892:
1.938 bisitz 7893: table.LC_data_table tr td.LC_mail_replied {
1.349 albertel 7894: background-color: $mail_replied;
7895: }
1.795 www 7896:
1.938 bisitz 7897: /*
7898: table.LC_data_table tr.LC_mail_replied:hover {
1.349 albertel 7899: background-color: $mail_replied_hover;
7900: }
1.938 bisitz 7901: */
1.795 www 7902:
1.938 bisitz 7903: table.LC_data_table tr td.LC_mail_other {
1.349 albertel 7904: background-color: $mail_other;
7905: }
1.795 www 7906:
1.938 bisitz 7907: /*
7908: table.LC_data_table tr.LC_mail_other:hover {
1.349 albertel 7909: background-color: $mail_other_hover;
7910: }
1.938 bisitz 7911: */
1.494 raeburn 7912:
1.777 tempelho 7913: table.LC_data_table tr > td.LC_browser_file,
7914: table.LC_data_table tr > td.LC_browser_file_published {
1.899 bisitz 7915: background: #AAEE77;
1.389 albertel 7916: }
1.795 www 7917:
1.777 tempelho 7918: table.LC_data_table tr > td.LC_browser_file_locked,
7919: table.LC_data_table tr > td.LC_browser_file_unpublished {
1.389 albertel 7920: background: #FFAA99;
1.387 albertel 7921: }
1.795 www 7922:
1.777 tempelho 7923: table.LC_data_table tr > td.LC_browser_file_obsolete {
1.899 bisitz 7924: background: #888888;
1.779 bisitz 7925: }
1.795 www 7926:
1.777 tempelho 7927: table.LC_data_table tr > td.LC_browser_file_modified,
1.779 bisitz 7928: table.LC_data_table tr > td.LC_browser_file_metamodified {
1.899 bisitz 7929: background: #F8F866;
1.777 tempelho 7930: }
1.795 www 7931:
1.696 bisitz 7932: table.LC_data_table tr.LC_browser_folder > td {
1.899 bisitz 7933: background: #E0E8FF;
1.387 albertel 7934: }
1.696 bisitz 7935:
1.707 bisitz 7936: table.LC_data_table tr > td.LC_roles_is {
1.911 bisitz 7937: /* background: #77FF77; */
1.707 bisitz 7938: }
1.795 www 7939:
1.707 bisitz 7940: table.LC_data_table tr > td.LC_roles_future {
1.939 bisitz 7941: border-right: 8px solid #FFFF77;
1.707 bisitz 7942: }
1.795 www 7943:
1.707 bisitz 7944: table.LC_data_table tr > td.LC_roles_will {
1.939 bisitz 7945: border-right: 8px solid #FFAA77;
1.707 bisitz 7946: }
1.795 www 7947:
1.707 bisitz 7948: table.LC_data_table tr > td.LC_roles_expired {
1.939 bisitz 7949: border-right: 8px solid #FF7777;
1.707 bisitz 7950: }
1.795 www 7951:
1.707 bisitz 7952: table.LC_data_table tr > td.LC_roles_will_not {
1.939 bisitz 7953: border-right: 8px solid #AAFF77;
1.707 bisitz 7954: }
1.795 www 7955:
1.707 bisitz 7956: table.LC_data_table tr > td.LC_roles_selected {
1.939 bisitz 7957: border-right: 8px solid #11CC55;
1.707 bisitz 7958: }
7959:
1.1469 raeburn 7960: table.LC_data_table tr.LC_prefs_row {
7961: line-height: 250%;
7962: }
7963:
1.388 albertel 7964: span.LC_current_location {
1.701 harmsja 7965: font-size:larger;
1.388 albertel 7966: background: $pgbg;
7967: }
1.387 albertel 7968:
1.1029 www 7969: span.LC_current_nav_location {
7970: font-weight:bold;
7971: background: $sidebg;
7972: }
7973:
1.395 albertel 7974: span.LC_parm_menu_item {
7975: font-size: larger;
7976: }
1.795 www 7977:
1.395 albertel 7978: span.LC_parm_scope_all {
7979: color: red;
7980: }
1.795 www 7981:
1.395 albertel 7982: span.LC_parm_scope_folder {
7983: color: green;
7984: }
1.795 www 7985:
1.395 albertel 7986: span.LC_parm_scope_resource {
7987: color: orange;
7988: }
1.795 www 7989:
1.395 albertel 7990: span.LC_parm_part {
7991: color: blue;
7992: }
1.795 www 7993:
1.911 bisitz 7994: span.LC_parm_folder,
7995: span.LC_parm_symb {
1.395 albertel 7996: font-size: x-small;
7997: font-family: $mono;
7998: color: #AAAAAA;
7999: }
8000:
1.977 bisitz 8001: ul.LC_parm_parmlist li {
8002: display: inline-block;
8003: padding: 0.3em 0.8em;
8004: vertical-align: top;
8005: width: 150px;
8006: border-top:1px solid $lg_border_color;
8007: }
8008:
1.795 www 8009: td.LC_parm_overview_level_menu,
8010: td.LC_parm_overview_map_menu,
8011: td.LC_parm_overview_parm_selectors,
8012: td.LC_parm_overview_restrictions {
1.396 albertel 8013: border: 1px solid black;
8014: border-collapse: collapse;
8015: }
1.795 www 8016:
1.1285 raeburn 8017: span.LC_parm_recursive,
8018: td.LC_parm_recursive {
8019: font-weight: bold;
8020: font-size: smaller;
8021: }
8022:
1.396 albertel 8023: table.LC_parm_overview_restrictions td {
8024: border-width: 1px 4px 1px 4px;
8025: border-style: solid;
8026: border-color: $pgbg;
8027: text-align: center;
8028: }
1.795 www 8029:
1.396 albertel 8030: table.LC_parm_overview_restrictions th {
8031: background: $tabbg;
8032: border-width: 1px 4px 1px 4px;
8033: border-style: solid;
8034: border-color: $pgbg;
8035: }
1.795 www 8036:
1.1459 raeburn 8037: h1.LC_helpmenu {
8038: display: inline;
8039: font-size: 100%;
8040: font-weight: normal;
8041: line-height: 1em;
8042: margin: 0;
8043: padding: 0;
8044: border: 0;
1.398 albertel 8045: }
1.795 www 8046:
1.1456 raeburn 8047: .LC_helpdesk_headbox {
8048: border: 2px groove threedface;
8049: padding: 1em;
8050: }
8051:
8052: h1.LC_helpdesk_legend {
8053: float: left;
8054: margin: -1.7em 0 0;
8055: padding: 0 .5em;
1.397 albertel 8056: background: $pgbg;
1.1456 raeburn 8057: font-size: 1em;
8058: font-weight: bold;
8059: }
8060:
8061: h1.LC_helpdesk_title {
8062: display: inline;
8063: font-size: 1em;
8064: line-height: 2.5em;
8065: margin: 0;
1.803 bisitz 8066: padding: 0;
1.1456 raeburn 8067: vertical-align: bottom;
1.397 albertel 8068: }
1.795 www 8069:
1.1456 raeburn 8070: .LC_helpdesk_links {
8071: border: 1px solid black;
8072: padding: 3px;
1.397 albertel 8073: background: $tabbg;
1.399 albertel 8074: text-align: center;
8075: font-weight: bold;
1.1456 raeburn 8076: display: inline;
8077: margin-right: -6px;
8078: }
8079:
8080: .LC_helpdesk_img,
8081: .LC_helpdesk_text {
8082: padding: 0;
8083: margin: 0;
8084: border: 0;
8085: display: inline;
1.397 albertel 8086: }
1.396 albertel 8087:
1.1456 raeburn 8088: .LC_helpdesk_img a:link,
8089: .LC_helpdesk_img a:visited,
8090: .LC_helpdesk_img a:active,
8091: .LC_helpdesk_text a:link,
8092: .LC_helpdesk_text a:visited,
8093: .LC_helpdesk_text a:active {
1.397 albertel 8094: text-decoration: none;
8095: color: $font;
8096: }
1.795 www 8097:
1.1456 raeburn 8098: div.LC_helpdesk_text a:hover {
1.397 albertel 8099: text-decoration: underline;
8100: color: $vlink;
8101: }
1.396 albertel 8102:
1.417 albertel 8103: .LC_chrt_popup_exists {
8104: border: 1px solid #339933;
8105: margin: -1px;
8106: }
1.795 www 8107:
1.417 albertel 8108: .LC_chrt_popup_up {
8109: border: 1px solid yellow;
8110: margin: -1px;
8111: }
1.795 www 8112:
1.417 albertel 8113: .LC_chrt_popup {
8114: border: 1px solid #8888FF;
8115: background: #CCCCFF;
8116: }
1.795 www 8117:
1.421 albertel 8118: table.LC_pick_box {
8119: border-collapse: separate;
8120: background: white;
8121: border: 1px solid black;
8122: border-spacing: 1px;
8123: }
1.795 www 8124:
1.1454 raeburn 8125: table.LC_pick_box th.LC_pick_box_title {
1.850 bisitz 8126: background: $sidebg;
1.421 albertel 8127: font-weight: bold;
1.900 bisitz 8128: text-align: left;
1.740 bisitz 8129: vertical-align: top;
1.421 albertel 8130: width: 184px;
8131: padding: 8px;
8132: }
1.795 www 8133:
1.579 raeburn 8134: table.LC_pick_box td.LC_pick_box_value {
8135: text-align: left;
8136: padding: 8px;
8137: }
1.795 www 8138:
1.579 raeburn 8139: table.LC_pick_box td.LC_pick_box_select {
8140: text-align: left;
8141: padding: 8px;
8142: }
1.795 www 8143:
1.424 albertel 8144: table.LC_pick_box td.LC_pick_box_separator {
1.803 bisitz 8145: padding: 0;
1.421 albertel 8146: height: 1px;
8147: background: black;
8148: }
1.795 www 8149:
1.421 albertel 8150: table.LC_pick_box td.LC_pick_box_submit {
8151: text-align: right;
8152: }
1.795 www 8153:
1.579 raeburn 8154: table.LC_pick_box td.LC_evenrow_value {
8155: text-align: left;
8156: padding: 8px;
8157: background-color: $data_table_light;
8158: }
1.795 www 8159:
1.579 raeburn 8160: table.LC_pick_box td.LC_oddrow_value {
8161: text-align: left;
8162: padding: 8px;
8163: background-color: $data_table_light;
8164: }
1.795 www 8165:
1.579 raeburn 8166: span.LC_helpform_receipt_cat {
8167: font-weight: bold;
8168: }
1.795 www 8169:
1.424 albertel 8170: table.LC_group_priv_box {
8171: background: white;
8172: border: 1px solid black;
8173: border-spacing: 1px;
8174: }
1.795 www 8175:
1.424 albertel 8176: table.LC_group_priv_box td.LC_pick_box_title {
8177: background: $tabbg;
8178: font-weight: bold;
8179: text-align: right;
8180: width: 184px;
8181: }
1.795 www 8182:
1.424 albertel 8183: table.LC_group_priv_box td.LC_groups_fixed {
8184: background: $data_table_light;
8185: text-align: center;
8186: }
1.795 www 8187:
1.424 albertel 8188: table.LC_group_priv_box td.LC_groups_optional {
8189: background: $data_table_dark;
8190: text-align: center;
8191: }
1.795 www 8192:
1.424 albertel 8193: table.LC_group_priv_box td.LC_groups_functionality {
8194: background: $data_table_darker;
8195: text-align: center;
8196: font-weight: bold;
8197: }
1.795 www 8198:
1.424 albertel 8199: table.LC_group_priv td {
8200: text-align: left;
1.803 bisitz 8201: padding: 0;
1.424 albertel 8202: }
8203:
8204: .LC_navbuttons {
8205: margin: 2ex 0ex 2ex 0ex;
8206: }
1.795 www 8207:
1.423 albertel 8208: .LC_topic_bar {
8209: font-weight: bold;
8210: background: $tabbg;
1.918 wenzelju 8211: margin: 1em 0em 1em 2em;
1.805 bisitz 8212: padding: 3px;
1.918 wenzelju 8213: font-size: 1.2em;
1.423 albertel 8214: }
1.795 www 8215:
1.423 albertel 8216: .LC_topic_bar span {
1.918 wenzelju 8217: left: 0.5em;
8218: position: absolute;
1.423 albertel 8219: vertical-align: middle;
1.918 wenzelju 8220: font-size: 1.2em;
1.423 albertel 8221: }
1.795 www 8222:
1.423 albertel 8223: table.LC_course_group_status {
8224: margin: 20px;
8225: }
1.795 www 8226:
1.423 albertel 8227: table.LC_status_selector td {
8228: vertical-align: top;
8229: text-align: center;
1.424 albertel 8230: padding: 4px;
8231: }
1.795 www 8232:
1.599 albertel 8233: div.LC_feedback_link {
1.616 albertel 8234: clear: both;
1.829 kalberla 8235: background: $sidebg;
1.779 bisitz 8236: width: 100%;
1.829 kalberla 8237: padding-bottom: 10px;
8238: border: 1px $tabbg solid;
1.833 kalberla 8239: height: 22px;
8240: line-height: 22px;
8241: padding-top: 5px;
8242: }
8243:
8244: div.LC_feedback_link img {
8245: height: 22px;
1.867 kalberla 8246: vertical-align:middle;
1.829 kalberla 8247: }
8248:
1.911 bisitz 8249: div.LC_feedback_link a {
1.829 kalberla 8250: text-decoration: none;
1.489 raeburn 8251: }
1.795 www 8252:
1.867 kalberla 8253: div.LC_comblock {
1.911 bisitz 8254: display:inline;
1.867 kalberla 8255: color:$font;
8256: font-size:90%;
8257: }
8258:
8259: div.LC_feedback_link div.LC_comblock {
8260: padding-left:5px;
8261: }
8262:
8263: div.LC_feedback_link div.LC_comblock a {
8264: color:$font;
8265: }
8266:
1.489 raeburn 8267: span.LC_feedback_link {
1.858 bisitz 8268: /* background: $feedback_link_bg; */
1.599 albertel 8269: font-size: larger;
8270: }
1.795 www 8271:
1.599 albertel 8272: span.LC_message_link {
1.858 bisitz 8273: /* background: $feedback_link_bg; */
1.599 albertel 8274: font-size: larger;
8275: position: absolute;
8276: right: 1em;
1.489 raeburn 8277: }
1.421 albertel 8278:
1.515 albertel 8279: table.LC_prior_tries {
1.524 albertel 8280: border: 1px solid #000000;
8281: border-collapse: separate;
8282: border-spacing: 1px;
1.515 albertel 8283: }
1.523 albertel 8284:
1.515 albertel 8285: table.LC_prior_tries td {
1.524 albertel 8286: padding: 2px;
1.515 albertel 8287: }
1.523 albertel 8288:
8289: .LC_answer_correct {
1.795 www 8290: background: lightgreen;
8291: color: darkgreen;
8292: padding: 6px;
1.523 albertel 8293: }
1.795 www 8294:
1.523 albertel 8295: .LC_answer_charged_try {
1.797 www 8296: background: #FFAAAA;
1.795 www 8297: color: darkred;
8298: padding: 6px;
1.523 albertel 8299: }
1.795 www 8300:
1.779 bisitz 8301: .LC_answer_not_charged_try,
1.523 albertel 8302: .LC_answer_no_grade,
8303: .LC_answer_late {
1.795 www 8304: background: lightyellow;
1.523 albertel 8305: color: black;
1.795 www 8306: padding: 6px;
1.523 albertel 8307: }
1.795 www 8308:
1.523 albertel 8309: .LC_answer_previous {
1.795 www 8310: background: lightblue;
8311: color: darkblue;
8312: padding: 6px;
1.523 albertel 8313: }
1.795 www 8314:
1.779 bisitz 8315: .LC_answer_no_message {
1.777 tempelho 8316: background: #FFFFFF;
8317: color: black;
1.795 www 8318: padding: 6px;
1.779 bisitz 8319: }
1.795 www 8320:
1.1334 raeburn 8321: .LC_answer_unknown,
8322: .LC_answer_warning {
1.779 bisitz 8323: background: orange;
8324: color: black;
1.795 www 8325: padding: 6px;
1.777 tempelho 8326: }
1.795 www 8327:
1.1467 raeburn 8328: span.LC_prob_status {
8329: margin: 5px 0 0 0;
8330: padding: 0 5px 0 0;
8331: vertical-align: middle;
8332: }
8333:
8334: div.LC_prob_status_outer {
8335: display: inline-block;
8336: margin: -5px 0 0 0;
8337: padding: 0;
8338: }
8339:
8340: div.LC_prob_status_inner {
8341: display: inline-block;
8342: margin: 0 5px 0 0;
8343: padding: 5px;
1.1446 raeburn 8344: }
8345:
1.1468 raeburn 8346: caption.LC_filesub_status {
8347: text-align: left;
8348: font-weight: bold;
8349: }
8350:
1.1448 raeburn 8351: .LC_mail_actions {
8352: float: left;
8353: padding: 0;
8354: margin: 6px;
8355: }
8356:
8357: .LC_vertical_line {
8358: width: 1px;
8359: background-color: black;
8360: height: 4em;
8361: float: left;
8362: margin: 0;
8363: padding: 0;
8364: }
8365:
1.529 albertel 8366: span.LC_prior_numerical,
8367: span.LC_prior_string,
8368: span.LC_prior_custom,
8369: span.LC_prior_reaction,
8370: span.LC_prior_math {
1.925 bisitz 8371: font-family: $mono;
1.523 albertel 8372: white-space: pre;
8373: }
8374:
1.525 albertel 8375: span.LC_prior_string {
1.925 bisitz 8376: font-family: $mono;
1.525 albertel 8377: white-space: pre;
8378: }
8379:
1.523 albertel 8380: table.LC_prior_option {
8381: width: 100%;
8382: border-collapse: collapse;
8383: }
1.795 www 8384:
1.911 bisitz 8385: table.LC_prior_rank,
1.795 www 8386: table.LC_prior_match {
1.528 albertel 8387: border-collapse: collapse;
8388: }
1.795 www 8389:
1.528 albertel 8390: table.LC_prior_option tr td,
8391: table.LC_prior_rank tr td,
8392: table.LC_prior_match tr td {
1.524 albertel 8393: border: 1px solid #000000;
1.515 albertel 8394: }
8395:
1.855 bisitz 8396: .LC_nobreak {
1.544 albertel 8397: white-space: nowrap;
1.519 raeburn 8398: }
8399:
1.576 raeburn 8400: span.LC_cusr_emph {
8401: font-style: italic;
8402: }
8403:
1.633 raeburn 8404: span.LC_cusr_subheading {
8405: font-weight: normal;
8406: font-size: 85%;
8407: }
8408:
1.861 bisitz 8409: div.LC_docs_entry_move {
1.859 bisitz 8410: border: 1px solid #BBBBBB;
1.545 albertel 8411: background: #DDDDDD;
1.861 bisitz 8412: width: 22px;
1.859 bisitz 8413: padding: 1px;
8414: margin: 0;
1.545 albertel 8415: }
8416:
1.861 bisitz 8417: table.LC_data_table tr > td.LC_docs_entry_commands,
8418: table.LC_data_table tr > td.LC_docs_entry_parameter {
1.545 albertel 8419: font-size: x-small;
8420: }
1.795 www 8421:
1.861 bisitz 8422: .LC_docs_entry_parameter {
8423: white-space: nowrap;
8424: }
8425:
1.544 albertel 8426: .LC_docs_copy {
1.545 albertel 8427: color: #000099;
1.544 albertel 8428: }
1.795 www 8429:
1.544 albertel 8430: .LC_docs_cut {
1.545 albertel 8431: color: #550044;
1.544 albertel 8432: }
1.795 www 8433:
1.544 albertel 8434: .LC_docs_rename {
1.545 albertel 8435: color: #009900;
1.544 albertel 8436: }
1.795 www 8437:
1.544 albertel 8438: .LC_docs_remove {
1.545 albertel 8439: color: #990000;
8440: }
8441:
1.1284 raeburn 8442: .LC_docs_alias {
8443: color: #440055;
8444: }
8445:
1.1286 raeburn 8446: .LC_domprefs_email,
1.1284 raeburn 8447: .LC_docs_alias_name,
1.547 albertel 8448: .LC_docs_reinit_warn,
8449: .LC_docs_ext_edit {
8450: font-size: x-small;
8451: }
8452:
1.545 albertel 8453: table.LC_docs_adddocs td,
8454: table.LC_docs_adddocs th {
8455: border: 1px solid #BBBBBB;
8456: padding: 4px;
8457: background: #DDDDDD;
1.543 albertel 8458: }
8459:
1.584 albertel 8460: table.LC_sty_begin {
8461: background: #BBFFBB;
8462: }
1.795 www 8463:
1.584 albertel 8464: table.LC_sty_end {
8465: background: #FFBBBB;
8466: }
8467:
1.589 raeburn 8468: table.LC_double_column {
1.803 bisitz 8469: border-width: 0;
1.589 raeburn 8470: border-collapse: collapse;
8471: width: 100%;
8472: padding: 2px;
8473: }
8474:
8475: table.LC_double_column tr td.LC_left_col {
1.590 raeburn 8476: top: 2px;
1.589 raeburn 8477: left: 2px;
8478: width: 47%;
8479: vertical-align: top;
8480: }
8481:
8482: table.LC_double_column tr td.LC_right_col {
8483: top: 2px;
1.779 bisitz 8484: right: 2px;
1.589 raeburn 8485: width: 47%;
8486: vertical-align: top;
8487: }
8488:
1.591 raeburn 8489: div.LC_left_float {
8490: float: left;
8491: padding-right: 5%;
1.597 albertel 8492: padding-bottom: 4px;
1.591 raeburn 8493: }
8494:
8495: div.LC_clear_float_header {
1.597 albertel 8496: padding-bottom: 2px;
1.591 raeburn 8497: }
8498:
8499: div.LC_clear_float_footer {
1.597 albertel 8500: padding-top: 10px;
1.591 raeburn 8501: clear: both;
8502: }
8503:
1.597 albertel 8504: div.LC_grade_show_user {
1.941 bisitz 8505: /* border-left: 5px solid $sidebg; */
8506: border-top: 5px solid #000000;
8507: margin: 50px 0 0 0;
1.936 bisitz 8508: padding: 15px 0 5px 10px;
1.597 albertel 8509: }
1.795 www 8510:
1.936 bisitz 8511: div.LC_grade_show_user_odd_row {
1.941 bisitz 8512: /* border-left: 5px solid #000000; */
8513: }
8514:
8515: div.LC_grade_show_user div.LC_Box {
8516: margin-right: 50px;
1.597 albertel 8517: }
8518:
1.1461 raeburn 8519: div.LC_grade_show_user div.LC_Box table tr th {
8520: font-weight: normal;
8521: }
8522:
1.597 albertel 8523: div.LC_grade_submissions,
8524: div.LC_grade_message_center,
1.936 bisitz 8525: div.LC_grade_info_links {
1.597 albertel 8526: margin: 5px;
8527: width: 99%;
8528: background: #FFFFFF;
8529: }
1.795 www 8530:
1.597 albertel 8531: div.LC_grade_submissions_header,
1.936 bisitz 8532: div.LC_grade_message_center_header {
1.705 tempelho 8533: font-weight: bold;
8534: font-size: large;
1.597 albertel 8535: }
1.795 www 8536:
1.597 albertel 8537: div.LC_grade_submissions_body,
1.936 bisitz 8538: div.LC_grade_message_center_body {
1.597 albertel 8539: border: 1px solid black;
8540: width: 99%;
8541: background: #FFFFFF;
8542: }
1.795 www 8543:
1.613 albertel 8544: table.LC_scantron_action {
8545: width: 100%;
8546: }
1.795 www 8547:
1.613 albertel 8548: table.LC_scantron_action tr th {
1.698 harmsja 8549: font-weight:bold;
8550: font-style:normal;
1.613 albertel 8551: }
1.795 www 8552:
1.1461 raeburn 8553: div.LC_edit_problem_daxe_header {
8554: padding: 3px;
8555: background: $tabbg;
8556: z-index: 100;
8557: }
8558:
1.779 bisitz 8559: .LC_edit_problem_header,
1.614 albertel 8560: div.LC_edit_problem_footer {
1.705 tempelho 8561: font-weight: normal;
8562: font-size: medium;
1.602 albertel 8563: margin: 2px;
1.1060 bisitz 8564: background-color: $sidebg;
1.600 albertel 8565: }
1.795 www 8566:
1.600 albertel 8567: div.LC_edit_problem_header,
1.602 albertel 8568: div.LC_edit_problem_header div,
1.614 albertel 8569: div.LC_edit_problem_footer,
8570: div.LC_edit_problem_footer div,
1.602 albertel 8571: div.LC_edit_problem_editxml_header,
8572: div.LC_edit_problem_editxml_header div {
1.1205 golterma 8573: z-index: 100;
1.600 albertel 8574: }
1.795 www 8575:
1.600 albertel 8576: div.LC_edit_problem_header_title {
1.705 tempelho 8577: font-weight: bold;
8578: font-size: larger;
1.602 albertel 8579: background: $tabbg;
8580: padding: 3px;
1.1060 bisitz 8581: margin: 0 0 5px 0;
1.602 albertel 8582: }
1.795 www 8583:
1.602 albertel 8584: table.LC_edit_problem_header_title {
8585: width: 100%;
1.600 albertel 8586: background: $tabbg;
1.602 albertel 8587: }
8588:
1.1205 golterma 8589: div.LC_edit_actionbar {
8590: background-color: $sidebg;
1.1218 droeschl 8591: margin: 0;
8592: padding: 0;
8593: line-height: 200%;
1.602 albertel 8594: }
1.795 www 8595:
1.1218 droeschl 8596: div.LC_edit_actionbar div{
8597: padding: 0;
8598: margin: 0;
8599: display: inline-block;
1.600 albertel 8600: }
1.795 www 8601:
1.1124 bisitz 8602: .LC_edit_opt {
8603: padding-left: 1em;
8604: white-space: nowrap;
8605: }
8606:
1.1152 golterma 8607: .LC_edit_problem_latexhelper{
8608: text-align: right;
8609: }
8610:
8611: #LC_edit_problem_colorful div{
8612: margin-left: 40px;
8613: }
8614:
1.1205 golterma 8615: #LC_edit_problem_codemirror div{
8616: margin-left: 0px;
8617: }
8618:
1.911 bisitz 8619: img.stift {
1.803 bisitz 8620: border-width: 0;
8621: vertical-align: middle;
1.677 riegler 8622: }
1.680 riegler 8623:
1.1460 raeburn 8624: div.LC_mainmenu {
8625: margin: 3px 2px 2px 1px;
8626: float: left;
1.777 tempelho 8627: }
1.795 www 8628:
1.716 raeburn 8629: div.LC_createcourse {
1.911 bisitz 8630: margin: 10px 10px 10px 10px;
1.716 raeburn 8631: }
8632:
1.917 raeburn 8633: .LC_dccid {
1.1130 raeburn 8634: float: right;
1.917 raeburn 8635: margin: 0.2em 0 0 0;
8636: padding: 0;
8637: font-size: 90%;
8638: display:none;
8639: }
8640:
1.897 wenzelju 8641: ol.LC_primary_menu a:hover,
1.721 harmsja 8642: ol#LC_MenuBreadcrumbs a:hover,
8643: ol#LC_PathBreadcrumbs a:hover,
1.897 wenzelju 8644: ul#LC_secondary_menu a:hover,
1.721 harmsja 8645: .LC_FormSectionClearButton input:hover
1.795 www 8646: ul.LC_TabContent li:hover a {
1.952 onken 8647: color:$button_hover;
1.911 bisitz 8648: text-decoration:none;
1.693 droeschl 8649: }
8650:
1.779 bisitz 8651: h1 {
1.911 bisitz 8652: padding: 0;
8653: line-height:130%;
1.693 droeschl 8654: }
1.698 harmsja 8655:
1.911 bisitz 8656: h2,
8657: h3,
8658: h4,
8659: h5,
8660: h6 {
8661: margin: 5px 0 5px 0;
8662: padding: 0;
8663: line-height:130%;
1.693 droeschl 8664: }
1.795 www 8665:
8666: .LC_hcell {
1.911 bisitz 8667: padding:3px 15px 3px 15px;
8668: margin: 0;
8669: background-color:$tabbg;
8670: color:$fontmenu;
8671: border-bottom:solid 1px $lg_border_color;
1.693 droeschl 8672: }
1.795 www 8673:
1.840 bisitz 8674: .LC_Box > .LC_hcell {
1.911 bisitz 8675: margin: 0 -10px 10px -10px;
1.835 bisitz 8676: }
8677:
1.721 harmsja 8678: .LC_noBorder {
1.911 bisitz 8679: border: 0;
1.698 harmsja 8680: }
1.693 droeschl 8681:
1.721 harmsja 8682: .LC_FormSectionClearButton input {
1.911 bisitz 8683: background-color:transparent;
8684: border: none;
8685: cursor:pointer;
8686: text-decoration:underline;
1.693 droeschl 8687: }
1.763 bisitz 8688:
8689: .LC_help_open_topic {
1.911 bisitz 8690: color: #FFFFFF;
8691: background-color: #EEEEFF;
8692: margin: 1px;
8693: padding: 4px;
8694: border: 1px solid #000033;
8695: white-space: nowrap;
8696: /* vertical-align: middle; */
1.759 neumanie 8697: }
1.693 droeschl 8698:
1.911 bisitz 8699: dl,
8700: ul,
8701: div,
8702: fieldset {
8703: margin: 10px 10px 10px 0;
8704: /* overflow: hidden; */
1.693 droeschl 8705: }
1.795 www 8706:
1.1404 raeburn 8707: fieldset#LC_selectuser {
1.1460 raeburn 8708: margin: -1px 0 0 0;
8709: padding: 0;
8710: border: 0;
1.1404 raeburn 8711: }
8712:
1.1211 raeburn 8713: article.geogebraweb div {
8714: margin: 0;
8715: }
8716:
1.838 bisitz 8717: fieldset > legend {
1.911 bisitz 8718: font-weight: bold;
8719: padding: 0 5px 0 5px;
1.838 bisitz 8720: }
8721:
1.813 bisitz 8722: #LC_nav_bar {
1.911 bisitz 8723: float: left;
1.995 raeburn 8724: background-color: $pgbg_or_bgcolor;
1.966 bisitz 8725: margin: 0 0 2px 0;
1.807 droeschl 8726: }
8727:
1.916 droeschl 8728: #LC_realm {
8729: margin: 0.2em 0 0 0;
8730: padding: 0;
8731: font-weight: bold;
8732: text-align: center;
1.995 raeburn 8733: background-color: $pgbg_or_bgcolor;
1.916 droeschl 8734: }
8735:
1.911 bisitz 8736: #LC_nav_bar em {
8737: font-weight: bold;
8738: font-style: normal;
1.807 droeschl 8739: }
8740:
1.897 wenzelju 8741: ol.LC_primary_menu {
1.934 droeschl 8742: margin: 0;
1.1076 raeburn 8743: padding: 0;
1.807 droeschl 8744: }
8745:
1.852 droeschl 8746: ol#LC_PathBreadcrumbs {
1.911 bisitz 8747: margin: 0;
1.693 droeschl 8748: }
8749:
1.897 wenzelju 8750: ol.LC_primary_menu li {
1.1076 raeburn 8751: color: RGB(80, 80, 80);
8752: vertical-align: middle;
8753: text-align: left;
8754: list-style: none;
1.1205 golterma 8755: position: relative;
1.1076 raeburn 8756: float: left;
1.1205 golterma 8757: z-index: 100; /* will be displayed above codemirror and underneath the help-layer */
8758: line-height: 1.5em;
1.1076 raeburn 8759: }
8760:
1.1205 golterma 8761: ol.LC_primary_menu li a,
8762: ol.LC_primary_menu li p {
1.1076 raeburn 8763: display: block;
8764: margin: 0;
8765: padding: 0 5px 0 10px;
8766: text-decoration: none;
8767: }
8768:
1.1205 golterma 8769: ol.LC_primary_menu li p span.LC_primary_menu_innertitle {
8770: display: inline-block;
8771: width: 95%;
8772: text-align: left;
8773: }
8774:
8775: ol.LC_primary_menu li p span.LC_primary_menu_innerarrow {
8776: display: inline-block;
8777: width: 5%;
8778: float: right;
8779: text-align: right;
8780: font-size: 70%;
8781: }
8782:
8783: ol.LC_primary_menu ul {
1.1076 raeburn 8784: display: none;
1.1205 golterma 8785: width: 15em;
1.1076 raeburn 8786: background-color: $data_table_light;
1.1205 golterma 8787: position: absolute;
8788: top: 100%;
1.1076 raeburn 8789: }
8790:
1.1205 golterma 8791: ol.LC_primary_menu ul ul {
8792: left: 100%;
8793: top: 0;
8794: }
8795:
8796: ol.LC_primary_menu li:hover > ul, ol.LC_primary_menu li.hover > ul {
1.1076 raeburn 8797: display: block;
8798: position: absolute;
8799: margin: 0;
8800: padding: 0;
1.1078 raeburn 8801: z-index: 2;
1.1076 raeburn 8802: }
8803:
8804: ol.LC_primary_menu li:hover li, ol.LC_primary_menu li.hover li {
1.1205 golterma 8805: /* First Submenu -> size should be smaller than the menu title of the whole menu */
1.1076 raeburn 8806: font-size: 90%;
1.911 bisitz 8807: vertical-align: top;
1.1076 raeburn 8808: float: none;
1.1079 raeburn 8809: border-left: 1px solid black;
8810: border-right: 1px solid black;
1.1205 golterma 8811: /* A dark bottom border to visualize different menu options;
8812: overwritten in the create_submenu routine for the last border-bottom of the menu */
8813: border-bottom: 1px solid $data_table_dark;
1.1076 raeburn 8814: }
8815:
1.1205 golterma 8816: ol.LC_primary_menu li li p:hover {
8817: color:$button_hover;
8818: text-decoration:none;
8819: background-color:$data_table_dark;
1.1076 raeburn 8820: }
8821:
8822: ol.LC_primary_menu li li a:hover {
8823: color:$button_hover;
8824: background-color:$data_table_dark;
1.693 droeschl 8825: }
8826:
1.1205 golterma 8827: /* Font-size equal to the size of the predecessors*/
8828: ol.LC_primary_menu li:hover li li {
8829: font-size: 100%;
8830: }
8831:
1.897 wenzelju 8832: ol.LC_primary_menu li img {
1.911 bisitz 8833: vertical-align: bottom;
1.934 droeschl 8834: height: 1.1em;
1.1077 raeburn 8835: margin: 0.2em 0 0 0;
1.693 droeschl 8836: }
8837:
1.897 wenzelju 8838: ol.LC_primary_menu a {
1.911 bisitz 8839: color: RGB(80, 80, 80);
8840: text-decoration: none;
1.693 droeschl 8841: }
1.795 www 8842:
1.949 droeschl 8843: ol.LC_primary_menu a.LC_new_message {
8844: font-weight:bold;
8845: color: darkred;
8846: }
8847:
1.975 raeburn 8848: ol.LC_docs_parameters {
8849: margin-left: 0;
8850: padding: 0;
8851: list-style: none;
8852: }
8853:
8854: ol.LC_docs_parameters li {
8855: margin: 0;
8856: padding-right: 20px;
8857: display: inline;
8858: }
8859:
1.976 raeburn 8860: ol.LC_docs_parameters li:before {
8861: content: "\\002022 \\0020";
8862: }
8863:
8864: li.LC_docs_parameters_title {
8865: font-weight: bold;
8866: }
8867:
8868: ol.LC_docs_parameters li.LC_docs_parameters_title:before {
8869: content: "";
8870: }
8871:
1.897 wenzelju 8872: ul#LC_secondary_menu {
1.1107 raeburn 8873: clear: right;
1.911 bisitz 8874: color: $fontmenu;
8875: background: $tabbg;
8876: list-style: none;
8877: padding: 0;
8878: margin: 0;
8879: width: 100%;
1.995 raeburn 8880: text-align: left;
1.1107 raeburn 8881: float: left;
1.808 droeschl 8882: }
8883:
1.897 wenzelju 8884: ul#LC_secondary_menu li {
1.911 bisitz 8885: font-weight: bold;
8886: line-height: 1.8em;
1.1107 raeburn 8887: border-right: 1px solid black;
8888: float: left;
8889: }
8890:
8891: ul#LC_secondary_menu li.LC_hoverable:hover, ul#LC_secondary_menu li.hover {
8892: background-color: $data_table_light;
8893: }
8894:
8895: ul#LC_secondary_menu li a {
1.911 bisitz 8896: padding: 0 0.8em;
1.1107 raeburn 8897: }
8898:
8899: ul#LC_secondary_menu li ul {
8900: display: none;
8901: }
8902:
8903: ul#LC_secondary_menu li:hover ul, ul#LC_secondary_menu li.hover ul {
8904: display: block;
8905: position: absolute;
8906: margin: 0;
8907: padding: 0;
8908: list-style:none;
8909: float: none;
8910: background-color: $data_table_light;
8911: z-index: 2;
8912: margin-left: -1px;
8913: }
8914:
8915: ul#LC_secondary_menu li ul li {
8916: font-size: 90%;
8917: vertical-align: top;
8918: border-left: 1px solid black;
1.911 bisitz 8919: border-right: 1px solid black;
1.1119 raeburn 8920: background-color: $data_table_light;
1.1107 raeburn 8921: list-style:none;
8922: float: none;
8923: }
8924:
8925: ul#LC_secondary_menu li ul li:hover, ul#LC_secondary_menu li ul li.hover {
8926: background-color: $data_table_dark;
1.807 droeschl 8927: }
8928:
1.847 tempelho 8929: ul.LC_TabContent {
1.911 bisitz 8930: display:block;
8931: background: $sidebg;
8932: border-bottom: solid 1px $lg_border_color;
8933: list-style:none;
1.1020 raeburn 8934: margin: -1px -10px 0 -10px;
1.911 bisitz 8935: padding: 0;
1.693 droeschl 8936: }
8937:
1.795 www 8938: ul.LC_TabContent li,
8939: ul.LC_TabContentBigger li {
1.911 bisitz 8940: float:left;
1.741 harmsja 8941: }
1.795 www 8942:
1.897 wenzelju 8943: ul#LC_secondary_menu li a {
1.911 bisitz 8944: color: $fontmenu;
8945: text-decoration: none;
1.693 droeschl 8946: }
1.795 www 8947:
1.721 harmsja 8948: ul.LC_TabContent {
1.952 onken 8949: min-height:20px;
1.721 harmsja 8950: }
1.795 www 8951:
8952: ul.LC_TabContent li {
1.911 bisitz 8953: vertical-align:middle;
1.959 onken 8954: padding: 0 16px 0 10px;
1.911 bisitz 8955: background-color:$tabbg;
8956: border-bottom:solid 1px $lg_border_color;
1.1020 raeburn 8957: border-left: solid 1px $font;
1.721 harmsja 8958: }
1.795 www 8959:
1.847 tempelho 8960: ul.LC_TabContent .right {
1.911 bisitz 8961: float:right;
1.847 tempelho 8962: }
8963:
1.911 bisitz 8964: ul.LC_TabContent li a,
8965: ul.LC_TabContent li {
8966: color:rgb(47,47,47);
8967: text-decoration:none;
8968: font-size:95%;
8969: font-weight:bold;
1.952 onken 8970: min-height:20px;
8971: }
8972:
1.959 onken 8973: ul.LC_TabContent li a:hover,
8974: ul.LC_TabContent li a:focus {
1.952 onken 8975: color: $button_hover;
1.959 onken 8976: background:none;
8977: outline:none;
1.952 onken 8978: }
8979:
8980: ul.LC_TabContent li:hover {
8981: color: $button_hover;
8982: cursor:pointer;
1.721 harmsja 8983: }
1.795 www 8984:
1.911 bisitz 8985: ul.LC_TabContent li.active {
1.952 onken 8986: color: $font;
1.911 bisitz 8987: background:#FFFFFF url(/adm/lonIcons/open.gif) no-repeat scroll right center;
1.952 onken 8988: border-bottom:solid 1px #FFFFFF;
8989: cursor: default;
1.744 ehlerst 8990: }
1.795 www 8991:
1.959 onken 8992: ul.LC_TabContent li.active a {
8993: color:$font;
8994: background:#FFFFFF;
8995: outline: none;
8996: }
1.1047 raeburn 8997:
8998: ul.LC_TabContent li.goback {
8999: float: left;
9000: border-left: none;
9001: }
9002:
1.870 tempelho 9003: #maincoursedoc {
1.911 bisitz 9004: clear:both;
1.870 tempelho 9005: }
9006:
9007: ul.LC_TabContentBigger {
1.911 bisitz 9008: display:block;
9009: list-style:none;
9010: padding: 0;
1.870 tempelho 9011: }
9012:
1.795 www 9013: ul.LC_TabContentBigger li {
1.911 bisitz 9014: vertical-align:bottom;
9015: height: 30px;
9016: font-size:110%;
9017: font-weight:bold;
9018: color: #737373;
1.841 tempelho 9019: }
9020:
1.957 onken 9021: ul.LC_TabContentBigger li.active {
9022: position: relative;
9023: top: 1px;
9024: }
9025:
1.870 tempelho 9026: ul.LC_TabContentBigger li a {
1.911 bisitz 9027: background:url('/adm/lonIcons/tabbgleft.gif') left bottom no-repeat;
9028: height: 30px;
9029: line-height: 30px;
9030: text-align: center;
9031: display: block;
9032: text-decoration: none;
1.958 onken 9033: outline: none;
1.741 harmsja 9034: }
1.795 www 9035:
1.870 tempelho 9036: ul.LC_TabContentBigger li.active a {
1.911 bisitz 9037: background:url('/adm/lonIcons/tabbgleft.gif') left top no-repeat;
9038: color:$font;
1.744 ehlerst 9039: }
1.795 www 9040:
1.870 tempelho 9041: ul.LC_TabContentBigger li b {
1.911 bisitz 9042: background: url('/adm/lonIcons/tabbgright.gif') no-repeat right bottom;
9043: display: block;
9044: float: left;
9045: padding: 0 30px;
1.957 onken 9046: border-bottom: 1px solid $lg_border_color;
1.870 tempelho 9047: }
9048:
1.956 onken 9049: ul.LC_TabContentBigger li:hover b {
9050: color:$button_hover;
9051: }
9052:
1.870 tempelho 9053: ul.LC_TabContentBigger li.active b {
1.911 bisitz 9054: background:url('/adm/lonIcons/tabbgright.gif') right top no-repeat;
9055: color:$font;
1.957 onken 9056: border: 0;
1.741 harmsja 9057: }
1.693 droeschl 9058:
1.870 tempelho 9059:
1.862 bisitz 9060: ul.LC_CourseBreadcrumbs {
9061: background: $sidebg;
1.1020 raeburn 9062: height: 2em;
1.862 bisitz 9063: padding-left: 10px;
1.1020 raeburn 9064: margin: 0;
1.862 bisitz 9065: list-style-position: inside;
9066: }
9067:
1.911 bisitz 9068: ol#LC_MenuBreadcrumbs,
1.862 bisitz 9069: ol#LC_PathBreadcrumbs {
1.911 bisitz 9070: padding-left: 10px;
9071: margin: 0;
1.933 droeschl 9072: height: 2.5em; /* equal to #LC_breadcrumbs line-height */
1.693 droeschl 9073: }
9074:
1.911 bisitz 9075: ol#LC_MenuBreadcrumbs li,
9076: ol#LC_PathBreadcrumbs li,
1.862 bisitz 9077: ul.LC_CourseBreadcrumbs li {
1.911 bisitz 9078: display: inline;
1.933 droeschl 9079: white-space: normal;
1.693 droeschl 9080: }
9081:
1.823 bisitz 9082: ol#LC_MenuBreadcrumbs li a,
1.862 bisitz 9083: ul.LC_CourseBreadcrumbs li a {
1.911 bisitz 9084: text-decoration: none;
9085: font-size:90%;
1.693 droeschl 9086: }
1.795 www 9087:
1.969 droeschl 9088: ol#LC_MenuBreadcrumbs h1 {
9089: display: inline;
9090: font-size: 90%;
9091: line-height: 2.5em;
9092: margin: 0;
9093: padding: 0;
9094: }
9095:
1.795 www 9096: ol#LC_PathBreadcrumbs li a {
1.911 bisitz 9097: text-decoration:none;
9098: font-size:100%;
9099: font-weight:bold;
1.693 droeschl 9100: }
1.795 www 9101:
1.840 bisitz 9102: .LC_Box {
1.911 bisitz 9103: border: solid 1px $lg_border_color;
9104: padding: 0 10px 10px 10px;
1.746 neumanie 9105: }
1.795 www 9106:
1.1020 raeburn 9107: .LC_DocsBox {
9108: border: solid 1px $lg_border_color;
9109: padding: 0 0 10px 10px;
9110: }
9111:
1.795 www 9112: .LC_AboutMe_Image {
1.911 bisitz 9113: float:left;
9114: margin-right:10px;
1.747 neumanie 9115: }
1.795 www 9116:
9117: .LC_Clear_AboutMe_Image {
1.911 bisitz 9118: clear:left;
1.747 neumanie 9119: }
1.795 www 9120:
1.721 harmsja 9121: dl.LC_ListStyleClean dt {
1.911 bisitz 9122: padding-right: 5px;
9123: display: table-header-group;
1.693 droeschl 9124: }
9125:
1.721 harmsja 9126: dl.LC_ListStyleClean dd {
1.911 bisitz 9127: display: table-row;
1.693 droeschl 9128: }
9129:
1.721 harmsja 9130: .LC_ListStyleClean,
9131: .LC_ListStyleSimple,
9132: .LC_ListStyleNormal,
1.795 www 9133: .LC_ListStyleSpecial {
1.911 bisitz 9134: /* display:block; */
9135: list-style-position: inside;
9136: list-style-type: none;
9137: overflow: hidden;
9138: padding: 0;
1.693 droeschl 9139: }
9140:
1.721 harmsja 9141: .LC_ListStyleSimple li,
9142: .LC_ListStyleSimple dd,
9143: .LC_ListStyleNormal li,
9144: .LC_ListStyleNormal dd,
9145: .LC_ListStyleSpecial li,
1.795 www 9146: .LC_ListStyleSpecial dd {
1.911 bisitz 9147: margin: 0;
9148: padding: 5px 5px 5px 10px;
9149: clear: both;
1.693 droeschl 9150: }
9151:
1.721 harmsja 9152: .LC_ListStyleClean li,
9153: .LC_ListStyleClean dd {
1.911 bisitz 9154: padding-top: 0;
9155: padding-bottom: 0;
1.693 droeschl 9156: }
9157:
1.721 harmsja 9158: .LC_ListStyleSimple dd,
1.795 www 9159: .LC_ListStyleSimple li {
1.911 bisitz 9160: border-bottom: solid 1px $lg_border_color;
1.693 droeschl 9161: }
9162:
1.721 harmsja 9163: .LC_ListStyleSpecial li,
9164: .LC_ListStyleSpecial dd {
1.911 bisitz 9165: list-style-type: none;
9166: background-color: RGB(220, 220, 220);
9167: margin-bottom: 4px;
1.693 droeschl 9168: }
9169:
1.721 harmsja 9170: table.LC_SimpleTable {
1.911 bisitz 9171: margin:5px;
9172: border:solid 1px $lg_border_color;
1.795 www 9173: }
1.693 droeschl 9174:
1.721 harmsja 9175: table.LC_SimpleTable tr {
1.911 bisitz 9176: padding: 0;
9177: border:solid 1px $lg_border_color;
1.693 droeschl 9178: }
1.795 www 9179:
9180: table.LC_SimpleTable thead {
1.911 bisitz 9181: background:rgb(220,220,220);
1.693 droeschl 9182: }
9183:
1.721 harmsja 9184: div.LC_columnSection {
1.911 bisitz 9185: display: block;
9186: clear: both;
9187: overflow: hidden;
9188: margin: 0;
1.693 droeschl 9189: }
9190:
1.721 harmsja 9191: div.LC_columnSection>* {
1.911 bisitz 9192: float: left;
9193: margin: 10px 20px 10px 0;
9194: overflow:hidden;
1.693 droeschl 9195: }
1.721 harmsja 9196:
1.795 www 9197: table em {
1.911 bisitz 9198: font-weight: bold;
9199: font-style: normal;
1.748 schulted 9200: }
1.795 www 9201:
1.779 bisitz 9202: table.LC_tableBrowseRes,
1.795 www 9203: table.LC_tableOfContent {
1.911 bisitz 9204: border:none;
9205: border-spacing: 1px;
9206: padding: 3px;
9207: background-color: #FFFFFF;
9208: font-size: 90%;
1.753 droeschl 9209: }
1.789 droeschl 9210:
1.911 bisitz 9211: table.LC_tableOfContent {
9212: border-collapse: collapse;
1.789 droeschl 9213: }
9214:
1.771 droeschl 9215: table.LC_tableBrowseRes a,
1.768 schulted 9216: table.LC_tableOfContent a {
1.911 bisitz 9217: background-color: transparent;
9218: text-decoration: none;
1.753 droeschl 9219: }
9220:
1.795 www 9221: table.LC_tableOfContent img {
1.911 bisitz 9222: border: none;
9223: height: 1.3em;
9224: vertical-align: text-bottom;
9225: margin-right: 0.3em;
1.753 droeschl 9226: }
1.757 schulted 9227:
1.795 www 9228: a#LC_content_toolbar_firsthomework {
1.911 bisitz 9229: background-image:url(/res/adm/pages/open-first-problem.gif);
1.774 ehlerst 9230: }
9231:
1.795 www 9232: a#LC_content_toolbar_everything {
1.911 bisitz 9233: background-image:url(/res/adm/pages/show-all.gif);
1.774 ehlerst 9234: }
9235:
1.795 www 9236: a#LC_content_toolbar_uncompleted {
1.911 bisitz 9237: background-image:url(/res/adm/pages/show-incomplete-problems.gif);
1.774 ehlerst 9238: }
9239:
1.795 www 9240: #LC_content_toolbar_clearbubbles {
1.911 bisitz 9241: background-image:url(/res/adm/pages/mark-discussionentries-read.gif);
1.774 ehlerst 9242: }
9243:
1.795 www 9244: a#LC_content_toolbar_changefolder {
1.911 bisitz 9245: background : url(/res/adm/pages/close-all-folders.gif) top center ;
1.757 schulted 9246: }
9247:
1.795 www 9248: a#LC_content_toolbar_changefolder_toggled {
1.911 bisitz 9249: background-image:url(/res/adm/pages/open-all-folders.gif);
1.757 schulted 9250: }
9251:
1.1043 raeburn 9252: a#LC_content_toolbar_edittoplevel {
9253: background-image:url(/res/adm/pages/edittoplevel.gif);
9254: }
9255:
1.1384 raeburn 9256: a#LC_content_toolbar_printout {
9257: background-image:url(/res/adm/pages/printout.gif);
9258: }
9259:
1.795 www 9260: ul#LC_toolbar li a:hover {
1.911 bisitz 9261: background-position: bottom center;
1.757 schulted 9262: }
9263:
1.795 www 9264: ul#LC_toolbar {
1.911 bisitz 9265: padding: 0;
9266: margin: 2px;
9267: list-style:none;
1.1449 raeburn 9268: display:inline;
1.911 bisitz 9269: background-color:white;
1.1082 raeburn 9270: overflow: auto;
1.757 schulted 9271: }
9272:
1.795 www 9273: ul#LC_toolbar li {
1.911 bisitz 9274: border:1px solid white;
9275: padding: 0;
9276: margin: 0;
9277: float: left;
9278: display:inline;
9279: vertical-align:middle;
1.1082 raeburn 9280: white-space: nowrap;
1.911 bisitz 9281: }
1.757 schulted 9282:
1.783 amueller 9283:
1.795 www 9284: a.LC_toolbarItem {
1.911 bisitz 9285: display:block;
9286: padding: 0;
9287: margin: 0;
9288: height: 32px;
9289: width: 32px;
9290: color:white;
9291: border: none;
9292: background-repeat:no-repeat;
9293: background-color:transparent;
1.757 schulted 9294: }
9295:
1.1449 raeburn 9296: .LC_navtools {
9297: display: inline-block;
9298: padding: 0;
9299: margin: 2px;
9300: vertical-align: middle;
9301: }
9302:
1.915 droeschl 9303: ul.LC_funclist {
9304: margin: 0;
9305: padding: 0.5em 1em 0.5em 0;
9306: }
9307:
1.933 droeschl 9308: ul.LC_funclist > li:first-child {
9309: font-weight:bold;
9310: margin-left:0.8em;
9311: }
9312:
1.915 droeschl 9313: ul.LC_funclist + ul.LC_funclist {
9314: /*
9315: left border as a seperator if we have more than
9316: one list
9317: */
9318: border-left: 1px solid $sidebg;
9319: /*
9320: this hides the left border behind the border of the
9321: outer box if element is wrapped to the next 'line'
9322: */
9323: margin-left: -1px;
9324: }
9325:
1.843 bisitz 9326: ul.LC_funclist li {
1.915 droeschl 9327: display: inline;
1.782 bisitz 9328: white-space: nowrap;
1.915 droeschl 9329: margin: 0 0 0 25px;
9330: line-height: 150%;
1.782 bisitz 9331: }
9332:
1.974 wenzelju 9333: .LC_hidden {
9334: display: none;
9335: }
9336:
1.1030 www 9337: .LCmodal-overlay {
9338: position:fixed;
9339: top:0;
9340: right:0;
9341: bottom:0;
9342: left:0;
9343: height:100%;
9344: width:100%;
9345: margin:0;
9346: padding:0;
9347: background:#999;
9348: opacity:.75;
9349: filter: alpha(opacity=75);
9350: -moz-opacity: 0.75;
9351: z-index:101;
9352: }
9353:
9354: * html .LCmodal-overlay {
9355: position: absolute;
9356: height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px');
9357: }
9358:
9359: .LCmodal-window {
9360: position:fixed;
9361: top:50%;
9362: left:50%;
9363: margin:0;
9364: padding:0;
9365: z-index:102;
9366: }
9367:
9368: * html .LCmodal-window {
9369: position:absolute;
9370: }
9371:
9372: .LCclose-window {
9373: position:absolute;
9374: width:32px;
9375: height:32px;
9376: right:8px;
9377: top:8px;
9378: background:transparent url('/res/adm/pages/process-stop.png') no-repeat scroll right top;
9379: text-indent:-99999px;
9380: overflow:hidden;
9381: cursor:pointer;
9382: }
9383:
1.1369 raeburn 9384: .LCisDisabled {
9385: cursor: not-allowed;
9386: opacity: 0.5;
9387: }
9388:
9389: a[aria-disabled="true"] {
9390: color: currentColor;
9391: display: inline-block; /* For IE11/ MS Edge bug */
9392: pointer-events: none;
9393: text-decoration: none;
9394: }
9395:
1.1335 raeburn 9396: pre.LC_wordwrap {
9397: white-space: pre-wrap;
9398: white-space: -moz-pre-wrap;
9399: white-space: -pre-wrap;
9400: white-space: -o-pre-wrap;
9401: word-wrap: break-word;
9402: }
9403:
1.1100 raeburn 9404: /*
1.1231 damieng 9405: styles used for response display
9406: */
1.1464 raeburn 9407: div.LC_radiofoil, div.LC_rankfoil, div.LC_optionfoil, div.LC_matchfoil, div.LC_login_links {
1.1231 damieng 9408: margin: .5em 0em .5em 0em;
9409: }
9410: table.LC_itemgroup {
9411: margin-top: 1em;
9412: }
9413:
1.1462 raeburn 9414: table.LC_itemgroup tr th {
9415: font-weight: normal;
9416: }
9417:
9418: fieldset.LC_webbubbles {
9419: margin: 2px 0 0 0;
9420: padding: 0;
9421: border: 0;
9422: }
9423:
9424: ul.LC_webbubbles {
9425: list-style: none;
9426: padding: 0;
9427: margin: 0;
9428: text-align: left;
9429: float: left;
9430: }
9431:
9432: ul.LC_webbubbles li {
9433: line-height: 1.8em;
9434: border: 1px solid black;
9435: padding: 0 2px 0 5px;
9436: margin: 0 0 0 -1px;
9437: float: left;
9438: }
9439:
1.1231 damieng 9440: /*
1.1100 raeburn 9441: styles used by TTH when "Default set of options to pass to tth/m
9442: when converting TeX" in course settings has been set
9443:
9444: option passed: -t
9445:
9446: */
9447:
9448: td div.comp { margin-top: -0.6ex; margin-bottom: -1ex;}
9449: td div.comb { margin-top: -0.6ex; margin-bottom: -.6ex;}
9450: td div.hrcomp { line-height: 0.9; margin-top: -0.8ex; margin-bottom: -1ex;}
9451: td div.norm {line-height:normal;}
9452:
9453: /*
9454: option passed -y3
9455: */
9456:
9457: span.roman {font-family: serif; font-style: normal; font-weight: normal;}
9458: span.overacc2 {position: relative; left: .8em; top: -1.2ex;}
9459: span.overacc1 {position: relative; left: .6em; top: -1.2ex;}
9460:
1.1230 damieng 9461: /*
9462: sections with roles, for content only
9463: */
9464: section[class^="role-"] {
9465: padding-left: 10px;
9466: padding-right: 5px;
9467: margin-top: 8px;
9468: margin-bottom: 8px;
9469: border: 1px solid #2A4;
9470: border-radius: 5px;
9471: box-shadow: 0px 1px 1px #BBB;
9472: }
9473: section[class^="role-"]>h1 {
9474: position: relative;
9475: margin: 0px;
9476: padding-top: 10px;
9477: padding-left: 40px;
9478: }
9479: section[class^="role-"]>h1:before {
9480: position: absolute;
9481: left: -5px;
9482: top: 5px;
9483: }
9484: section.role-activity>h1:before {
9485: content:url('/adm/daxe/images/section_icons/activity.png');
9486: }
9487: section.role-advice>h1:before {
9488: content:url('/adm/daxe/images/section_icons/advice.png');
9489: }
9490: section.role-bibliography>h1:before {
9491: content:url('/adm/daxe/images/section_icons/bibliography.png');
9492: }
9493: section.role-citation>h1:before {
9494: content:url('/adm/daxe/images/section_icons/citation.png');
9495: }
9496: section.role-conclusion>h1:before {
9497: content:url('/adm/daxe/images/section_icons/conclusion.png');
9498: }
9499: section.role-definition>h1:before {
9500: content:url('/adm/daxe/images/section_icons/definition.png');
9501: }
9502: section.role-demonstration>h1:before {
9503: content:url('/adm/daxe/images/section_icons/demonstration.png');
9504: }
9505: section.role-example>h1:before {
9506: content:url('/adm/daxe/images/section_icons/example.png');
9507: }
9508: section.role-explanation>h1:before {
9509: content:url('/adm/daxe/images/section_icons/explanation.png');
9510: }
9511: section.role-introduction>h1:before {
9512: content:url('/adm/daxe/images/section_icons/introduction.png');
9513: }
9514: section.role-method>h1:before {
9515: content:url('/adm/daxe/images/section_icons/method.png');
9516: }
9517: section.role-more_information>h1:before {
9518: content:url('/adm/daxe/images/section_icons/more_information.png');
9519: }
9520: section.role-objectives>h1:before {
9521: content:url('/adm/daxe/images/section_icons/objectives.png');
9522: }
9523: section.role-prerequisites>h1:before {
9524: content:url('/adm/daxe/images/section_icons/prerequisites.png');
9525: }
9526: section.role-remark>h1:before {
9527: content:url('/adm/daxe/images/section_icons/remark.png');
9528: }
9529: section.role-reminder>h1:before {
9530: content:url('/adm/daxe/images/section_icons/reminder.png');
9531: }
9532: section.role-summary>h1:before {
9533: content:url('/adm/daxe/images/section_icons/summary.png');
9534: }
9535: section.role-syntax>h1:before {
9536: content:url('/adm/daxe/images/section_icons/syntax.png');
9537: }
9538: section.role-warning>h1:before {
9539: content:url('/adm/daxe/images/section_icons/warning.png');
9540: }
9541:
1.1269 raeburn 9542: #LC_minitab_header {
9543: float:left;
9544: width:100%;
9545: background:#DAE0D2 url("/res/adm/pages/minitabmenu_bg.gif") repeat-x bottom;
9546: font-size:93%;
9547: line-height:normal;
9548: margin: 0.5em 0 0.5em 0;
9549: }
9550: #LC_minitab_header ul {
9551: margin:0;
9552: padding:10px 10px 0;
9553: list-style:none;
9554: }
9555: #LC_minitab_header li {
9556: float:left;
9557: background:url("/res/adm/pages/minitabmenu_left.gif") no-repeat left top;
9558: margin:0;
9559: padding:0 0 0 9px;
9560: }
9561: #LC_minitab_header a {
9562: display:block;
9563: background:url("/res/adm/pages/minitabmenu_right.gif") no-repeat right top;
9564: padding:5px 15px 4px 6px;
9565: }
9566: #LC_minitab_header #LC_current_minitab {
9567: background-image:url("/res/adm/pages/minitabmenu_left_on.gif");
9568: }
9569: #LC_minitab_header #LC_current_minitab a {
9570: background-image:url("/res/adm/pages/minitabmenu_right_on.gif");
9571: padding-bottom:5px;
9572: }
9573:
9574:
1.343 albertel 9575: END
9576: }
9577:
1.306 albertel 9578: =pod
9579:
9580: =item * &headtag()
9581:
9582: Returns a uniform footer for LON-CAPA web pages.
9583:
1.307 albertel 9584: Inputs: $title - optional title for the head
9585: $head_extra - optional extra HTML to put inside the <head>
1.315 albertel 9586: $args - optional arguments
1.319 albertel 9587: force_register - if is true call registerurl so the remote is
9588: informed
1.415 albertel 9589: redirect -> array ref of
9590: 1- seconds before redirect occurs
9591: 2- url to redirect to
9592: 3- whether the side effect should occur
1.315 albertel 9593: (side effect of setting
9594: $env{'internal.head.redirect'} to the url
1.1386 raeburn 9595: redirected to)
9596: 4- whether the redirect target should be
9597: the opener of the current (pop-up)
9598: window (side effect of setting
9599: $env{'internal.head.to_opener'} to
9600: 1, if true.
1.1388 raeburn 9601: 5- whether encrypt check should be skipped
1.352 albertel 9602: domain -> force to color decorate a page for a specific
9603: domain
9604: function -> force usage of a specific rolish color scheme
9605: bgcolor -> override the default page bgcolor
1.460 albertel 9606: no_auto_mt_title
9607: -> prevent &mt()ing the title arg
1.464 albertel 9608:
1.306 albertel 9609: =cut
9610:
9611: sub headtag {
1.313 albertel 9612: my ($title,$head_extra,$args) = @_;
1.306 albertel 9613:
1.363 albertel 9614: my $function = $args->{'function'} || &get_users_function();
9615: my $domain = $args->{'domain'} || &determinedomain();
9616: my $bgcolor = $args->{'bgcolor'} || &designparm($function.'.pgbg',$domain);
1.1154 raeburn 9617: my $httphost = $args->{'use_absolute'};
1.418 albertel 9618: my $url = join(':',$env{'user.name'},$env{'user.domain'},
1.458 albertel 9619: $Apache::lonnet::perlvar{'lonVersion'},
1.531 albertel 9620: #time(),
1.418 albertel 9621: $env{'environment.color.timestamp'},
1.363 albertel 9622: $function,$domain,$bgcolor);
9623:
1.369 www 9624: $url = '/adm/css/'.&escape($url).'.css';
1.363 albertel 9625:
1.308 albertel 9626: my $result =
9627: '<head>'.
1.1160 raeburn 9628: &font_settings($args);
1.319 albertel 9629:
1.1188 raeburn 9630: my $inhibitprint;
9631: if ($args->{'print_suppress'}) {
9632: $inhibitprint = &print_suppression();
9633: }
1.1064 raeburn 9634:
1.1439 raeburn 9635: if (!$args->{'frameset'} && !$args->{'switchserver'}) {
1.461 albertel 9636: $result .= &Apache::lonhtmlcommon::htmlareaheaders();
9637: }
1.962 droeschl 9638: if ($args->{'force_register'} && $env{'request.noversionuri'} !~ m{^/res/adm/pages/}) {
9639: $result .= Apache::lonxml::display_title();
1.319 albertel 9640: }
1.436 albertel 9641: if (!$args->{'no_nav_bar'}
9642: && !$args->{'only_body'}
1.1438 raeburn 9643: && !$args->{'frameset'}
9644: && !$args->{'switchserver'}) {
1.1154 raeburn 9645: $result .= &help_menu_js($httphost);
1.1032 www 9646: $result.=&modal_window();
1.1038 www 9647: $result.=&togglebox_script();
1.1034 www 9648: $result.=&wishlist_window();
1.1041 www 9649: $result.=&LCprogressbarUpdate_script();
1.1034 www 9650: } else {
9651: if ($args->{'add_modal'}) {
9652: $result.=&modal_window();
9653: }
9654: if ($args->{'add_wishlist'}) {
9655: $result.=&wishlist_window();
9656: }
1.1038 www 9657: if ($args->{'add_togglebox'}) {
9658: $result.=&togglebox_script();
9659: }
1.1041 www 9660: if ($args->{'add_progressbar'}) {
9661: $result.=&LCprogressbarUpdate_script();
9662: }
1.436 albertel 9663: }
1.314 albertel 9664: if (ref($args->{'redirect'})) {
1.1388 raeburn 9665: my ($time,$url,$inhibit_continue,$to_opener,$skip_enc_check) = @{$args->{'redirect'}};
9666: if (!$skip_enc_check) {
9667: $url = &Apache::lonenc::check_encrypt($url);
9668: }
1.414 albertel 9669: if (!$inhibit_continue) {
9670: $env{'internal.head.redirect'} = $url;
9671: }
1.1386 raeburn 9672: $result.=<<"ADDMETA";
1.313 albertel 9673: <meta http-equiv="pragma" content="no-cache" />
1.1386 raeburn 9674: ADDMETA
9675: if ($to_opener) {
9676: $env{'internal.head.to_opener'} = 1;
9677: my $dest = &js_escape($url);
9678: my $timeout = int($time * 1000);
9679: $result .=<<"ENDJS";
9680: <script type="text/javascript">
9681: // <![CDATA[
9682: function LC_To_Opener() {
9683: var dest = '$dest';
9684: if (dest != '') {
9685: if (window.opener != null && !window.opener.closed) {
9686: window.opener.location.href=dest;
9687: window.close();
9688: } else {
9689: window.location.href=dest;
9690: }
9691: }
9692: }
9693: \$(document).ready(function () {
9694: setTimeout('LC_To_Opener()',$timeout);
9695: });
9696: // ]]>
9697: </script>
9698: ENDJS
9699: } else {
9700: $result.=<<"ADDMETA";
1.344 albertel 9701: <meta http-equiv="Refresh" content="$time; url=$url" />
1.313 albertel 9702: ADDMETA
1.1386 raeburn 9703: }
1.1210 raeburn 9704: } else {
9705: unless (($args->{'frameset'}) || ($args->{'js_ready'}) || ($args->{'only_body'}) || ($args->{'no_nav_bar'})) {
9706: my $requrl = $env{'request.uri'};
9707: if ($requrl eq '') {
9708: $requrl = $ENV{'REQUEST_URI'};
9709: $requrl =~ s/\?.+$//;
9710: }
9711: unless (($requrl =~ m{^/adm/(?:switchserver|login|authenticate|logout|groupsort|cleanup|helper|slotrequest|grades)(\?|$)}) ||
9712: (($requrl =~ m{^/res/}) && (($env{'form.submitted'} eq 'scantron') ||
9713: ($env{'form.grade_symb'}) || ($Apache::lonhomework::scantronmode)))) {
9714: my $dom_in_use = $Apache::lonnet::perlvar{'lonDefDomain'};
9715: unless (&Apache::lonnet::allowed('mau',$dom_in_use)) {
9716: my %domdefs = &Apache::lonnet::get_domain_defaults($dom_in_use);
1.1340 raeburn 9717: my $lonhost = $Apache::lonnet::perlvar{'lonHostID'};
1.1352 raeburn 9718: my ($offload,$offloadoth);
1.1210 raeburn 9719: if (ref($domdefs{'offloadnow'}) eq 'HASH') {
9720: if ($domdefs{'offloadnow'}{$lonhost}) {
1.1340 raeburn 9721: $offload = 1;
1.1353 raeburn 9722: if (($env{'user.domain'} ne '') && ($env{'user.domain'} ne $dom_in_use) &&
9723: (!(($env{'user.name'} eq 'public') && ($env{'user.domain'} eq 'public')))) {
9724: unless (&Apache::lonnet::shared_institution($env{'user.domain'})) {
9725: $offloadoth = 1;
9726: $dom_in_use = $env{'user.domain'};
9727: }
9728: }
1.1340 raeburn 9729: }
9730: }
9731: unless ($offload) {
9732: if (ref($domdefs{'offloadoth'}) eq 'HASH') {
9733: if ($domdefs{'offloadoth'}{$lonhost}) {
9734: if (($env{'user.domain'} ne '') && ($env{'user.domain'} ne $dom_in_use) &&
9735: (!(($env{'user.name'} eq 'public') && ($env{'user.domain'} eq 'public')))) {
9736: unless (&Apache::lonnet::shared_institution($env{'user.domain'})) {
9737: $offload = 1;
1.1352 raeburn 9738: $offloadoth = 1;
1.1340 raeburn 9739: $dom_in_use = $env{'user.domain'};
9740: }
1.1210 raeburn 9741: }
1.1340 raeburn 9742: }
9743: }
9744: }
9745: if ($offload) {
1.1358 raeburn 9746: my $newserver = &Apache::lonnet::spareserver(undef,30000,undef,1,$dom_in_use);
1.1352 raeburn 9747: if (($newserver eq '') && ($offloadoth)) {
9748: my @domains = &Apache::lonnet::current_machine_domains();
9749: if (($dom_in_use ne '') && (!grep(/^\Q$dom_in_use\E$/,@domains))) {
9750: ($newserver) = &Apache::lonnet::choose_server($dom_in_use);
9751: }
9752: }
1.1340 raeburn 9753: if (($newserver) && ($newserver ne $lonhost)) {
9754: my $numsec = 5;
9755: my $timeout = $numsec * 1000;
9756: my ($newurl,$locknum,%locks,$msg);
9757: if ($env{'request.role.adv'}) {
9758: ($locknum,%locks) = &Apache::lonnet::get_locks();
9759: }
9760: my $disable_submit = 0;
9761: if ($requrl =~ /$LONCAPA::assess_re/) {
9762: $disable_submit = 1;
9763: }
9764: if ($locknum) {
9765: my @lockinfo = sort(values(%locks));
1.1354 raeburn 9766: $msg = &mt('Once the following tasks are complete:')." \n".
1.1340 raeburn 9767: join(", ",sort(values(%locks)))."\n";
9768: if (&show_course()) {
9769: $msg .= &mt('your session will be transferred to a different server, after you click "Courses".');
9770: } else {
9771: $msg .= &mt('your session will be transferred to a different server, after you click "Roles".');
1.1210 raeburn 9772: }
1.1340 raeburn 9773: } else {
9774: if (($requrl =~ m{^/res/}) && ($env{'form.submitted'} =~ /^part_/)) {
9775: $msg = &mt('Your LON-CAPA submission has been recorded')."\n";
9776: }
9777: $msg .= &mt('Your current LON-CAPA session will be transferred to a different server in [quant,_1,second].',$numsec);
9778: $newurl = '/adm/switchserver?otherserver='.$newserver;
9779: if (($env{'request.role'}) && ($env{'request.role'} ne 'cm')) {
9780: $newurl .= '&role='.$env{'request.role'};
9781: }
9782: if ($env{'request.symb'}) {
9783: my $shownsymb = &Apache::lonenc::check_encrypt($env{'request.symb'});
9784: if ($shownsymb =~ m{^/enc/}) {
9785: my $reqdmajor = 2;
9786: my $reqdminor = 11;
9787: my $reqdsubminor = 3;
9788: my $newserverrev = &Apache::lonnet::get_server_loncaparev('',$newserver);
9789: my $remoterev = &Apache::lonnet::get_server_loncaparev(undef,$newserver);
9790: my ($major,$minor,$subminor) = ($remoterev =~ /^\'?(\d+)\.(\d+)\.(\d+|)[\w.\-]+\'?$/);
9791: if (($major eq '' && $minor eq '') ||
9792: (($reqdmajor > $major) || (($reqdmajor == $major) && ($reqdminor > $minor)) ||
9793: (($reqdmajor == $major) && ($reqdminor == $minor) && (($subminor eq '') ||
9794: ($reqdsubminor > $subminor))))) {
9795: undef($shownsymb);
9796: }
1.1210 raeburn 9797: }
1.1340 raeburn 9798: if ($shownsymb) {
9799: &js_escape(\$shownsymb);
9800: $newurl .= '&symb='.$shownsymb;
1.1210 raeburn 9801: }
1.1340 raeburn 9802: } else {
9803: my $shownurl = &Apache::lonenc::check_encrypt($requrl);
9804: &js_escape(\$shownurl);
9805: $newurl .= '&origurl='.$shownurl;
1.1210 raeburn 9806: }
1.1340 raeburn 9807: }
9808: &js_escape(\$msg);
9809: $result.=<<OFFLOAD
1.1210 raeburn 9810: <meta http-equiv="pragma" content="no-cache" />
9811: <script type="text/javascript">
1.1215 raeburn 9812: // <![CDATA[
1.1210 raeburn 9813: function LC_Offload_Now() {
9814: var dest = "$newurl";
9815: if (dest != '') {
9816: window.location.href="$newurl";
9817: }
9818: }
1.1214 raeburn 9819: \$(document).ready(function () {
9820: window.alert('$msg');
9821: if ($disable_submit) {
1.1210 raeburn 9822: \$(".LC_hwk_submit").prop("disabled", true);
9823: \$( ".LC_textline" ).prop( "readonly", "readonly");
1.1214 raeburn 9824: }
9825: setTimeout('LC_Offload_Now()', $timeout);
9826: });
1.1215 raeburn 9827: // ]]>
1.1210 raeburn 9828: </script>
9829: OFFLOAD
9830: }
9831: }
9832: }
9833: }
9834: }
1.313 albertel 9835: }
1.306 albertel 9836: if (!defined($title)) {
9837: $title = 'The LearningOnline Network with CAPA';
9838: }
1.460 albertel 9839: if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
1.1432 raeburn 9840: if ($title =~ /^LON-CAPA\s+/) {
9841: $result .= '<title> '.$title.'</title>';
9842: } else {
9843: $result .= '<title> LON-CAPA '.$title.'</title>';
9844: }
9845: $result .= "\n".'<link rel="stylesheet" type="text/css" href="'.$url.'"';
1.1168 raeburn 9846: if (!$args->{'frameset'}) {
9847: $result .= ' /';
9848: }
9849: $result .= '>'
1.1064 raeburn 9850: .$inhibitprint
1.414 albertel 9851: .$head_extra;
1.1242 raeburn 9852: my $clientmobile;
9853: if (($env{'user.name'} eq '') && ($env{'user.domain'} eq '')) {
9854: (undef,undef,undef,undef,undef,undef,$clientmobile) = &decode_user_agent();
9855: } else {
9856: $clientmobile = $env{'browser.mobile'};
9857: }
9858: if ($clientmobile) {
1.1137 raeburn 9859: $result .= '
1.1435 raeburn 9860: <meta name="viewport" content="width=device-width, initial-scale=1.0">
1.1137 raeburn 9861: <meta name="apple-mobile-web-app-capable" content="yes" />';
9862: }
1.1455 raeburn 9863: $result .= '<meta name="google" content="notranslate"';
9864: if (!$args->{'frameset'}) {
9865: $result .= ' /';
9866: }
9867: $result .= '>'."\n";
1.962 droeschl 9868: return $result.'</head>';
1.306 albertel 9869: }
9870:
9871: =pod
9872:
1.340 albertel 9873: =item * &font_settings()
9874:
9875: Returns neccessary <meta> to set the proper encoding
9876:
1.1160 raeburn 9877: Inputs: optional reference to HASH -- $args passed to &headtag()
1.340 albertel 9878:
9879: =cut
9880:
9881: sub font_settings {
1.1160 raeburn 9882: my ($args) = @_;
1.340 albertel 9883: my $headerstring='';
1.1160 raeburn 9884: if ((!$env{'browser.mathml'} && $env{'browser.unicode'}) ||
9885: ((ref($args) eq 'HASH') && ($args->{'browser.unicode'}))) {
1.1168 raeburn 9886: $headerstring.=
9887: '<meta http-equiv="Content-Type" content="text/html; charset=utf-8"';
9888: if (!$args->{'frameset'}) {
9889: $headerstring.= ' /';
9890: }
9891: $headerstring .= '>'."\n";
1.340 albertel 9892: }
9893: return $headerstring;
9894: }
9895:
1.341 albertel 9896: =pod
9897:
1.1064 raeburn 9898: =item * &print_suppression()
9899:
9900: In course context returns css which causes the body to be blank when media="print",
9901: if printout generation is unavailable for the current resource.
9902:
9903: This could be because:
9904:
9905: (a) printstartdate is in the future
9906:
9907: (b) printenddate is in the past
9908:
9909: (c) there is an active exam block with "printout"
9910: functionality blocked
9911:
9912: Users with pav, pfo or evb privileges are exempt.
9913:
9914: Inputs: none
9915:
9916: =cut
9917:
9918:
9919: sub print_suppression {
9920: my $noprint;
9921: if ($env{'request.course.id'}) {
9922: my $scope = $env{'request.course.id'};
9923: if ((&Apache::lonnet::allowed('pav',$scope)) ||
9924: (&Apache::lonnet::allowed('pfo',$scope))) {
9925: return;
9926: }
9927: if ($env{'request.course.sec'} ne '') {
9928: $scope .= "/$env{'request.course.sec'}";
9929: if ((&Apache::lonnet::allowed('pav',$scope)) ||
9930: (&Apache::lonnet::allowed('pfo',$scope))) {
1.1065 raeburn 9931: return;
1.1064 raeburn 9932: }
9933: }
9934: my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
9935: my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
1.1372 raeburn 9936: my $clientip = &Apache::lonnet::get_requestor_ip();
9937: my $blocked = &blocking_status('printout',$clientip,$cnum,$cdom,undef,1);
1.1064 raeburn 9938: if ($blocked) {
9939: my $checkrole = "cm./$cdom/$cnum";
9940: if ($env{'request.course.sec'} ne '') {
9941: $checkrole .= "/$env{'request.course.sec'}";
9942: }
9943: unless ((&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) &&
9944: ($env{'request.role'} !~ m{^st\./$cdom/$cnum})) {
9945: $noprint = 1;
9946: }
9947: }
9948: unless ($noprint) {
9949: my $symb = &Apache::lonnet::symbread();
9950: if ($symb ne '') {
9951: my $navmap = Apache::lonnavmaps::navmap->new();
9952: if (ref($navmap)) {
9953: my $res = $navmap->getBySymb($symb);
9954: if (ref($res)) {
9955: if (!$res->resprintable()) {
9956: $noprint = 1;
9957: }
9958: }
9959: }
9960: }
9961: }
9962: if ($noprint) {
9963: return <<"ENDSTYLE";
9964: <style type="text/css" media="print">
9965: body { display:none }
9966: </style>
9967: ENDSTYLE
9968: }
9969: }
9970: return;
9971: }
9972:
9973: =pod
9974:
1.341 albertel 9975: =item * &xml_begin()
9976:
9977: Returns the needed doctype and <html>
9978:
9979: Inputs: none
9980:
9981: =cut
9982:
9983: sub xml_begin {
1.1168 raeburn 9984: my ($is_frameset) = @_;
1.341 albertel 9985: my $output='';
9986:
9987: if ($env{'browser.mathml'}) {
9988: $output='<?xml version="1.0"?>'
9989: #.'<?xml-stylesheet type="text/css" href="/adm/MathML/mathml.css"?>'."\n"
9990: # .'<!DOCTYPE html SYSTEM "/adm/MathML/mathml.dtd" '
9991:
9992: # .'<!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">] >'
9993: .'<!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">'
9994: .'<html xmlns:math="http://www.w3.org/1998/Math/MathML" '
9995: .'xmlns="http://www.w3.org/1999/xhtml">';
1.1168 raeburn 9996: } elsif ($is_frameset) {
9997: $output='<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">'."\n".
1.1459 raeburn 9998: '<html lang="en">'."\n";
1.341 albertel 9999: } else {
1.1168 raeburn 10000: $output='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'."\n".
10001: '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">'."\n";
1.341 albertel 10002: }
10003: return $output;
10004: }
1.340 albertel 10005:
10006: =pod
10007:
1.306 albertel 10008: =item * &start_page()
10009:
10010: Returns a complete <html> .. <body> section for LON-CAPA web pages.
10011:
1.648 raeburn 10012: Inputs:
10013:
10014: =over 4
10015:
10016: $title - optional title for the page
10017:
10018: $head_extra - optional extra HTML to incude inside the <head>
10019:
10020: $args - additional optional args supported are:
10021:
10022: =over 8
10023:
10024: only_body -> is true will set &bodytag() onlybodytag
1.317 albertel 10025: arg on
1.814 bisitz 10026: no_nav_bar -> is true will set &bodytag() no_nav_bar arg on
1.648 raeburn 10027: add_entries -> additional attributes to add to the <body>
10028: domain -> force to color decorate a page for a
1.317 albertel 10029: specific domain
1.648 raeburn 10030: function -> force usage of a specific rolish color
1.317 albertel 10031: scheme
1.648 raeburn 10032: redirect -> see &headtag()
10033: bgcolor -> override the default page bg color
10034: js_ready -> return a string ready for being used in
1.317 albertel 10035: a javascript writeln
1.648 raeburn 10036: html_encode -> return a string ready for being used in
1.320 albertel 10037: a html attribute
1.648 raeburn 10038: force_register -> if is true will turn on the &bodytag()
1.317 albertel 10039: $forcereg arg
1.648 raeburn 10040: frameset -> if true will start with a <frameset>
1.330 albertel 10041: rather than <body>
1.648 raeburn 10042: skip_phases -> hash ref of
1.338 albertel 10043: head -> skip the <html><head> generation
10044: body -> skip all <body> generation
1.648 raeburn 10045: no_auto_mt_title -> prevent &mt()ing the title arg
1.867 kalberla 10046: bread_crumbs -> Array containing breadcrumbs
1.983 raeburn 10047: bread_crumbs_component -> if exists show it as headline else show only the breadcrumbs
1.1437 raeburn 10048: bread_crumbs_style -> breadcrumbs are contained within <div id="LC_breadcrumbs">,
10049: and &standard_css() contains CSS for #LC_breadcrumbs, if you want
10050: to override those values, or add to them, specify the value to
10051: include in the style attribute to include in the div tag by using
10052: bread_crumbs_style (e.g., overflow: visible)
1.1272 raeburn 10053: bread_crumbs_nomenu -> if true will pass false as the value of $menulink
10054: to lonhtmlcommon::breadcrumbs
1.1096 raeburn 10055: group -> includes the current group, if page is for a
1.1274 raeburn 10056: specific group
10057: use_absolute -> for request for external resource or syllabus, this
10058: will contain https://<hostname> if server uses
10059: https (as per hosts.tab), but request is for http
10060: hostname -> hostname, originally from $r->hostname(), (optional).
1.1369 raeburn 10061: links_disabled -> Links in primary and secondary menus are disabled
10062: (Can enable them once page has loaded - see lonroles.pm
10063: for an example).
1.1380 raeburn 10064: links_target -> Target for links, e.g., _parent (optional).
1.361 albertel 10065:
1.648 raeburn 10066: =back
1.460 albertel 10067:
1.648 raeburn 10068: =back
1.562 albertel 10069:
1.306 albertel 10070: =cut
10071:
10072: sub start_page {
1.309 albertel 10073: my ($title,$head_extra,$args) = @_;
1.318 albertel 10074: #&Apache::lonnet::logthis("start_page ".join(':',caller(0)));
1.319 albertel 10075:
1.315 albertel 10076: $env{'internal.start_page'}++;
1.1359 raeburn 10077: my ($result,@advtools,$ltiscope,$ltiuri,%ltimenu,$menucoll,%menu);
1.964 droeschl 10078:
1.338 albertel 10079: if (! exists($args->{'skip_phases'}{'head'}) ) {
1.1168 raeburn 10080: $result .= &xml_begin($args->{'frameset'}) . &headtag($title, $head_extra, $args);
1.338 albertel 10081: }
1.1316 raeburn 10082:
10083: if (($env{'request.course.id'}) && ($env{'request.lti.login'})) {
1.1318 raeburn 10084: if ($env{'course.'.$env{'request.course.id'}.'.lti.override'}) {
10085: unless ($env{'course.'.$env{'request.course.id'}.'.lti.topmenu'}) {
10086: $args->{'no_primary_menu'} = 1;
10087: }
10088: unless ($env{'course.'.$env{'request.course.id'}.'.lti.inlinemenu'}) {
10089: $args->{'no_inline_menu'} = 1;
10090: }
10091: if ($env{'course.'.$env{'request.course.id'}.'.lti.lcmenu'}) {
10092: map { $ltimenu{$_} = 1; } split(/,/,$env{'course.'.$env{'request.course.id'}.'.lti.lcmenu'});
10093: }
10094: } else {
10095: my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
10096: my %lti = &Apache::lonnet::get_domain_lti($cdom,'provider');
10097: if (ref($lti{$env{'request.lti.login'}}) eq 'HASH') {
10098: unless ($lti{$env{'request.lti.login'}}{'topmenu'}) {
10099: $args->{'no_primary_menu'} = 1;
10100: }
10101: unless ($lti{$env{'request.lti.login'}}{'inlinemenu'}) {
10102: $args->{'no_inline_menu'} = 1;
10103: }
10104: if (ref($lti{$env{'request.lti.login'}}{'lcmenu'}) eq 'ARRAY') {
10105: map { $ltimenu{$_} = 1; } @{$lti{$env{'request.lti.login'}}{'lcmenu'}};
10106: }
10107: }
10108: }
1.1316 raeburn 10109: ($ltiscope,$ltiuri) = &LONCAPA::ltiutils::lti_provider_scope($env{'request.lti.uri'},
10110: $env{'course.'.$env{'request.course.id'}.'.domain'},
10111: $env{'course.'.$env{'request.course.id'}.'.num'});
1.1359 raeburn 10112: } elsif ($env{'request.course.id'}) {
10113: my $expiretime=600;
10114: if ((time-$env{'course.'.$env{'request.course.id'}.'.last_cache'}) > $expiretime) {
10115: &Apache::lonnet::coursedescription($env{'request.course.id'},{'freshen_cache' => 1});
10116: }
10117: my ($deeplinkmenu,$menuref);
10118: ($menucoll,$deeplinkmenu,$menuref) = &menucoll_in_effect();
10119: if ($menucoll) {
10120: if (ref($menuref) eq 'HASH') {
10121: %menu = %{$menuref};
10122: }
10123: if ($menu{'top'} eq 'n') {
10124: $args->{'no_primary_menu'} = 1;
10125: }
10126: if ($menu{'inline'} eq 'n') {
10127: unless (&Apache::lonnet::allowed('opa')) {
10128: my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
10129: my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
10130: my $crstype = &course_type();
10131: my $now = time;
10132: my $ccrole;
10133: if ($crstype eq 'Community') {
10134: $ccrole = 'co';
10135: } else {
10136: $ccrole = 'cc';
10137: }
10138: if ($env{'user.role.'.$ccrole.'./'.$cdom.'/'.$cnum}) {
10139: my ($start,$end) = split(/\./,$env{'user.role.'.$ccrole.'./'.$cdom.'/'.$cnum});
10140: if ((($start) && ($start<0)) ||
10141: (($end) && ($end<$now)) ||
10142: (($start) && ($now<$start))) {
10143: $args->{'no_inline_menu'} = 1;
10144: }
10145: } else {
10146: $args->{'no_inline_menu'} = 1;
10147: }
10148: }
10149: }
10150: }
1.1316 raeburn 10151: }
1.1359 raeburn 10152:
1.1385 raeburn 10153: my $showncrumbs;
1.338 albertel 10154: if (! exists($args->{'skip_phases'}{'body'}) ) {
10155: if ($args->{'frameset'}) {
10156: my $attr_string = &make_attr_string($args->{'force_register'},
10157: $args->{'add_entries'});
10158: $result .= "\n<frameset $attr_string>\n";
1.831 bisitz 10159: } else {
10160: $result .=
10161: &bodytag($title,
10162: $args->{'function'}, $args->{'add_entries'},
10163: $args->{'only_body'}, $args->{'domain'},
10164: $args->{'force_register'}, $args->{'no_nav_bar'},
1.1096 raeburn 10165: $args->{'bgcolor'}, $args,
1.1385 raeburn 10166: \@advtools,$ltiscope,$ltiuri,\%ltimenu,$menucoll,
10167: \%menu,\$showncrumbs);
1.831 bisitz 10168: }
1.330 albertel 10169: }
1.338 albertel 10170:
1.315 albertel 10171: if ($args->{'js_ready'}) {
1.713 kaisler 10172: $result = &js_ready($result);
1.315 albertel 10173: }
1.320 albertel 10174: if ($args->{'html_encode'}) {
1.713 kaisler 10175: $result = &html_encode($result);
10176: }
10177:
1.813 bisitz 10178: # Preparation for new and consistent functionlist at top of screen
10179: # if ($args->{'functionlist'}) {
10180: # $result .= &build_functionlist();
10181: #}
10182:
1.964 droeschl 10183: # Don't add anything more if only_body wanted or in const space
10184: return $result if $args->{'only_body'}
10185: || $env{'request.state'} eq 'construct';
1.813 bisitz 10186:
10187: #Breadcrumbs
1.758 kaisler 10188: if (exists($args->{'bread_crumbs'}) or exists($args->{'bread_crumbs_component'})) {
1.1385 raeburn 10189: unless ($showncrumbs) {
1.758 kaisler 10190: &Apache::lonhtmlcommon::clear_breadcrumbs();
10191: #if any br links exists, add them to the breadcrumbs
10192: if (exists($args->{'bread_crumbs'}) and ref($args->{'bread_crumbs'}) eq 'ARRAY') {
10193: foreach my $crumb (@{$args->{'bread_crumbs'}}){
10194: &Apache::lonhtmlcommon::add_breadcrumb($crumb);
10195: }
10196: }
1.1096 raeburn 10197: # if @advtools array contains items add then to the breadcrumbs
10198: if (@advtools > 0) {
10199: &Apache::lonmenu::advtools_crumbs(@advtools);
10200: }
1.1272 raeburn 10201: my $menulink;
10202: # if arg: bread_crumbs_nomenu is true pass 0 as $menulink item.
10203: if ((exists($args->{'bread_crumbs_nomenu'})) ||
1.1312 raeburn 10204: ($ltiscope eq 'map') || ($ltiscope eq 'resource') ||
1.1272 raeburn 10205: ((($args->{'crstype'} eq 'Placement') || (($env{'request.course.id'}) &&
10206: ($env{'course.'.$env{'request.course.id'}.'.type'} eq 'Placement'))) &&
10207: (!$env{'request.role.adv'}))) {
10208: $menulink = 0;
10209: } else {
10210: undef($menulink);
10211: }
1.1385 raeburn 10212: my $linkprotout;
10213: if ($env{'request.deeplink.login'}) {
10214: my $linkprotout = &Apache::lonmenu::linkprot_exit();
10215: if ($linkprotout) {
10216: &Apache::lonhtmlcommon::add_breadcrumb_tool('tools',$linkprotout);
10217: }
10218: }
1.758 kaisler 10219: #if bread_crumbs_component exists show it as headline else show only the breadcrumbs
10220: if(exists($args->{'bread_crumbs_component'})){
1.1437 raeburn 10221: $result .= &Apache::lonhtmlcommon::breadcrumbs($args->{'bread_crumbs_component'},
10222: '',$menulink,'',
10223: $args->{'bread_crumbs_style'});
1.1237 raeburn 10224: } else {
1.1437 raeburn 10225: $result .= &Apache::lonhtmlcommon::breadcrumbs('','',$menulink,'',
10226: $args->{'bread_crumbs_style'});
1.758 kaisler 10227: }
1.1385 raeburn 10228: }
1.320 albertel 10229: }
1.315 albertel 10230: return $result;
1.306 albertel 10231: }
10232:
10233: sub end_page {
1.315 albertel 10234: my ($args) = @_;
10235: $env{'internal.end_page'}++;
1.330 albertel 10236: my $result;
1.335 albertel 10237: if ($args->{'discussion'}) {
10238: my ($target,$parser);
10239: if (ref($args->{'discussion'})) {
10240: ($target,$parser) =($args->{'discussion'}{'target'},
10241: $args->{'discussion'}{'parser'});
10242: }
10243: $result .= &Apache::lonxml::xmlend($target,$parser);
10244: }
1.330 albertel 10245: if ($args->{'frameset'}) {
10246: $result .= '</frameset>';
10247: } else {
1.635 raeburn 10248: $result .= &endbodytag($args);
1.330 albertel 10249: }
1.1080 raeburn 10250: unless ($args->{'notbody'}) {
10251: $result .= "\n</html>";
10252: }
1.330 albertel 10253:
1.315 albertel 10254: if ($args->{'js_ready'}) {
1.317 albertel 10255: $result = &js_ready($result);
1.315 albertel 10256: }
1.335 albertel 10257:
1.320 albertel 10258: if ($args->{'html_encode'}) {
10259: $result = &html_encode($result);
10260: }
1.335 albertel 10261:
1.315 albertel 10262: return $result;
10263: }
10264:
1.1359 raeburn 10265: sub menucoll_in_effect {
10266: my ($menucoll,$deeplinkmenu,%menu);
10267: if ($env{'request.course.id'}) {
10268: $menucoll = $env{'course.'.$env{'request.course.id'}.'.menudefault'};
1.1362 raeburn 10269: if ($env{'request.deeplink.login'}) {
1.1370 raeburn 10270: my ($deeplink_symb,$deeplink,$check_login_symb);
1.1362 raeburn 10271: my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
10272: my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
10273: if ($env{'request.noversionuri'} =~ m{^/(res|uploaded)/}) {
10274: if ($env{'request.noversionuri'} =~ /\.(page|sequence)$/) {
10275: my $navmap = Apache::lonnavmaps::navmap->new();
10276: if (ref($navmap)) {
10277: $deeplink = $navmap->get_mapparam(undef,
10278: &Apache::lonnet::declutter($env{'request.noversionuri'}),
10279: '0.deeplink');
1.1370 raeburn 10280: } else {
10281: $check_login_symb = 1;
1.1362 raeburn 10282: }
10283: } else {
1.1370 raeburn 10284: my $symb = &Apache::lonnet::symbread();
10285: if ($symb) {
10286: $deeplink = &Apache::lonnet::EXT('resource.0.deeplink',$symb);
10287: } else {
10288: $check_login_symb = 1;
10289: }
1.1362 raeburn 10290: }
10291: } else {
1.1370 raeburn 10292: $check_login_symb = 1;
10293: }
10294: if ($check_login_symb) {
1.1362 raeburn 10295: $deeplink_symb = &deeplink_login_symb($cnum,$cdom);
10296: if ($deeplink_symb =~ /\.(page|sequence)$/) {
10297: my $mapname = &Apache::lonnet::deversion((&Apache::lonnet::decode_symb($deeplink_symb))[2]);
10298: my $navmap = Apache::lonnavmaps::navmap->new();
10299: if (ref($navmap)) {
10300: $deeplink = $navmap->get_mapparam(undef,$mapname,'0.deeplink');
10301: }
10302: } else {
10303: $deeplink = &Apache::lonnet::EXT('resource.0.deeplink',$deeplink_symb);
10304: }
10305: }
1.1359 raeburn 10306: if ($deeplink ne '') {
1.1378 raeburn 10307: my ($state,$others,$listed,$scope,$protect,$display,$target) = split(/,/,$deeplink);
1.1359 raeburn 10308: if ($display =~ /^\d+$/) {
10309: $deeplinkmenu = 1;
10310: $menucoll = $display;
10311: }
10312: }
10313: }
10314: if ($menucoll) {
10315: %menu = &page_menu($env{'course.'.$env{'request.course.id'}.'.menucollections'},$menucoll);
10316: }
10317: }
10318: return ($menucoll,$deeplinkmenu,\%menu);
10319: }
10320:
1.1362 raeburn 10321: sub deeplink_login_symb {
10322: my ($cnum,$cdom) = @_;
10323: my $login_symb;
10324: if ($env{'request.deeplink.login'}) {
1.1364 raeburn 10325: $login_symb = &symb_from_tinyurl($env{'request.deeplink.login'},$cnum,$cdom);
10326: }
10327: return $login_symb;
10328: }
10329:
10330: sub symb_from_tinyurl {
10331: my ($url,$cnum,$cdom) = @_;
10332: if ($url =~ m{^\Q/tiny/$cdom/\E(\w+)$}) {
10333: my $key = $1;
10334: my ($tinyurl,$login);
10335: my ($result,$cached)=&Apache::lonnet::is_cached_new('tiny',$cdom."\0".$key);
10336: if (defined($cached)) {
10337: $tinyurl = $result;
10338: } else {
10339: my $configuname = &Apache::lonnet::get_domainconfiguser($cdom);
10340: my %currtiny = &Apache::lonnet::get('tiny',[$key],$cdom,$configuname);
10341: if ($currtiny{$key} ne '') {
10342: $tinyurl = $currtiny{$key};
10343: &Apache::lonnet::do_cache_new('tiny',$cdom."\0".$key,$currtiny{$key},600);
1.1362 raeburn 10344: }
1.1364 raeburn 10345: }
10346: if ($tinyurl ne '') {
10347: my ($cnumreq,$symb) = split(/\&/,$tinyurl);
10348: if (wantarray) {
10349: return ($cnumreq,$symb);
10350: } elsif ($cnumreq eq $cnum) {
10351: return $symb;
1.1362 raeburn 10352: }
10353: }
10354: }
1.1364 raeburn 10355: if (wantarray) {
10356: return ();
10357: } else {
10358: return;
10359: }
1.1362 raeburn 10360: }
10361:
1.1405 raeburn 10362: sub usable_exttools {
10363: my %tooltypes;
10364: if ($env{'request.course.id'}) {
10365: if ($env{'course.'.$env{'request.course.id'}.'.internal.exttool'}) {
10366: if ($env{'course.'.$env{'request.course.id'}.'.internal.exttool'} eq 'both') {
10367: %tooltypes = (
10368: crs => 1,
10369: dom => 1,
10370: );
10371: } elsif ($env{'course.'.$env{'request.course.id'}.'.internal.exttool'} eq 'crs') {
10372: $tooltypes{'crs'} = 1;
10373: } elsif ($env{'course.'.$env{'request.course.id'}.'.internal.exttool'} eq 'dom') {
10374: $tooltypes{'dom'} = 1;
10375: }
10376: } else {
10377: my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
10378: my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
10379: my $crstype = lc($env{'course.'.$env{'request.course.id'}.'.type'});
10380: if ($crstype eq '') {
10381: $crstype = 'course';
10382: }
10383: if ($crstype eq 'course') {
10384: if ($env{'course.'.$env{'request.course.id'}.'internal.coursecode'}) {
10385: $crstype = 'official';
10386: } elsif ($env{'course.'.$env{'request.course.id'}.'.internal.textbook'}) {
10387: $crstype = 'textbook';
10388: } elsif ($env{'course.'.$env{'request.course.id'}.'.internal.lti'}) {
10389: $crstype = 'lti';
10390: } else {
10391: $crstype = 'unofficial';
10392: }
10393: }
10394: my %domdefaults = &Apache::lonnet::get_domain_defaults($cdom);
10395: if ($domdefaults{$crstype.'domexttool'}) {
10396: $tooltypes{'dom'} = 1;
10397: }
10398: if ($domdefaults{$crstype.'exttool'}) {
10399: $tooltypes{'crs'} = 1;
10400: }
10401: }
10402: }
10403: return %tooltypes;
10404: }
10405:
1.1034 www 10406: sub wishlist_window {
10407: return(<<'ENDWISHLIST');
1.1046 raeburn 10408: <script type="text/javascript">
1.1034 www 10409: // <![CDATA[
10410: // <!-- BEGIN LON-CAPA Internal
10411: function set_wishlistlink(title, path) {
10412: if (!title) {
10413: title = document.title;
10414: title = title.replace(/^LON-CAPA /,'');
10415: }
1.1175 raeburn 10416: title = encodeURIComponent(title);
1.1203 raeburn 10417: title = title.replace("'","\\\'");
1.1034 www 10418: if (!path) {
10419: path = location.pathname;
10420: }
1.1175 raeburn 10421: path = encodeURIComponent(path);
1.1203 raeburn 10422: path = path.replace("'","\\\'");
1.1034 www 10423: Win = window.open('/adm/wishlist?mode=newLink&setTitle='+title+'&setPath='+path,
10424: 'wishlistNewLink','width=560,height=350,scrollbars=0');
10425: }
10426: // END LON-CAPA Internal -->
10427: // ]]>
10428: </script>
10429: ENDWISHLIST
10430: }
10431:
1.1030 www 10432: sub modal_window {
10433: return(<<'ENDMODAL');
1.1046 raeburn 10434: <script type="text/javascript">
1.1030 www 10435: // <![CDATA[
10436: // <!-- BEGIN LON-CAPA Internal
10437: var modalWindow = {
10438: parent:"body",
10439: windowId:null,
10440: content:null,
10441: width:null,
10442: height:null,
10443: close:function()
10444: {
10445: $(".LCmodal-window").remove();
10446: $(".LCmodal-overlay").remove();
10447: },
10448: open:function()
10449: {
10450: var modal = "";
10451: modal += "<div class=\"LCmodal-overlay\"></div>";
10452: 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;\">";
10453: modal += this.content;
10454: modal += "</div>";
10455:
10456: $(this.parent).append(modal);
10457:
10458: $(".LCmodal-window").append("<a class=\"LCclose-window\"></a>");
10459: $(".LCclose-window").click(function(){modalWindow.close();});
10460: $(".LCmodal-overlay").click(function(){modalWindow.close();});
10461: }
10462: };
1.1140 raeburn 10463: var openMyModal = function(source,width,height,scrolling,transparency,style)
1.1030 www 10464: {
1.1266 raeburn 10465: source = source.replace(/'/g,"'");
1.1030 www 10466: modalWindow.windowId = "myModal";
10467: modalWindow.width = width;
10468: modalWindow.height = height;
1.1196 raeburn 10469: modalWindow.content = "<iframe width='"+width+"' height='"+height+"' frameborder='0' scrolling='"+scrolling+"' allowtransparency='"+transparency+"' src='" + source + "' style='"+style+"'></iframe>";
1.1030 www 10470: modalWindow.open();
1.1208 raeburn 10471: };
1.1030 www 10472: // END LON-CAPA Internal -->
10473: // ]]>
10474: </script>
10475: ENDMODAL
10476: }
10477:
10478: sub modal_link {
1.1140 raeburn 10479: my ($link,$linktext,$width,$height,$target,$scrolling,$title,$transparency,$style)=@_;
1.1030 www 10480: unless ($width) { $width=480; }
10481: unless ($height) { $height=400; }
1.1031 www 10482: unless ($scrolling) { $scrolling='yes'; }
1.1140 raeburn 10483: unless ($transparency) { $transparency='true'; }
10484:
1.1074 raeburn 10485: my $target_attr;
10486: if (defined($target)) {
10487: $target_attr = 'target="'.$target.'"';
10488: }
10489: return <<"ENDLINK";
1.1336 raeburn 10490: <a href="$link" $target_attr title="$title" onclick="javascript:openMyModal('$link',$width,$height,'$scrolling','$transparency','$style'); return false;">$linktext</a>
1.1074 raeburn 10491: ENDLINK
1.1030 www 10492: }
10493:
1.1032 www 10494: sub modal_adhoc_script {
1.1365 raeburn 10495: my ($funcname,$width,$height,$content,$possmathjax)=@_;
10496: my $mathjax;
10497: if ($possmathjax) {
10498: $mathjax = <<'ENDJAX';
10499: if (typeof MathJax == 'object') {
10500: MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
10501: }
10502: ENDJAX
10503: }
1.1032 www 10504: return (<<ENDADHOC);
1.1046 raeburn 10505: <script type="text/javascript">
1.1032 www 10506: // <![CDATA[
10507: var $funcname = function()
10508: {
10509: modalWindow.windowId = "myModal";
10510: modalWindow.width = $width;
10511: modalWindow.height = $height;
10512: modalWindow.content = '$content';
10513: modalWindow.open();
1.1365 raeburn 10514: $mathjax
1.1032 www 10515: };
10516: // ]]>
10517: </script>
10518: ENDADHOC
10519: }
10520:
1.1041 www 10521: sub modal_adhoc_inner {
1.1365 raeburn 10522: my ($funcname,$width,$height,$content,$possmathjax)=@_;
1.1041 www 10523: my $innerwidth=$width-20;
10524: $content=&js_ready(
1.1140 raeburn 10525: &start_page('Dialog',undef,{'only_body'=>1,'bgcolor'=>'#FFFFFF'}).
10526: &start_scrollbox($width.'px',$innerwidth.'px',$height.'px','myModal','#FFFFFF',undef,1).
10527: $content.
1.1041 www 10528: &end_scrollbox().
1.1140 raeburn 10529: &end_page()
1.1041 www 10530: );
1.1365 raeburn 10531: return &modal_adhoc_script($funcname,$width,$height,$content,$possmathjax);
1.1041 www 10532: }
10533:
10534: sub modal_adhoc_window {
1.1365 raeburn 10535: my ($funcname,$width,$height,$content,$linktext,$possmathjax)=@_;
10536: return &modal_adhoc_inner($funcname,$width,$height,$content,$possmathjax).
1.1041 www 10537: "<a href=\"javascript:$funcname();void(0);\">".$linktext."</a>";
10538: }
10539:
10540: sub modal_adhoc_launch {
10541: my ($funcname,$width,$height,$content)=@_;
10542: return &modal_adhoc_inner($funcname,$width,$height,$content).(<<ENDLAUNCH);
10543: <script type="text/javascript">
10544: // <![CDATA[
10545: $funcname();
10546: // ]]>
10547: </script>
10548: ENDLAUNCH
10549: }
10550:
10551: sub modal_adhoc_close {
10552: return (<<ENDCLOSE);
10553: <script type="text/javascript">
10554: // <![CDATA[
10555: modalWindow.close();
10556: // ]]>
10557: </script>
10558: ENDCLOSE
10559: }
10560:
1.1038 www 10561: sub togglebox_script {
10562: return(<<ENDTOGGLE);
10563: <script type="text/javascript">
10564: // <![CDATA[
10565: function LCtoggleDisplay(id,hidetext,showtext) {
10566: link = document.getElementById(id + "link").childNodes[0];
10567: with (document.getElementById(id).style) {
10568: if (display == "none" ) {
10569: display = "inline";
10570: link.nodeValue = hidetext;
10571: } else {
10572: display = "none";
10573: link.nodeValue = showtext;
10574: }
10575: }
10576: }
10577: // ]]>
10578: </script>
10579: ENDTOGGLE
10580: }
10581:
1.1039 www 10582: sub start_togglebox {
10583: my ($id,$heading,$headerbg,$hidetext,$showtext)=@_;
10584: unless ($heading) { $heading=''; } else { $heading.=' '; }
10585: unless ($showtext) { $showtext=&mt('show'); }
10586: unless ($hidetext) { $hidetext=&mt('hide'); }
10587: unless ($headerbg) { $headerbg='#FFFFFF'; }
10588: return &start_data_table().
10589: &start_data_table_header_row().
10590: '<td bgcolor="'.$headerbg.'">'.$heading.
10591: '[<a id="'.$id.'link" href="javascript:LCtoggleDisplay(\''.$id.'\',\''.$hidetext.'\',\''.
10592: $showtext.'\')">'.$showtext.'</a>]</td>'.
10593: &end_data_table_header_row().
10594: '<tr id="'.$id.'" style="display:none""><td>';
10595: }
10596:
10597: sub end_togglebox {
10598: return '</td></tr>'.&end_data_table();
10599: }
10600:
1.1041 www 10601: sub LCprogressbar_script {
1.1302 raeburn 10602: my ($id,$number_to_do)=@_;
10603: if ($number_to_do) {
10604: return(<<ENDPROGRESS);
1.1041 www 10605: <script type="text/javascript">
10606: // <![CDATA[
1.1045 www 10607: \$('#progressbar$id').progressbar({
1.1041 www 10608: value: 0,
10609: change: function(event, ui) {
10610: var newVal = \$(this).progressbar('option', 'value');
10611: \$('.pblabel', this).text(LCprogressTxt);
10612: }
10613: });
10614: // ]]>
10615: </script>
10616: ENDPROGRESS
1.1302 raeburn 10617: } else {
10618: return(<<ENDPROGRESS);
10619: <script type="text/javascript">
10620: // <![CDATA[
10621: \$('#progressbar$id').progressbar({
10622: value: false,
10623: create: function(event, ui) {
10624: \$('.ui-widget-header', this).css({'background':'#F0F0F0'});
10625: \$('.ui-progressbar-overlay', this).css({'margin':'0'});
10626: }
10627: });
10628: // ]]>
10629: </script>
10630: ENDPROGRESS
10631: }
1.1041 www 10632: }
10633:
10634: sub LCprogressbarUpdate_script {
10635: return(<<ENDPROGRESSUPDATE);
10636: <style type="text/css">
10637: .ui-progressbar { position:relative; }
1.1302 raeburn 10638: .progress-label {position: absolute; width: 100%; text-align: center; top: 1px; font-weight: bold; text-shadow: 1px 1px 0 #fff;margin: 0; line-height: 200%; }
1.1041 www 10639: .pblabel { position: absolute; width: 100%; text-align: center; line-height: 1.9em; }
10640: </style>
10641: <script type="text/javascript">
10642: // <![CDATA[
1.1045 www 10643: var LCprogressTxt='---';
10644:
1.1302 raeburn 10645: function LCupdateProgress(percent,progresstext,id,maxnum) {
1.1041 www 10646: LCprogressTxt=progresstext;
1.1302 raeburn 10647: if ((maxnum == '') || (maxnum == undefined) || (maxnum == null)) {
10648: \$('#progressbar'+id).find('.progress-label').text(LCprogressTxt);
10649: } else if (percent === \$('#progressbar'+id).progressbar( "value" )) {
1.1301 raeburn 10650: \$('#progressbar'+id).find('.pblabel').text(LCprogressTxt);
10651: } else {
10652: \$('#progressbar'+id).progressbar('value',percent);
10653: }
1.1041 www 10654: }
10655: // ]]>
10656: </script>
10657: ENDPROGRESSUPDATE
10658: }
10659:
1.1042 www 10660: my $LClastpercent;
1.1045 www 10661: my $LCidcnt;
10662: my $LCcurrentid;
1.1042 www 10663:
1.1041 www 10664: sub LCprogressbar {
1.1302 raeburn 10665: my ($r,$number_to_do,$preamble)=@_;
1.1042 www 10666: $LClastpercent=0;
1.1045 www 10667: $LCidcnt++;
10668: $LCcurrentid=$$.'_'.$LCidcnt;
1.1302 raeburn 10669: my ($starting,$content);
10670: if ($number_to_do) {
10671: $starting=&mt('Starting');
10672: $content=(<<ENDPROGBAR);
10673: $preamble
1.1045 www 10674: <div id="progressbar$LCcurrentid">
1.1041 www 10675: <span class="pblabel">$starting</span>
10676: </div>
10677: ENDPROGBAR
1.1302 raeburn 10678: } else {
10679: $starting=&mt('Loading...');
10680: $LClastpercent='false';
10681: $content=(<<ENDPROGBAR);
10682: $preamble
10683: <div id="progressbar$LCcurrentid">
10684: <div class="progress-label">$starting</div>
10685: </div>
10686: ENDPROGBAR
10687: }
10688: &r_print($r,$content.&LCprogressbar_script($LCcurrentid,$number_to_do));
1.1041 www 10689: }
10690:
10691: sub LCprogressbarUpdate {
1.1302 raeburn 10692: my ($r,$val,$text,$number_to_do)=@_;
10693: if ($number_to_do) {
10694: unless ($val) {
10695: if ($LClastpercent) {
10696: $val=$LClastpercent;
10697: } else {
10698: $val=0;
10699: }
10700: }
10701: if ($val<0) { $val=0; }
10702: if ($val>100) { $val=0; }
10703: $LClastpercent=$val;
10704: unless ($text) { $text=$val.'%'; }
10705: } else {
10706: $val = 'false';
1.1042 www 10707: }
1.1041 www 10708: $text=&js_ready($text);
1.1044 www 10709: &r_print($r,<<ENDUPDATE);
1.1041 www 10710: <script type="text/javascript">
10711: // <![CDATA[
1.1302 raeburn 10712: LCupdateProgress($val,'$text','$LCcurrentid','$number_to_do');
1.1041 www 10713: // ]]>
10714: </script>
10715: ENDUPDATE
1.1035 www 10716: }
10717:
1.1042 www 10718: sub LCprogressbarClose {
10719: my ($r)=@_;
10720: $LClastpercent=0;
1.1044 www 10721: &r_print($r,<<ENDCLOSE);
1.1042 www 10722: <script type="text/javascript">
10723: // <![CDATA[
1.1045 www 10724: \$("#progressbar$LCcurrentid").hide('slow');
1.1042 www 10725: // ]]>
10726: </script>
10727: ENDCLOSE
1.1044 www 10728: }
10729:
10730: sub r_print {
10731: my ($r,$to_print)=@_;
10732: if ($r) {
10733: $r->print($to_print);
10734: $r->rflush();
10735: } else {
10736: print($to_print);
10737: }
1.1042 www 10738: }
10739:
1.320 albertel 10740: sub html_encode {
10741: my ($result) = @_;
10742:
1.322 albertel 10743: $result = &HTML::Entities::encode($result,'<>&"');
1.320 albertel 10744:
10745: return $result;
10746: }
1.1044 www 10747:
1.317 albertel 10748: sub js_ready {
10749: my ($result) = @_;
10750:
1.323 albertel 10751: $result =~ s/[\n\r]/ /xmsg;
10752: $result =~ s/\\/\\\\/xmsg;
10753: $result =~ s/'/\\'/xmsg;
1.372 albertel 10754: $result =~ s{</}{<\\/}xmsg;
1.317 albertel 10755:
10756: return $result;
10757: }
10758:
1.315 albertel 10759: sub validate_page {
10760: if ( exists($env{'internal.start_page'})
1.316 albertel 10761: && $env{'internal.start_page'} > 1) {
10762: &Apache::lonnet::logthis('start_page called multiple times '.
1.318 albertel 10763: $env{'internal.start_page'}.' '.
1.316 albertel 10764: $ENV{'request.filename'});
1.315 albertel 10765: }
10766: if ( exists($env{'internal.end_page'})
1.316 albertel 10767: && $env{'internal.end_page'} > 1) {
10768: &Apache::lonnet::logthis('end_page called multiple times '.
1.318 albertel 10769: $env{'internal.end_page'}.' '.
1.316 albertel 10770: $env{'request.filename'});
1.315 albertel 10771: }
10772: if ( exists($env{'internal.start_page'})
10773: && ! exists($env{'internal.end_page'})) {
1.316 albertel 10774: &Apache::lonnet::logthis('start_page called without end_page '.
10775: $env{'request.filename'});
1.315 albertel 10776: }
10777: if ( ! exists($env{'internal.start_page'})
10778: && exists($env{'internal.end_page'})) {
1.316 albertel 10779: &Apache::lonnet::logthis('end_page called without start_page'.
10780: $env{'request.filename'});
1.315 albertel 10781: }
1.306 albertel 10782: }
1.315 albertel 10783:
1.996 www 10784:
10785: sub start_scrollbox {
1.1140 raeburn 10786: my ($outerwidth,$width,$height,$id,$bgcolor,$cursor,$needjsready) = @_;
1.998 raeburn 10787: unless ($outerwidth) { $outerwidth='520px'; }
10788: unless ($width) { $width='500px'; }
10789: unless ($height) { $height='200px'; }
1.1075 raeburn 10790: my ($table_id,$div_id,$tdcol);
1.1018 raeburn 10791: if ($id ne '') {
1.1140 raeburn 10792: $table_id = ' id="table_'.$id.'"';
1.1137 raeburn 10793: $div_id = ' id="div_'.$id.'"';
1.1018 raeburn 10794: }
1.1075 raeburn 10795: if ($bgcolor ne '') {
10796: $tdcol = "background-color: $bgcolor;";
10797: }
1.1137 raeburn 10798: my $nicescroll_js;
10799: if ($env{'browser.mobile'}) {
1.1140 raeburn 10800: $nicescroll_js = &nicescroll_javascript('div_'.$id,$cursor,$needjsready);
10801: }
10802: return <<"END";
10803: $nicescroll_js
10804:
10805: <table style="width: $outerwidth; border: 1px solid none;"$table_id><tr><td style="width: $width;$tdcol">
10806: <div style="overflow:auto; width:$width; height:$height;"$div_id>
10807: END
10808: }
10809:
10810: sub end_scrollbox {
10811: return '</div></td></tr></table>';
10812: }
10813:
10814: sub nicescroll_javascript {
10815: my ($id,$cursor,$needjsready,$framecheck,$location) = @_;
10816: my %options;
10817: if (ref($cursor) eq 'HASH') {
10818: %options = %{$cursor};
10819: }
10820: unless ($options{'railalign'} =~ /^left|right$/) {
10821: $options{'railalign'} = 'left';
10822: }
10823: unless ($options{'cursorcolor'} =~ /^\#\w+$/) {
10824: my $function = &get_users_function();
10825: $options{'cursorcolor'} = &designparm($function.'.sidebg',$env{'request.role.domain'});
1.1138 raeburn 10826: unless ($options{'cursorcolor'} =~ /^\#\w+$/) {
1.1140 raeburn 10827: $options{'cursorcolor'} = '#00F';
1.1138 raeburn 10828: }
1.1140 raeburn 10829: }
10830: if ($options{'cursoropacity'} =~ /^[\d.]+$/) {
10831: unless ($options{'cursoropacity'} >= 0.0 && $options{'cursoropacity'} <=1.0) {
1.1138 raeburn 10832: $options{'cursoropacity'}='1.0';
10833: }
1.1140 raeburn 10834: } else {
10835: $options{'cursoropacity'}='1.0';
10836: }
10837: if ($options{'cursorfixedheight'} eq 'none') {
10838: delete($options{'cursorfixedheight'});
10839: } else {
10840: unless ($options{'cursorfixedheight'} =~ /^\d+$/) { $options{'cursorfixedheight'}='50'; }
10841: }
10842: unless ($options{'railoffset'} =~ /^{[\w\:\d\-,]+}$/) {
10843: delete($options{'railoffset'});
10844: }
10845: my @niceoptions;
10846: while (my($key,$value) = each(%options)) {
10847: if ($value =~ /^\{.+\}$/) {
10848: push(@niceoptions,$key.':'.$value);
1.1138 raeburn 10849: } else {
1.1140 raeburn 10850: push(@niceoptions,$key.':"'.$value.'"');
1.1138 raeburn 10851: }
1.1140 raeburn 10852: }
10853: my $nicescroll_js = '
1.1137 raeburn 10854: $(document).ready(
1.1140 raeburn 10855: function() {
10856: $("#'.$id.'").niceScroll({'.join(',',@niceoptions).'});
10857: }
1.1137 raeburn 10858: );
10859: ';
1.1140 raeburn 10860: if ($framecheck) {
10861: $nicescroll_js .= '
10862: function expand_div(caller) {
10863: if (top === self) {
10864: document.getElementById("'.$id.'").style.width = "auto";
10865: document.getElementById("'.$id.'").style.height = "auto";
10866: } else {
10867: try {
10868: if (parent.frames) {
10869: if (parent.frames.length > 1) {
10870: var framesrc = parent.frames[1].location.href;
10871: var currsrc = framesrc.replace(/\#.*$/,"");
10872: if ((caller == "search") || (currsrc == "'.$location.'")) {
10873: document.getElementById("'.$id.'").style.width = "auto";
10874: document.getElementById("'.$id.'").style.height = "auto";
10875: }
10876: }
10877: }
10878: } catch (e) {
10879: return;
10880: }
1.1137 raeburn 10881: }
1.1140 raeburn 10882: return;
1.996 www 10883: }
1.1140 raeburn 10884: ';
10885: }
10886: if ($needjsready) {
10887: $nicescroll_js = '
10888: <script type="text/javascript">'."\n".$nicescroll_js."\n</script>\n";
10889: } else {
10890: $nicescroll_js = &Apache::lonhtmlcommon::scripttag($nicescroll_js);
10891: }
10892: return $nicescroll_js;
1.996 www 10893: }
10894:
1.318 albertel 10895: sub simple_error_page {
1.1150 bisitz 10896: my ($r,$title,$msg,$args) = @_;
1.1304 raeburn 10897: my %displayargs;
1.1151 raeburn 10898: if (ref($args) eq 'HASH') {
10899: if (!$args->{'no_auto_mt_msg'}) { $msg = &mt($msg); }
1.1304 raeburn 10900: if ($args->{'only_body'}) {
10901: $displayargs{'only_body'} = 1;
10902: }
10903: if ($args->{'no_nav_bar'}) {
10904: $displayargs{'no_nav_bar'} = 1;
10905: }
1.1151 raeburn 10906: } else {
10907: $msg = &mt($msg);
10908: }
1.1150 bisitz 10909:
1.318 albertel 10910: my $page =
1.1459 raeburn 10911: &Apache::loncommon::start_page($title,'',\%displayargs)."\n".
1.1460 raeburn 10912: '<div class="LC_landmark" style="clear:both" role="main">'.
1.1150 bisitz 10913: '<p class="LC_error">'.$msg.'</p>'.
1.1459 raeburn 10914: '</div>'.
1.318 albertel 10915: &Apache::loncommon::end_page();
10916: if (ref($r)) {
10917: $r->print($page);
1.327 albertel 10918: return;
1.318 albertel 10919: }
10920: return $page;
10921: }
1.347 albertel 10922:
10923: {
1.610 albertel 10924: my @row_count;
1.961 onken 10925:
10926: sub start_data_table_count {
10927: unshift(@row_count, 0);
10928: return;
10929: }
10930:
10931: sub end_data_table_count {
10932: shift(@row_count);
10933: return;
10934: }
10935:
1.1466 raeburn 10936: sub set_data_table_count {
10937: my ($count) = @_;
10938: unshift(@row_count,$count);
10939: }
10940:
1.347 albertel 10941: sub start_data_table {
1.1018 raeburn 10942: my ($add_class,$id) = @_;
1.422 albertel 10943: my $css_class = (join(' ','LC_data_table',$add_class));
1.1018 raeburn 10944: my $table_id;
10945: if (defined($id)) {
10946: $table_id = ' id="'.$id.'"';
10947: }
1.961 onken 10948: &start_data_table_count();
1.1018 raeburn 10949: return '<table class="'.$css_class.'"'.$table_id.'>'."\n";
1.347 albertel 10950: }
10951:
10952: sub end_data_table {
1.961 onken 10953: &end_data_table_count();
1.389 albertel 10954: return '</table>'."\n";;
1.347 albertel 10955: }
10956:
10957: sub start_data_table_row {
1.974 wenzelju 10958: my ($add_class, $id) = @_;
1.610 albertel 10959: $row_count[0]++;
10960: my $css_class = ($row_count[0] % 2)?'LC_odd_row':'LC_even_row';
1.900 bisitz 10961: $css_class = (join(' ',$css_class,$add_class)) unless ($add_class eq '');
1.974 wenzelju 10962: $id = (' id="'.$id.'"') unless ($id eq '');
10963: return '<tr class="'.$css_class.'"'.$id.'>'."\n";
1.347 albertel 10964: }
1.471 banghart 10965:
10966: sub continue_data_table_row {
1.974 wenzelju 10967: my ($add_class, $id) = @_;
1.610 albertel 10968: my $css_class = ($row_count[0] % 2)?'LC_odd_row':'LC_even_row';
1.974 wenzelju 10969: $css_class = (join(' ',$css_class,$add_class)) unless ($add_class eq '');
10970: $id = (' id="'.$id.'"') unless ($id eq '');
10971: return '<tr class="'.$css_class.'"'.$id.'>'."\n";
1.471 banghart 10972: }
1.347 albertel 10973:
10974: sub end_data_table_row {
1.389 albertel 10975: return '</tr>'."\n";;
1.347 albertel 10976: }
1.367 www 10977:
1.421 albertel 10978: sub start_data_table_empty_row {
1.707 bisitz 10979: # $row_count[0]++;
1.421 albertel 10980: return '<tr class="LC_empty_row" >'."\n";;
10981: }
10982:
10983: sub end_data_table_empty_row {
10984: return '</tr>'."\n";;
10985: }
10986:
1.367 www 10987: sub start_data_table_header_row {
1.1465 raeburn 10988: my ($add_class,$id) = @_;
10989: my $css_class = 'LC_header_row';
10990: $css_class = (join(' ',$css_class,$add_class)) unless ($add_class eq '');
10991: $id = (' id="'.$id.'"') unless ($id eq '');
10992: return '<tr class="'.$css_class.'"'.$id.'>'."\n";
1.367 www 10993: }
10994:
10995: sub end_data_table_header_row {
1.389 albertel 10996: return '</tr>'."\n";;
1.367 www 10997: }
1.890 droeschl 10998:
10999: sub data_table_caption {
1.1468 raeburn 11000: my ($caption,$css_class) = @_;
11001: return "<caption class=\"LC_caption $css_class\">$caption</caption>";
1.890 droeschl 11002: }
1.347 albertel 11003: }
11004:
1.548 albertel 11005: =pod
11006:
11007: =item * &inhibit_menu_check($arg)
11008:
11009: Checks for a inhibitmenu state and generates output to preserve it
11010:
11011: Inputs: $arg - can be any of
11012: - undef - in which case the return value is a string
11013: to add into arguments list of a uri
11014: - 'input' - in which case the return value is a HTML
11015: <form> <input> field of type hidden to
11016: preserve the value
11017: - a url - in which case the return value is the url with
11018: the neccesary cgi args added to preserve the
11019: inhibitmenu state
11020: - a ref to a url - no return value, but the string is
11021: updated to include the neccessary cgi
11022: args to preserve the inhibitmenu state
11023:
11024: =cut
11025:
11026: sub inhibit_menu_check {
11027: my ($arg) = @_;
11028: &get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['inhibitmenu']);
11029: if ($arg eq 'input') {
11030: if ($env{'form.inhibitmenu'}) {
11031: return '<input type="hidden" name="inhibitmenu" value="'.$env{'form.inhibitmenu'}.'" />';
11032: } else {
11033: return
11034: }
11035: }
11036: if ($env{'form.inhibitmenu'}) {
11037: if (ref($arg)) {
11038: $$arg .= '?inhibitmenu='.$env{'form.inhibitmenu'};
11039: } elsif ($arg eq '') {
11040: $arg .= 'inhibitmenu='.$env{'form.inhibitmenu'};
11041: } else {
11042: $arg .= '?inhibitmenu='.$env{'form.inhibitmenu'};
11043: }
11044: }
11045: if (!ref($arg)) {
11046: return $arg;
11047: }
11048: }
11049:
1.251 albertel 11050: ###############################################
1.182 matthew 11051:
11052: =pod
11053:
1.549 albertel 11054: =back
11055:
11056: =head1 User Information Routines
11057:
11058: =over 4
11059:
1.405 albertel 11060: =item * &get_users_function()
1.182 matthew 11061:
11062: Used by &bodytag to determine the current users primary role.
11063: Returns either 'student','coordinator','admin', or 'author'.
11064:
11065: =cut
11066:
11067: ###############################################
11068: sub get_users_function {
1.815 tempelho 11069: my $function = 'norole';
1.818 tempelho 11070: if ($env{'request.role'}=~/^(st)/) {
11071: $function='student';
11072: }
1.907 raeburn 11073: if ($env{'request.role'}=~/^(cc|co|in|ta|ep)/) {
1.182 matthew 11074: $function='coordinator';
11075: }
1.258 albertel 11076: if ($env{'request.role'}=~/^(su|dc|ad|li)/) {
1.182 matthew 11077: $function='admin';
11078: }
1.826 bisitz 11079: if (($env{'request.role'}=~/^(au|ca|aa)/) ||
1.1025 raeburn 11080: ($ENV{'REQUEST_URI'}=~ m{/^(/priv)})) {
1.182 matthew 11081: $function='author';
11082: }
11083: return $function;
1.54 www 11084: }
1.99 www 11085:
11086: ###############################################
11087:
1.233 raeburn 11088: =pod
11089:
1.821 raeburn 11090: =item * &show_course()
11091:
11092: Used by lonmenu.pm and lonroles.pm to determine whether to use the word
11093: 'Courses' or 'Roles' in inline navigation and on screen displaying user's roles.
11094:
11095: Inputs:
11096: None
11097:
11098: Outputs:
11099: Scalar: 1 if 'Course' to be used, 0 otherwise.
11100:
11101: =cut
11102:
11103: ###############################################
11104: sub show_course {
1.1408 raeburn 11105: my ($udom,$uname) = @_;
11106: if (($udom ne '') && ($uname ne '')) {
11107: if (($udom ne $env{'user.domain'}) || ($uname ne $env{'user.name'})) {
1.1410 raeburn 11108: if (&Apache::lonnet::is_advanced_user($udom,$uname)) {
1.1408 raeburn 11109: return 0;
11110: } else {
11111: return 1;
11112: }
11113: }
11114: }
1.821 raeburn 11115: my $course = !$env{'user.adv'};
11116: if (!$env{'user.adv'}) {
11117: foreach my $env (keys(%env)) {
11118: next if ($env !~ m/^user\.priv\./);
11119: if ($env !~ m/^user\.priv\.(?:st|cm)/) {
11120: $course = 0;
11121: last;
11122: }
11123: }
11124: }
11125: return $course;
11126: }
11127:
11128: ###############################################
11129:
11130: =pod
11131:
1.542 raeburn 11132: =item * &check_user_status()
1.274 raeburn 11133:
11134: Determines current status of supplied role for a
11135: specific user. Roles can be active, previous or future.
11136:
11137: Inputs:
11138: user's domain, user's username, course's domain,
1.375 raeburn 11139: course's number, optional section ID.
1.274 raeburn 11140:
11141: Outputs:
11142: role status: active, previous or future.
11143:
11144: =cut
11145:
11146: sub check_user_status {
1.412 raeburn 11147: my ($udom,$uname,$cdom,$crs,$role,$sec) = @_;
1.1073 raeburn 11148: my %userinfo = &Apache::lonnet::dump('roles',$udom,$uname);
1.1202 raeburn 11149: my @uroles = keys(%userinfo);
1.274 raeburn 11150: my $srchstr;
11151: my $active_chk = 'none';
1.412 raeburn 11152: my $now = time;
1.274 raeburn 11153: if (@uroles > 0) {
1.908 raeburn 11154: if (($role eq 'cc') || ($role eq 'co') || ($sec eq '') || (!defined($sec))) {
1.274 raeburn 11155: $srchstr = '/'.$cdom.'/'.$crs.'_'.$role;
11156: } else {
1.412 raeburn 11157: $srchstr = '/'.$cdom.'/'.$crs.'/'.$sec.'_'.$role;
11158: }
11159: if (grep/^\Q$srchstr\E$/,@uroles) {
1.274 raeburn 11160: my $role_end = 0;
11161: my $role_start = 0;
11162: $active_chk = 'active';
1.412 raeburn 11163: if ($userinfo{$srchstr} =~ m/^\Q$role\E_(\d+)/) {
11164: $role_end = $1;
11165: if ($userinfo{$srchstr} =~ m/^\Q$role\E_\Q$role_end\E_(\d+)$/) {
11166: $role_start = $1;
1.274 raeburn 11167: }
11168: }
11169: if ($role_start > 0) {
1.412 raeburn 11170: if ($now < $role_start) {
1.274 raeburn 11171: $active_chk = 'future';
11172: }
11173: }
11174: if ($role_end > 0) {
1.412 raeburn 11175: if ($now > $role_end) {
1.274 raeburn 11176: $active_chk = 'previous';
11177: }
11178: }
11179: }
11180: }
11181: return $active_chk;
11182: }
11183:
11184: ###############################################
11185:
11186: =pod
11187:
1.405 albertel 11188: =item * &get_sections()
1.233 raeburn 11189:
11190: Determines all the sections for a course including
11191: sections with students and sections containing other roles.
1.419 raeburn 11192: Incoming parameters:
11193:
11194: 1. domain
11195: 2. course number
11196: 3. reference to array containing roles for which sections should
11197: be gathered (optional).
11198: 4. reference to array containing status types for which sections
11199: should be gathered (optional).
11200:
11201: If the third argument is undefined, sections are gathered for any role.
11202: If the fourth argument is undefined, sections are gathered for any status.
11203: Permissible values are 'active' or 'future' or 'previous'.
1.233 raeburn 11204:
1.374 raeburn 11205: Returns section hash (keys are section IDs, values are
11206: number of users in each section), subject to the
1.419 raeburn 11207: optional roles filter, optional status filter
1.233 raeburn 11208:
11209: =cut
11210:
11211: ###############################################
11212: sub get_sections {
1.419 raeburn 11213: my ($cdom,$cnum,$possible_roles,$possible_status) = @_;
1.366 albertel 11214: if (!defined($cdom) || !defined($cnum)) {
11215: my $cid = $env{'request.course.id'};
11216:
11217: return if (!defined($cid));
11218:
11219: $cdom = $env{'course.'.$cid.'.domain'};
11220: $cnum = $env{'course.'.$cid.'.num'};
11221: }
11222:
11223: my %sectioncount;
1.419 raeburn 11224: my $now = time;
1.240 albertel 11225:
1.1118 raeburn 11226: my $check_students = 1;
11227: my $only_students = 0;
11228: if (ref($possible_roles) eq 'ARRAY') {
11229: if (grep(/^st$/,@{$possible_roles})) {
11230: if (@{$possible_roles} == 1) {
11231: $only_students = 1;
11232: }
11233: } else {
11234: $check_students = 0;
11235: }
11236: }
11237:
11238: if ($check_students) {
1.276 albertel 11239: my ($classlist) = &Apache::loncoursedata::get_classlist($cdom,$cnum);
1.240 albertel 11240: my $sec_index = &Apache::loncoursedata::CL_SECTION();
11241: my $status_index = &Apache::loncoursedata::CL_STATUS();
1.419 raeburn 11242: my $start_index = &Apache::loncoursedata::CL_START();
11243: my $end_index = &Apache::loncoursedata::CL_END();
11244: my $status;
1.366 albertel 11245: while (my ($student,$data) = each(%$classlist)) {
1.419 raeburn 11246: my ($section,$stu_status,$start,$end) = ($data->[$sec_index],
11247: $data->[$status_index],
11248: $data->[$start_index],
11249: $data->[$end_index]);
11250: if ($stu_status eq 'Active') {
11251: $status = 'active';
11252: } elsif ($end < $now) {
11253: $status = 'previous';
11254: } elsif ($start > $now) {
11255: $status = 'future';
11256: }
11257: if ($section ne '-1' && $section !~ /^\s*$/) {
11258: if ((!defined($possible_status)) || (($status ne '') &&
11259: (grep/^\Q$status\E$/,@{$possible_status}))) {
11260: $sectioncount{$section}++;
11261: }
1.240 albertel 11262: }
11263: }
11264: }
1.1118 raeburn 11265: if ($only_students) {
11266: return %sectioncount;
11267: }
1.240 albertel 11268: my %courseroles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
11269: foreach my $user (sort(keys(%courseroles))) {
11270: if ($user !~ /^(\w{2})/) { next; }
11271: my ($role) = ($user =~ /^(\w{2})/);
11272: if ($possible_roles && !(grep(/^$role$/,@$possible_roles))) { next; }
1.419 raeburn 11273: my ($section,$status);
1.240 albertel 11274: if ($role eq 'cr' &&
11275: $user =~ m-^$role/[^/]*/[^/]*/[^/]*:[^:]*:[^:]*:(\w+)-) {
11276: $section=$1;
11277: }
11278: if ($user =~ /^$role:[^:]*:[^:]*:(\w+)/) { $section=$1; }
11279: if (!defined($section) || $section eq '-1') { next; }
1.419 raeburn 11280: my ($end,$start) = ($courseroles{$user} =~ /^([^:]*):([^:]*)$/);
11281: if ($end == -1 && $start == -1) {
11282: next; #deleted role
11283: }
11284: if (!defined($possible_status)) {
11285: $sectioncount{$section}++;
11286: } else {
11287: if ((!$end || $end >= $now) && (!$start || $start <= $now)) {
11288: $status = 'active';
11289: } elsif ($end < $now) {
11290: $status = 'future';
11291: } elsif ($start > $now) {
11292: $status = 'previous';
11293: }
11294: if (($status ne '') && (grep/^\Q$status\E$/,@{$possible_status})) {
11295: $sectioncount{$section}++;
11296: }
11297: }
1.233 raeburn 11298: }
1.366 albertel 11299: return %sectioncount;
1.233 raeburn 11300: }
11301:
1.274 raeburn 11302: ###############################################
1.294 raeburn 11303:
11304: =pod
1.405 albertel 11305:
11306: =item * &get_course_users()
11307:
1.275 raeburn 11308: Retrieves usernames:domains for users in the specified course
11309: with specific role(s), and access status.
11310:
11311: Incoming parameters:
1.277 albertel 11312: 1. course domain
11313: 2. course number
11314: 3. access status: users must have - either active,
1.275 raeburn 11315: previous, future, or all.
1.277 albertel 11316: 4. reference to array of permissible roles
1.288 raeburn 11317: 5. reference to array of section restrictions (optional)
11318: 6. reference to results object (hash of hashes).
11319: 7. reference to optional userdata hash
1.609 raeburn 11320: 8. reference to optional statushash
1.630 raeburn 11321: 9. flag if privileged users (except those set to unhide in
11322: course settings) should be excluded
1.609 raeburn 11323: Keys of top level results hash are roles.
1.275 raeburn 11324: Keys of inner hashes are username:domain, with
11325: values set to access type.
1.288 raeburn 11326: Optional userdata hash returns an array with arguments in the
11327: same order as loncoursedata::get_classlist() for student data.
11328:
1.609 raeburn 11329: Optional statushash returns
11330:
1.288 raeburn 11331: Entries for end, start, section and status are blank because
11332: of the possibility of multiple values for non-student roles.
11333:
1.275 raeburn 11334: =cut
1.405 albertel 11335:
1.275 raeburn 11336: ###############################################
1.405 albertel 11337:
1.275 raeburn 11338: sub get_course_users {
1.630 raeburn 11339: my ($cdom,$cnum,$types,$roles,$sections,$users,$userdata,$statushash,$hidepriv) = @_;
1.288 raeburn 11340: my %idx = ();
1.419 raeburn 11341: my %seclists;
1.288 raeburn 11342:
11343: $idx{udom} = &Apache::loncoursedata::CL_SDOM();
11344: $idx{uname} = &Apache::loncoursedata::CL_SNAME();
11345: $idx{end} = &Apache::loncoursedata::CL_END();
11346: $idx{start} = &Apache::loncoursedata::CL_START();
11347: $idx{id} = &Apache::loncoursedata::CL_ID();
11348: $idx{section} = &Apache::loncoursedata::CL_SECTION();
11349: $idx{fullname} = &Apache::loncoursedata::CL_FULLNAME();
11350: $idx{status} = &Apache::loncoursedata::CL_STATUS();
11351:
1.290 albertel 11352: if (grep(/^st$/,@{$roles})) {
1.276 albertel 11353: my ($classlist,$keylist)=&Apache::loncoursedata::get_classlist($cdom,$cnum);
1.278 raeburn 11354: my $now = time;
1.277 albertel 11355: foreach my $student (keys(%{$classlist})) {
1.288 raeburn 11356: my $match = 0;
1.412 raeburn 11357: my $secmatch = 0;
1.419 raeburn 11358: my $section = $$classlist{$student}[$idx{section}];
1.609 raeburn 11359: my $status = $$classlist{$student}[$idx{status}];
1.419 raeburn 11360: if ($section eq '') {
11361: $section = 'none';
11362: }
1.291 albertel 11363: if ((ref($sections) eq 'ARRAY') && (@{$sections} > 0)) {
1.420 albertel 11364: if (grep(/^all$/,@{$sections})) {
1.412 raeburn 11365: $secmatch = 1;
11366: } elsif ($$classlist{$student}[$idx{section}] eq '') {
1.420 albertel 11367: if (grep(/^none$/,@{$sections})) {
1.412 raeburn 11368: $secmatch = 1;
11369: }
11370: } else {
1.419 raeburn 11371: if (grep(/^\Q$section\E$/,@{$sections})) {
1.412 raeburn 11372: $secmatch = 1;
11373: }
1.290 albertel 11374: }
1.412 raeburn 11375: if (!$secmatch) {
11376: next;
11377: }
1.419 raeburn 11378: }
1.275 raeburn 11379: if (defined($$types{'active'})) {
1.288 raeburn 11380: if ($$classlist{$student}[$idx{status}] eq 'Active') {
1.275 raeburn 11381: push(@{$$users{st}{$student}},'active');
1.288 raeburn 11382: $match = 1;
1.275 raeburn 11383: }
11384: }
11385: if (defined($$types{'previous'})) {
1.609 raeburn 11386: if ($$classlist{$student}[$idx{status}] eq 'Expired') {
1.275 raeburn 11387: push(@{$$users{st}{$student}},'previous');
1.288 raeburn 11388: $match = 1;
1.275 raeburn 11389: }
11390: }
11391: if (defined($$types{'future'})) {
1.609 raeburn 11392: if ($$classlist{$student}[$idx{status}] eq 'Future') {
1.275 raeburn 11393: push(@{$$users{st}{$student}},'future');
1.288 raeburn 11394: $match = 1;
1.275 raeburn 11395: }
11396: }
1.609 raeburn 11397: if ($match) {
11398: push(@{$seclists{$student}},$section);
11399: if (ref($userdata) eq 'HASH') {
11400: $$userdata{$student} = $$classlist{$student};
11401: }
11402: if (ref($statushash) eq 'HASH') {
11403: $statushash->{$student}{'st'}{$section} = $status;
11404: }
1.288 raeburn 11405: }
1.275 raeburn 11406: }
11407: }
1.412 raeburn 11408: if ((@{$roles} > 1) || ((@{$roles} == 1) && ($$roles[0] ne "st"))) {
1.439 raeburn 11409: my %coursepersonnel = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
11410: my $now = time;
1.609 raeburn 11411: my %displaystatus = ( previous => 'Expired',
11412: active => 'Active',
11413: future => 'Future',
11414: );
1.1121 raeburn 11415: my (%nothide,@possdoms);
1.630 raeburn 11416: if ($hidepriv) {
11417: my %coursehash=&Apache::lonnet::coursedescription($cdom.'_'.$cnum);
11418: foreach my $user (split(/\s*\,\s*/,$coursehash{'nothideprivileged'})) {
11419: if ($user !~ /:/) {
11420: $nothide{join(':',split(/[\@]/,$user))}=1;
11421: } else {
11422: $nothide{$user} = 1;
11423: }
11424: }
1.1121 raeburn 11425: my @possdoms = ($cdom);
11426: if ($coursehash{'checkforpriv'}) {
11427: push(@possdoms,split(/,/,$coursehash{'checkforpriv'}));
11428: }
1.630 raeburn 11429: }
1.439 raeburn 11430: foreach my $person (sort(keys(%coursepersonnel))) {
1.288 raeburn 11431: my $match = 0;
1.412 raeburn 11432: my $secmatch = 0;
1.439 raeburn 11433: my $status;
1.412 raeburn 11434: my ($role,$user,$usec) = ($person =~ /^([^:]*):([^:]+:[^:]+):([^:]*)/);
1.275 raeburn 11435: $user =~ s/:$//;
1.439 raeburn 11436: my ($end,$start) = split(/:/,$coursepersonnel{$person});
11437: if ($end == -1 || $start == -1) {
11438: next;
11439: }
11440: if (($role) && ((grep(/^\Q$role\E$/,@{$roles})) ||
11441: (grep(/^cr$/,@{$roles}) && $role =~ /^cr\//))) {
1.412 raeburn 11442: my ($uname,$udom) = split(/:/,$user);
11443: if ((ref($sections) eq 'ARRAY') && (@{$sections} > 0)) {
1.420 albertel 11444: if (grep(/^all$/,@{$sections})) {
1.412 raeburn 11445: $secmatch = 1;
11446: } elsif ($usec eq '') {
1.420 albertel 11447: if (grep(/^none$/,@{$sections})) {
1.412 raeburn 11448: $secmatch = 1;
11449: }
11450: } else {
11451: if (grep(/^\Q$usec\E$/,@{$sections})) {
11452: $secmatch = 1;
11453: }
11454: }
11455: if (!$secmatch) {
11456: next;
11457: }
1.288 raeburn 11458: }
1.419 raeburn 11459: if ($usec eq '') {
11460: $usec = 'none';
11461: }
1.275 raeburn 11462: if ($uname ne '' && $udom ne '') {
1.630 raeburn 11463: if ($hidepriv) {
1.1121 raeburn 11464: if ((&Apache::lonnet::privileged($uname,$udom,\@possdoms)) &&
1.630 raeburn 11465: (!$nothide{$uname.':'.$udom})) {
11466: next;
11467: }
11468: }
1.503 raeburn 11469: if ($end > 0 && $end < $now) {
1.439 raeburn 11470: $status = 'previous';
11471: } elsif ($start > $now) {
11472: $status = 'future';
11473: } else {
11474: $status = 'active';
11475: }
1.277 albertel 11476: foreach my $type (keys(%{$types})) {
1.275 raeburn 11477: if ($status eq $type) {
1.420 albertel 11478: if (!grep(/^\Q$type\E$/,@{$$users{$role}{$user}})) {
1.419 raeburn 11479: push(@{$$users{$role}{$user}},$type);
11480: }
1.288 raeburn 11481: $match = 1;
11482: }
11483: }
1.419 raeburn 11484: if (($match) && (ref($userdata) eq 'HASH')) {
11485: if (!exists($$userdata{$uname.':'.$udom})) {
11486: &get_user_info($udom,$uname,\%idx,$userdata);
11487: }
1.420 albertel 11488: if (!grep(/^\Q$usec\E$/,@{$seclists{$uname.':'.$udom}})) {
1.419 raeburn 11489: push(@{$seclists{$uname.':'.$udom}},$usec);
11490: }
1.609 raeburn 11491: if (ref($statushash) eq 'HASH') {
11492: $statushash->{$uname.':'.$udom}{$role}{$usec} = $displaystatus{$status};
11493: }
1.275 raeburn 11494: }
11495: }
11496: }
11497: }
1.290 albertel 11498: if (grep(/^ow$/,@{$roles})) {
1.279 raeburn 11499: if ((defined($cdom)) && (defined($cnum))) {
11500: my %csettings = &Apache::lonnet::get('environment',['internal.courseowner'],$cdom,$cnum);
11501: if ( defined($csettings{'internal.courseowner'}) ) {
11502: my $owner = $csettings{'internal.courseowner'};
1.609 raeburn 11503: next if ($owner eq '');
11504: my ($ownername,$ownerdom);
11505: if ($owner =~ /^([^:]+):([^:]+)$/) {
11506: $ownername = $1;
11507: $ownerdom = $2;
11508: } else {
11509: $ownername = $owner;
11510: $ownerdom = $cdom;
11511: $owner = $ownername.':'.$ownerdom;
1.439 raeburn 11512: }
11513: @{$$users{'ow'}{$owner}} = 'any';
1.290 albertel 11514: if (defined($userdata) &&
1.609 raeburn 11515: !exists($$userdata{$owner})) {
11516: &get_user_info($ownerdom,$ownername,\%idx,$userdata);
11517: if (!grep(/^none$/,@{$seclists{$owner}})) {
11518: push(@{$seclists{$owner}},'none');
11519: }
11520: if (ref($statushash) eq 'HASH') {
11521: $statushash->{$owner}{'ow'}{'none'} = 'Any';
1.419 raeburn 11522: }
1.290 albertel 11523: }
1.279 raeburn 11524: }
11525: }
11526: }
1.419 raeburn 11527: foreach my $user (keys(%seclists)) {
11528: @{$seclists{$user}} = (sort {$a <=> $b} @{$seclists{$user}});
11529: $$userdata{$user}[$idx{section}] = join(',',@{$seclists{$user}});
11530: }
1.275 raeburn 11531: }
11532: return;
11533: }
11534:
1.288 raeburn 11535: sub get_user_info {
11536: my ($udom,$uname,$idx,$userdata) = @_;
1.289 albertel 11537: $$userdata{$uname.':'.$udom}[$$idx{fullname}] =
11538: &plainname($uname,$udom,'lastname');
1.291 albertel 11539: $$userdata{$uname.':'.$udom}[$$idx{uname}] = $uname;
1.297 raeburn 11540: $$userdata{$uname.':'.$udom}[$$idx{udom}] = $udom;
1.609 raeburn 11541: my %idhash = &Apache::lonnet::idrget($udom,($uname));
11542: $$userdata{$uname.':'.$udom}[$$idx{id}] = $idhash{$uname};
1.288 raeburn 11543: return;
11544: }
1.275 raeburn 11545:
1.472 raeburn 11546: ###############################################
11547:
11548: =pod
11549:
11550: =item * &get_user_quota()
11551:
1.1134 raeburn 11552: Retrieves quota assigned for storage of user files.
11553: Default is to report quota for portfolio files.
1.472 raeburn 11554:
11555: Incoming parameters:
11556: 1. user's username
11557: 2. user's domain
1.1134 raeburn 11558: 3. quota name - portfolio, author, or course
1.1136 raeburn 11559: (if no quota name provided, defaults to portfolio).
1.1237 raeburn 11560: 4. crstype - official, unofficial, textbook, placement or community,
11561: if quota name is course
1.472 raeburn 11562:
11563: Returns:
1.1163 raeburn 11564: 1. Disk quota (in MB) assigned to student.
1.536 raeburn 11565: 2. (Optional) Type of setting: custom or default
11566: (individually assigned or default for user's
11567: institutional status).
11568: 3. (Optional) - User's institutional status (e.g., faculty, staff
11569: or student - types as defined in localenroll::inst_usertypes
11570: for user's domain, which determines default quota for user.
11571: 4. (Optional) - Default quota which would apply to the user.
1.472 raeburn 11572:
11573: If a value has been stored in the user's environment,
1.536 raeburn 11574: it will return that, otherwise it returns the maximal default
1.1134 raeburn 11575: defined for the user's institutional status(es) in the domain.
1.472 raeburn 11576:
11577: =cut
11578:
11579: ###############################################
11580:
11581:
11582: sub get_user_quota {
1.1136 raeburn 11583: my ($uname,$udom,$quotaname,$crstype) = @_;
1.536 raeburn 11584: my ($quota,$quotatype,$settingstatus,$defquota);
1.472 raeburn 11585: if (!defined($udom)) {
11586: $udom = $env{'user.domain'};
11587: }
11588: if (!defined($uname)) {
11589: $uname = $env{'user.name'};
11590: }
11591: if (($udom eq '' || $uname eq '') ||
11592: ($udom eq 'public') && ($uname eq 'public')) {
11593: $quota = 0;
1.536 raeburn 11594: $quotatype = 'default';
11595: $defquota = 0;
1.472 raeburn 11596: } else {
1.536 raeburn 11597: my $inststatus;
1.1134 raeburn 11598: if ($quotaname eq 'course') {
11599: if (($env{'course.'.$udom.'_'.$uname.'.num'} eq $uname) &&
11600: ($env{'course.'.$udom.'_'.$uname.'.domain'} eq $udom)) {
11601: $quota = $env{'course.'.$udom.'_'.$uname.'.internal.uploadquota'};
11602: } else {
11603: my %cenv = &Apache::lonnet::coursedescription("$udom/$uname");
11604: $quota = $cenv{'internal.uploadquota'};
11605: }
1.536 raeburn 11606: } else {
1.1134 raeburn 11607: if ($udom eq $env{'user.domain'} && $uname eq $env{'user.name'}) {
11608: if ($quotaname eq 'author') {
11609: $quota = $env{'environment.authorquota'};
11610: } else {
11611: $quota = $env{'environment.portfolioquota'};
11612: }
11613: $inststatus = $env{'environment.inststatus'};
11614: } else {
11615: my %userenv =
11616: &Apache::lonnet::get('environment',['portfolioquota',
11617: 'authorquota','inststatus'],$udom,$uname);
11618: my ($tmp) = keys(%userenv);
11619: if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
11620: if ($quotaname eq 'author') {
11621: $quota = $userenv{'authorquota'};
11622: } else {
11623: $quota = $userenv{'portfolioquota'};
11624: }
11625: $inststatus = $userenv{'inststatus'};
11626: } else {
11627: undef(%userenv);
11628: }
11629: }
11630: }
11631: if ($quota eq '' || wantarray) {
11632: if ($quotaname eq 'course') {
11633: my %domdefs = &Apache::lonnet::get_domain_defaults($udom);
1.1165 raeburn 11634: if (($crstype eq 'official') || ($crstype eq 'unofficial') ||
1.1237 raeburn 11635: ($crstype eq 'community') || ($crstype eq 'textbook') ||
11636: ($crstype eq 'placement')) {
1.1136 raeburn 11637: $defquota = $domdefs{$crstype.'quota'};
11638: }
11639: if ($defquota eq '') {
11640: $defquota = 500;
11641: }
1.1134 raeburn 11642: } else {
11643: ($defquota,$settingstatus) = &default_quota($udom,$inststatus,$quotaname);
11644: }
11645: if ($quota eq '') {
11646: $quota = $defquota;
11647: $quotatype = 'default';
11648: } else {
11649: $quotatype = 'custom';
11650: }
1.472 raeburn 11651: }
11652: }
1.536 raeburn 11653: if (wantarray) {
11654: return ($quota,$quotatype,$settingstatus,$defquota);
11655: } else {
11656: return $quota;
11657: }
1.472 raeburn 11658: }
11659:
11660: ###############################################
11661:
11662: =pod
11663:
11664: =item * &default_quota()
11665:
1.536 raeburn 11666: Retrieves default quota assigned for storage of user portfolio files,
11667: given an (optional) user's institutional status.
1.472 raeburn 11668:
11669: Incoming parameters:
1.1142 raeburn 11670:
1.472 raeburn 11671: 1. domain
1.536 raeburn 11672: 2. (Optional) institutional status(es). This is a : separated list of
11673: status types (e.g., faculty, staff, student etc.)
11674: which apply to the user for whom the default is being retrieved.
11675: If the institutional status string in undefined, the domain
1.1134 raeburn 11676: default quota will be returned.
11677: 3. quota name - portfolio, author, or course
11678: (if no quota name provided, defaults to portfolio).
1.472 raeburn 11679:
11680: Returns:
1.1142 raeburn 11681:
1.1163 raeburn 11682: 1. Default disk quota (in MB) for user portfolios in the domain.
1.536 raeburn 11683: 2. (Optional) institutional type which determined the value of the
11684: default quota.
1.472 raeburn 11685:
11686: If a value has been stored in the domain's configuration db,
11687: it will return that, otherwise it returns 20 (for backwards
11688: compatibility with domains which have not set up a configuration
1.1163 raeburn 11689: db file; the original statically defined portfolio quota was 20 MB).
1.472 raeburn 11690:
1.536 raeburn 11691: If the user's status includes multiple types (e.g., staff and student),
11692: the largest default quota which applies to the user determines the
11693: default quota returned.
11694:
1.472 raeburn 11695: =cut
11696:
11697: ###############################################
11698:
11699:
11700: sub default_quota {
1.1134 raeburn 11701: my ($udom,$inststatus,$quotaname) = @_;
1.536 raeburn 11702: my ($defquota,$settingstatus);
11703: my %quotahash = &Apache::lonnet::get_dom('configuration',
1.622 raeburn 11704: ['quotas'],$udom);
1.1134 raeburn 11705: my $key = 'defaultquota';
11706: if ($quotaname eq 'author') {
11707: $key = 'authorquota';
11708: }
1.622 raeburn 11709: if (ref($quotahash{'quotas'}) eq 'HASH') {
1.536 raeburn 11710: if ($inststatus ne '') {
1.765 raeburn 11711: my @statuses = map { &unescape($_); } split(/:/,$inststatus);
1.536 raeburn 11712: foreach my $item (@statuses) {
1.1134 raeburn 11713: if (ref($quotahash{'quotas'}{$key}) eq 'HASH') {
11714: if ($quotahash{'quotas'}{$key}{$item} ne '') {
1.711 raeburn 11715: if ($defquota eq '') {
1.1134 raeburn 11716: $defquota = $quotahash{'quotas'}{$key}{$item};
1.711 raeburn 11717: $settingstatus = $item;
1.1134 raeburn 11718: } elsif ($quotahash{'quotas'}{$key}{$item} > $defquota) {
11719: $defquota = $quotahash{'quotas'}{$key}{$item};
1.711 raeburn 11720: $settingstatus = $item;
11721: }
11722: }
1.1134 raeburn 11723: } elsif ($key eq 'defaultquota') {
1.711 raeburn 11724: if ($quotahash{'quotas'}{$item} ne '') {
11725: if ($defquota eq '') {
11726: $defquota = $quotahash{'quotas'}{$item};
11727: $settingstatus = $item;
11728: } elsif ($quotahash{'quotas'}{$item} > $defquota) {
11729: $defquota = $quotahash{'quotas'}{$item};
11730: $settingstatus = $item;
11731: }
1.536 raeburn 11732: }
11733: }
11734: }
11735: }
11736: if ($defquota eq '') {
1.1134 raeburn 11737: if (ref($quotahash{'quotas'}{$key}) eq 'HASH') {
11738: $defquota = $quotahash{'quotas'}{$key}{'default'};
11739: } elsif ($key eq 'defaultquota') {
1.711 raeburn 11740: $defquota = $quotahash{'quotas'}{'default'};
11741: }
1.536 raeburn 11742: $settingstatus = 'default';
1.1139 raeburn 11743: if ($defquota eq '') {
11744: if ($quotaname eq 'author') {
11745: $defquota = 500;
11746: }
11747: }
1.536 raeburn 11748: }
11749: } else {
11750: $settingstatus = 'default';
1.1134 raeburn 11751: if ($quotaname eq 'author') {
11752: $defquota = 500;
11753: } else {
11754: $defquota = 20;
11755: }
1.536 raeburn 11756: }
11757: if (wantarray) {
11758: return ($defquota,$settingstatus);
1.472 raeburn 11759: } else {
1.536 raeburn 11760: return $defquota;
1.472 raeburn 11761: }
11762: }
11763:
1.1135 raeburn 11764: ###############################################
11765:
11766: =pod
11767:
1.1136 raeburn 11768: =item * &excess_filesize_warning()
1.1135 raeburn 11769:
11770: Returns warning message if upload of file to authoring space, or copying
1.1136 raeburn 11771: of existing file within authoring space will cause quota for the authoring
1.1146 raeburn 11772: space to be exceeded.
1.1136 raeburn 11773:
11774: Same, if upload of a file directly to a course/community via Course Editor
1.1137 raeburn 11775: will cause quota for uploaded content for the course to be exceeded.
1.1135 raeburn 11776:
1.1165 raeburn 11777: Inputs: 7
1.1136 raeburn 11778: 1. username or coursenum
1.1135 raeburn 11779: 2. domain
1.1136 raeburn 11780: 3. context ('author' or 'course')
1.1135 raeburn 11781: 4. filename of file for which action is being requested
11782: 5. filesize (kB) of file
11783: 6. action being taken: copy or upload.
1.1237 raeburn 11784: 7. quotatype (in course context -- official, unofficial, textbook, placement or community).
1.1135 raeburn 11785:
11786: Returns: 1 scalar: HTML to display containing warning if quota would be exceeded,
1.1142 raeburn 11787: otherwise return null.
11788:
11789: =back
1.1135 raeburn 11790:
11791: =cut
11792:
1.1136 raeburn 11793: sub excess_filesize_warning {
1.1165 raeburn 11794: my ($uname,$udom,$context,$filename,$filesize,$action,$quotatype) = @_;
1.1136 raeburn 11795: my $current_disk_usage = 0;
1.1165 raeburn 11796: my $disk_quota = &get_user_quota($uname,$udom,$context,$quotatype); #expressed in MB
1.1136 raeburn 11797: if ($context eq 'author') {
11798: my $authorspace = $Apache::lonnet::perlvar{'lonDocRoot'}."/priv/$udom/$uname";
11799: $current_disk_usage = &Apache::lonnet::diskusage($udom,$uname,$authorspace);
11800: } else {
11801: foreach my $subdir ('docs','supplemental') {
11802: $current_disk_usage += &Apache::lonnet::diskusage($udom,$uname,"userfiles/$subdir",1);
11803: }
11804: }
1.1135 raeburn 11805: $disk_quota = int($disk_quota * 1000);
11806: if (($current_disk_usage + $filesize) > $disk_quota) {
1.1179 bisitz 11807: return '<p class="LC_warning">'.
1.1135 raeburn 11808: &mt("Unable to $action [_1]. (size = [_2] kilobytes). Disk quota will be exceeded.",
1.1179 bisitz 11809: '<span class="LC_filename">'.$filename.'</span>',$filesize).'</p>'.
11810: '<p>'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',
1.1135 raeburn 11811: $disk_quota,$current_disk_usage).
11812: '</p>';
11813: }
11814: return;
11815: }
11816:
11817: ###############################################
11818:
11819:
1.1136 raeburn 11820:
11821:
1.384 raeburn 11822: sub get_secgrprole_info {
11823: my ($cdom,$cnum,$needroles,$type) = @_;
11824: my %sections_count = &get_sections($cdom,$cnum);
11825: my @sections = (sort {$a <=> $b} keys(%sections_count));
11826: my %curr_groups = &Apache::longroup::coursegroups($cdom,$cnum);
11827: my @groups = sort(keys(%curr_groups));
11828: my $allroles = [];
11829: my $rolehash;
11830: my $accesshash = {
11831: active => 'Currently has access',
11832: future => 'Will have future access',
11833: previous => 'Previously had access',
11834: };
11835: if ($needroles) {
11836: $rolehash = {'all' => 'all'};
1.385 albertel 11837: my %user_roles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
11838: if (&Apache::lonnet::error(%user_roles)) {
11839: undef(%user_roles);
11840: }
11841: foreach my $item (keys(%user_roles)) {
1.384 raeburn 11842: my ($role)=split(/\:/,$item,2);
11843: if ($role eq 'cr') { next; }
11844: if ($role =~ /^cr/) {
11845: $$rolehash{$role} = (split('/',$role))[3];
11846: } else {
11847: $$rolehash{$role} = &Apache::lonnet::plaintext($role,$type);
11848: }
11849: }
11850: foreach my $key (sort(keys(%{$rolehash}))) {
11851: push(@{$allroles},$key);
11852: }
11853: push (@{$allroles},'st');
11854: $$rolehash{'st'} = &Apache::lonnet::plaintext('st',$type);
11855: }
11856: return (\@sections,\@groups,$allroles,$rolehash,$accesshash);
11857: }
11858:
1.555 raeburn 11859: sub user_picker {
1.1279 raeburn 11860: my ($dom,$srch,$forcenewuser,$caller,$cancreate,$usertype,$context,$fixeddom,$noinstd) = @_;
1.555 raeburn 11861: my $currdom = $dom;
1.1253 raeburn 11862: my @alldoms = &Apache::lonnet::all_domains();
11863: if (@alldoms == 1) {
11864: my %domsrch = &Apache::lonnet::get_dom('configuration',
11865: ['directorysrch'],$alldoms[0]);
11866: my $domdesc = &Apache::lonnet::domain($alldoms[0],'description');
11867: my $showdom = $domdesc;
11868: if ($showdom eq '') {
11869: $showdom = $dom;
11870: }
11871: if (ref($domsrch{'directorysrch'}) eq 'HASH') {
11872: if ((!$domsrch{'directorysrch'}{'available'}) &&
11873: ($domsrch{'directorysrch'}{'lcavailable'} eq '0')) {
11874: return (&mt('LON-CAPA directory search is not available in domain: [_1]',$showdom),0);
11875: }
11876: }
11877: }
1.555 raeburn 11878: my %curr_selected = (
11879: srchin => 'dom',
1.580 raeburn 11880: srchby => 'lastname',
1.555 raeburn 11881: );
11882: my $srchterm;
1.625 raeburn 11883: if ((ref($srch) eq 'HASH') && ($env{'form.origform'} ne 'crtusername')) {
1.555 raeburn 11884: if ($srch->{'srchby'} ne '') {
11885: $curr_selected{'srchby'} = $srch->{'srchby'};
11886: }
11887: if ($srch->{'srchin'} ne '') {
11888: $curr_selected{'srchin'} = $srch->{'srchin'};
11889: }
11890: if ($srch->{'srchtype'} ne '') {
11891: $curr_selected{'srchtype'} = $srch->{'srchtype'};
11892: }
11893: if ($srch->{'srchdomain'} ne '') {
11894: $currdom = $srch->{'srchdomain'};
11895: }
11896: $srchterm = $srch->{'srchterm'};
11897: }
1.1222 damieng 11898: my %html_lt=&Apache::lonlocal::texthash(
1.573 raeburn 11899: 'usr' => 'Search criteria',
1.563 raeburn 11900: 'doma' => 'Domain/institution to search',
1.558 albertel 11901: 'uname' => 'username',
11902: 'lastname' => 'last name',
1.555 raeburn 11903: 'lastfirst' => 'last name, first name',
1.558 albertel 11904: 'crs' => 'in this course',
1.576 raeburn 11905: 'dom' => 'in selected LON-CAPA domain',
1.558 albertel 11906: 'alc' => 'all LON-CAPA',
1.573 raeburn 11907: 'instd' => 'in institutional directory for selected domain',
1.558 albertel 11908: 'exact' => 'is',
11909: 'contains' => 'contains',
1.569 raeburn 11910: 'begins' => 'begins with',
1.1222 damieng 11911: );
11912: my %js_lt=&Apache::lonlocal::texthash(
1.571 raeburn 11913: 'youm' => "You must include some text to search for.",
11914: 'thte' => "The text you are searching for must contain at least two characters when using a 'begins' type search.",
11915: 'thet' => "The text you are searching for must contain at least three characters when using a 'contains' type search.",
11916: 'yomc' => "You must choose a domain when using an institutional directory search.",
11917: 'ymcd' => "You must choose a domain when using a domain search.",
11918: 'whus' => "When using searching by last,first you must include a comma as separator between last name and first name.",
11919: 'whse' => "When searching by last,first you must include at least one character in the first name.",
11920: 'thfo' => "The following need to be corrected before the search can be run:",
1.555 raeburn 11921: );
1.1222 damieng 11922: &html_escape(\%html_lt);
11923: &js_escape(\%js_lt);
1.1255 raeburn 11924: my $domform;
1.1277 raeburn 11925: my $allow_blank = 1;
1.1255 raeburn 11926: if ($fixeddom) {
1.1277 raeburn 11927: $allow_blank = 0;
11928: $domform = &select_dom_form($currdom,'srchdomain',$allow_blank,1,undef,[$currdom]);
1.1255 raeburn 11929: } else {
1.1287 raeburn 11930: my $defdom = $env{'request.role.domain'};
1.1288 raeburn 11931: my ($trusted,$untrusted);
1.1287 raeburn 11932: if (($context eq 'requestcrs') || ($context eq 'course')) {
1.1288 raeburn 11933: ($trusted,$untrusted) = &Apache::lonnet::trusted_domains('enroll',$defdom);
1.1287 raeburn 11934: } elsif ($context eq 'author') {
1.1288 raeburn 11935: ($trusted,$untrusted) = &Apache::lonnet::trusted_domains('othcoau',$defdom);
1.1287 raeburn 11936: } elsif ($context eq 'domain') {
1.1288 raeburn 11937: ($trusted,$untrusted) = &Apache::lonnet::trusted_domains('domroles',$defdom);
1.1287 raeburn 11938: }
1.1288 raeburn 11939: $domform = &select_dom_form($currdom,'srchdomain',$allow_blank,1,undef,$trusted,$untrusted);
1.1255 raeburn 11940: }
1.563 raeburn 11941: my $srchinsel = ' <select name="srchin">';
1.555 raeburn 11942:
11943: my @srchins = ('crs','dom','alc','instd');
11944:
11945: foreach my $option (@srchins) {
11946: # FIXME 'alc' option unavailable until
11947: # loncreateuser::print_user_query_page()
11948: # has been completed.
11949: next if ($option eq 'alc');
1.880 raeburn 11950: next if (($option eq 'crs') && ($env{'form.form'} eq 'requestcrs'));
1.555 raeburn 11951: next if ($option eq 'crs' && !$env{'request.course.id'});
1.1279 raeburn 11952: next if (($option eq 'instd') && ($noinstd));
1.563 raeburn 11953: if ($curr_selected{'srchin'} eq $option) {
11954: $srchinsel .= '
1.1222 damieng 11955: <option value="'.$option.'" selected="selected">'.$html_lt{$option}.'</option>';
1.563 raeburn 11956: } else {
11957: $srchinsel .= '
1.1222 damieng 11958: <option value="'.$option.'">'.$html_lt{$option}.'</option>';
1.563 raeburn 11959: }
1.555 raeburn 11960: }
1.563 raeburn 11961: $srchinsel .= "\n </select>\n";
1.555 raeburn 11962:
11963: my $srchbysel = ' <select name="srchby">';
1.580 raeburn 11964: foreach my $option ('lastname','lastfirst','uname') {
1.555 raeburn 11965: if ($curr_selected{'srchby'} eq $option) {
11966: $srchbysel .= '
1.1222 damieng 11967: <option value="'.$option.'" selected="selected">'.$html_lt{$option}.'</option>';
1.555 raeburn 11968: } else {
11969: $srchbysel .= '
1.1222 damieng 11970: <option value="'.$option.'">'.$html_lt{$option}.'</option>';
1.555 raeburn 11971: }
11972: }
11973: $srchbysel .= "\n </select>\n";
11974:
11975: my $srchtypesel = ' <select name="srchtype">';
1.580 raeburn 11976: foreach my $option ('begins','contains','exact') {
1.555 raeburn 11977: if ($curr_selected{'srchtype'} eq $option) {
11978: $srchtypesel .= '
1.1222 damieng 11979: <option value="'.$option.'" selected="selected">'.$html_lt{$option}.'</option>';
1.555 raeburn 11980: } else {
11981: $srchtypesel .= '
1.1222 damieng 11982: <option value="'.$option.'">'.$html_lt{$option}.'</option>';
1.555 raeburn 11983: }
11984: }
11985: $srchtypesel .= "\n </select>\n";
11986:
1.558 albertel 11987: my ($newuserscript,$new_user_create);
1.994 raeburn 11988: my $context_dom = $env{'request.role.domain'};
11989: if ($context eq 'requestcrs') {
11990: if ($env{'form.coursedom'} ne '') {
11991: $context_dom = $env{'form.coursedom'};
11992: }
11993: }
1.556 raeburn 11994: if ($forcenewuser) {
1.576 raeburn 11995: if (ref($srch) eq 'HASH') {
1.994 raeburn 11996: if ($srch->{'srchby'} eq 'uname' && $srch->{'srchtype'} eq 'exact' && $srch->{'srchin'} eq 'dom' && $srch->{'srchdomain'} eq $context_dom) {
1.627 raeburn 11997: if ($cancreate) {
11998: $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>';
11999: } else {
1.799 bisitz 12000: my $helplink = 'javascript:helpMenu('."'display'".')';
1.627 raeburn 12001: my %usertypetext = (
12002: official => 'institutional',
12003: unofficial => 'non-institutional',
12004: );
1.799 bisitz 12005: $new_user_create = '<p class="LC_warning">'
12006: .&mt("You are not authorized to create new $usertypetext{$usertype} users in this domain.")
12007: .' '
12008: .&mt('Please contact the [_1]helpdesk[_2] for assistance.'
12009: ,'<a href="'.$helplink.'">','</a>')
12010: .'</p><br />';
1.627 raeburn 12011: }
1.576 raeburn 12012: }
12013: }
12014:
1.556 raeburn 12015: $newuserscript = <<"ENDSCRIPT";
12016:
1.570 raeburn 12017: function setSearch(createnew,callingForm) {
1.556 raeburn 12018: if (createnew == 1) {
1.570 raeburn 12019: for (var i=0; i<callingForm.srchby.length; i++) {
12020: if (callingForm.srchby.options[i].value == 'uname') {
12021: callingForm.srchby.selectedIndex = i;
1.556 raeburn 12022: }
12023: }
1.570 raeburn 12024: for (var i=0; i<callingForm.srchin.length; i++) {
12025: if ( callingForm.srchin.options[i].value == 'dom') {
12026: callingForm.srchin.selectedIndex = i;
1.556 raeburn 12027: }
12028: }
1.570 raeburn 12029: for (var i=0; i<callingForm.srchtype.length; i++) {
12030: if (callingForm.srchtype.options[i].value == 'exact') {
12031: callingForm.srchtype.selectedIndex = i;
1.556 raeburn 12032: }
12033: }
1.570 raeburn 12034: for (var i=0; i<callingForm.srchdomain.length; i++) {
1.994 raeburn 12035: if (callingForm.srchdomain.options[i].value == '$context_dom') {
1.570 raeburn 12036: callingForm.srchdomain.selectedIndex = i;
1.556 raeburn 12037: }
12038: }
12039: }
12040: }
12041: ENDSCRIPT
1.558 albertel 12042:
1.556 raeburn 12043: }
12044:
1.555 raeburn 12045: my $output = <<"END_BLOCK";
1.556 raeburn 12046: <script type="text/javascript">
1.824 bisitz 12047: // <![CDATA[
1.570 raeburn 12048: function validateEntry(callingForm) {
1.558 albertel 12049:
1.556 raeburn 12050: var checkok = 1;
1.558 albertel 12051: var srchin;
1.570 raeburn 12052: for (var i=0; i<callingForm.srchin.length; i++) {
12053: if ( callingForm.srchin[i].checked ) {
12054: srchin = callingForm.srchin[i].value;
1.558 albertel 12055: }
12056: }
12057:
1.570 raeburn 12058: var srchtype = callingForm.srchtype.options[callingForm.srchtype.selectedIndex].value;
12059: var srchby = callingForm.srchby.options[callingForm.srchby.selectedIndex].value;
12060: var srchdomain = callingForm.srchdomain.options[callingForm.srchdomain.selectedIndex].value;
12061: var srchterm = callingForm.srchterm.value;
12062: var srchin = callingForm.srchin.options[callingForm.srchin.selectedIndex].value;
1.556 raeburn 12063: var msg = "";
12064:
12065: if (srchterm == "") {
12066: checkok = 0;
1.1222 damieng 12067: msg += "$js_lt{'youm'}\\n";
1.556 raeburn 12068: }
12069:
1.569 raeburn 12070: if (srchtype== 'begins') {
12071: if (srchterm.length < 2) {
12072: checkok = 0;
1.1222 damieng 12073: msg += "$js_lt{'thte'}\\n";
1.569 raeburn 12074: }
12075: }
12076:
1.556 raeburn 12077: if (srchtype== 'contains') {
12078: if (srchterm.length < 3) {
12079: checkok = 0;
1.1222 damieng 12080: msg += "$js_lt{'thet'}\\n";
1.556 raeburn 12081: }
12082: }
12083: if (srchin == 'instd') {
12084: if (srchdomain == '') {
12085: checkok = 0;
1.1222 damieng 12086: msg += "$js_lt{'yomc'}\\n";
1.556 raeburn 12087: }
12088: }
12089: if (srchin == 'dom') {
12090: if (srchdomain == '') {
12091: checkok = 0;
1.1222 damieng 12092: msg += "$js_lt{'ymcd'}\\n";
1.556 raeburn 12093: }
12094: }
12095: if (srchby == 'lastfirst') {
12096: if (srchterm.indexOf(",") == -1) {
12097: checkok = 0;
1.1222 damieng 12098: msg += "$js_lt{'whus'}\\n";
1.556 raeburn 12099: }
12100: if (srchterm.indexOf(",") == srchterm.length -1) {
12101: checkok = 0;
1.1222 damieng 12102: msg += "$js_lt{'whse'}\\n";
1.556 raeburn 12103: }
12104: }
12105: if (checkok == 0) {
1.1222 damieng 12106: alert("$js_lt{'thfo'}\\n"+msg);
1.556 raeburn 12107: return;
12108: }
12109: if (checkok == 1) {
1.570 raeburn 12110: callingForm.submit();
1.556 raeburn 12111: }
12112: }
12113:
12114: $newuserscript
12115:
1.824 bisitz 12116: // ]]>
1.556 raeburn 12117: </script>
1.558 albertel 12118:
12119: $new_user_create
12120:
1.555 raeburn 12121: END_BLOCK
1.558 albertel 12122:
1.876 raeburn 12123: $output .= &Apache::lonhtmlcommon::start_pick_box().
1.1222 damieng 12124: &Apache::lonhtmlcommon::row_title($html_lt{'doma'}).
1.876 raeburn 12125: $domform.
12126: &Apache::lonhtmlcommon::row_closure().
1.1222 damieng 12127: &Apache::lonhtmlcommon::row_title($html_lt{'usr'}).
1.876 raeburn 12128: $srchbysel.
12129: $srchtypesel.
12130: '<input type="text" size="15" name="srchterm" value="'.$srchterm.'" />'.
12131: $srchinsel.
12132: &Apache::lonhtmlcommon::row_closure(1).
12133: &Apache::lonhtmlcommon::end_pick_box().
12134: '<br />';
1.1253 raeburn 12135: return ($output,1);
1.555 raeburn 12136: }
12137:
1.612 raeburn 12138: sub user_rule_check {
1.615 raeburn 12139: my ($usershash,$checks,$alerts,$rulematch,$inst_results,$curr_rules,$got_rules) = @_;
1.1226 raeburn 12140: my ($response,%inst_response);
1.612 raeburn 12141: if (ref($usershash) eq 'HASH') {
1.1226 raeburn 12142: if (keys(%{$usershash}) > 1) {
12143: my (%by_username,%by_id,%userdoms);
12144: my $checkid;
12145: if (ref($checks) eq 'HASH') {
12146: if ((!defined($checks->{'username'})) && (defined($checks->{'id'}))) {
12147: $checkid = 1;
12148: }
12149: }
12150: foreach my $user (keys(%{$usershash})) {
12151: my ($uname,$udom) = split(/:/,$user);
12152: if ($checkid) {
12153: if (ref($usershash->{$user}) eq 'HASH') {
12154: if ($usershash->{$user}->{'id'} ne '') {
1.1227 raeburn 12155: $by_id{$udom}{$usershash->{$user}->{'id'}} = $uname;
1.1226 raeburn 12156: $userdoms{$udom} = 1;
1.1227 raeburn 12157: if (ref($inst_results) eq 'HASH') {
12158: $inst_results->{$uname.':'.$udom} = {};
12159: }
1.1226 raeburn 12160: }
12161: }
12162: } else {
12163: $by_username{$udom}{$uname} = 1;
12164: $userdoms{$udom} = 1;
1.1227 raeburn 12165: if (ref($inst_results) eq 'HASH') {
12166: $inst_results->{$uname.':'.$udom} = {};
12167: }
1.1226 raeburn 12168: }
12169: }
12170: foreach my $udom (keys(%userdoms)) {
12171: if (!$got_rules->{$udom}) {
12172: my %domconfig = &Apache::lonnet::get_dom('configuration',
12173: ['usercreation'],$udom);
12174: if (ref($domconfig{'usercreation'}) eq 'HASH') {
12175: foreach my $item ('username','id') {
12176: if (ref($domconfig{'usercreation'}{$item.'_rule'}) eq 'ARRAY') {
1.1227 raeburn 12177: $$curr_rules{$udom}{$item} =
12178: $domconfig{'usercreation'}{$item.'_rule'};
1.1226 raeburn 12179: }
12180: }
12181: }
12182: $got_rules->{$udom} = 1;
12183: }
1.612 raeburn 12184: }
1.1226 raeburn 12185: if ($checkid) {
12186: foreach my $udom (keys(%by_id)) {
12187: my ($outcome,$results) = &Apache::lonnet::get_multiple_instusers($udom,$by_id{$udom},'id');
12188: if ($outcome eq 'ok') {
1.1227 raeburn 12189: foreach my $id (keys(%{$by_id{$udom}})) {
12190: my $uname = $by_id{$udom}{$id};
12191: $inst_response{$uname.':'.$udom} = $outcome;
12192: }
1.1226 raeburn 12193: if (ref($results) eq 'HASH') {
12194: foreach my $uname (keys(%{$results})) {
1.1227 raeburn 12195: if (exists($inst_response{$uname.':'.$udom})) {
12196: $inst_response{$uname.':'.$udom} = $outcome;
12197: $inst_results->{$uname.':'.$udom} = $results->{$uname};
12198: }
1.1226 raeburn 12199: }
12200: }
12201: }
1.612 raeburn 12202: }
1.615 raeburn 12203: } else {
1.1226 raeburn 12204: foreach my $udom (keys(%by_username)) {
12205: my ($outcome,$results) = &Apache::lonnet::get_multiple_instusers($udom,$by_username{$udom});
12206: if ($outcome eq 'ok') {
1.1227 raeburn 12207: foreach my $uname (keys(%{$by_username{$udom}})) {
12208: $inst_response{$uname.':'.$udom} = $outcome;
12209: }
1.1226 raeburn 12210: if (ref($results) eq 'HASH') {
12211: foreach my $uname (keys(%{$results})) {
12212: $inst_results->{$uname.':'.$udom} = $results->{$uname};
12213: }
12214: }
12215: }
12216: }
1.612 raeburn 12217: }
1.1226 raeburn 12218: } elsif (keys(%{$usershash}) == 1) {
12219: my $user = (keys(%{$usershash}))[0];
12220: my ($uname,$udom) = split(/:/,$user);
12221: if (($udom ne '') && ($uname ne '')) {
12222: if (ref($usershash->{$user}) eq 'HASH') {
12223: if (ref($checks) eq 'HASH') {
12224: if (defined($checks->{'username'})) {
12225: ($inst_response{$user},%{$inst_results->{$user}}) =
12226: &Apache::lonnet::get_instuser($udom,$uname);
12227: } elsif (defined($checks->{'id'})) {
12228: if ($usershash->{$user}->{'id'} ne '') {
12229: ($inst_response{$user},%{$inst_results->{$user}}) =
12230: &Apache::lonnet::get_instuser($udom,undef,
12231: $usershash->{$user}->{'id'});
12232: } else {
12233: ($inst_response{$user},%{$inst_results->{$user}}) =
12234: &Apache::lonnet::get_instuser($udom,$uname);
12235: }
1.585 raeburn 12236: }
1.1226 raeburn 12237: } else {
12238: ($inst_response{$user},%{$inst_results->{$user}}) =
12239: &Apache::lonnet::get_instuser($udom,$uname);
12240: return;
12241: }
12242: if (!$got_rules->{$udom}) {
12243: my %domconfig = &Apache::lonnet::get_dom('configuration',
12244: ['usercreation'],$udom);
12245: if (ref($domconfig{'usercreation'}) eq 'HASH') {
12246: foreach my $item ('username','id') {
12247: if (ref($domconfig{'usercreation'}{$item.'_rule'}) eq 'ARRAY') {
12248: $$curr_rules{$udom}{$item} =
12249: $domconfig{'usercreation'}{$item.'_rule'};
12250: }
12251: }
12252: }
12253: $got_rules->{$udom} = 1;
1.585 raeburn 12254: }
12255: }
1.1226 raeburn 12256: } else {
12257: return;
12258: }
12259: } else {
12260: return;
12261: }
12262: foreach my $user (keys(%{$usershash})) {
12263: my ($uname,$udom) = split(/:/,$user);
12264: next if (($udom eq '') || ($uname eq ''));
12265: my $id;
1.1227 raeburn 12266: if (ref($inst_results) eq 'HASH') {
12267: if (ref($inst_results->{$user}) eq 'HASH') {
12268: $id = $inst_results->{$user}->{'id'};
12269: }
12270: }
12271: if ($id eq '') {
12272: if (ref($usershash->{$user})) {
12273: $id = $usershash->{$user}->{'id'};
12274: }
1.585 raeburn 12275: }
1.612 raeburn 12276: foreach my $item (keys(%{$checks})) {
12277: if (ref($$curr_rules{$udom}) eq 'HASH') {
12278: if (ref($$curr_rules{$udom}{$item}) eq 'ARRAY') {
12279: if (@{$$curr_rules{$udom}{$item}} > 0) {
1.1226 raeburn 12280: my %rule_check = &Apache::lonnet::inst_rulecheck($udom,$uname,$id,$item,
12281: $$curr_rules{$udom}{$item});
1.612 raeburn 12282: foreach my $rule (@{$$curr_rules{$udom}{$item}}) {
12283: if ($rule_check{$rule}) {
12284: $$rulematch{$user}{$item} = $rule;
1.1226 raeburn 12285: if ($inst_response{$user} eq 'ok') {
1.615 raeburn 12286: if (ref($inst_results) eq 'HASH') {
12287: if (ref($inst_results->{$user}) eq 'HASH') {
12288: if (keys(%{$inst_results->{$user}}) == 0) {
12289: $$alerts{$item}{$udom}{$uname} = 1;
1.1227 raeburn 12290: } elsif ($item eq 'id') {
12291: if ($inst_results->{$user}->{'id'} eq '') {
12292: $$alerts{$item}{$udom}{$uname} = 1;
12293: }
1.615 raeburn 12294: }
1.612 raeburn 12295: }
12296: }
1.615 raeburn 12297: }
12298: last;
1.585 raeburn 12299: }
12300: }
12301: }
12302: }
12303: }
12304: }
12305: }
12306: }
1.612 raeburn 12307: return;
12308: }
12309:
12310: sub user_rule_formats {
12311: my ($domain,$domdesc,$curr_rules,$check) = @_;
12312: my %text = (
12313: 'username' => 'Usernames',
12314: 'id' => 'IDs',
12315: );
12316: my $output;
12317: my ($rules,$ruleorder) = &Apache::lonnet::inst_userrules($domain,$check);
12318: if ((ref($rules) eq 'HASH') && (ref($ruleorder) eq 'ARRAY')) {
12319: if (@{$ruleorder} > 0) {
1.1102 raeburn 12320: $output = '<br />'.
12321: &mt($text{$check}.' with the following format(s) may [_1]only[_2] be used for verified users at [_3]:',
12322: '<span class="LC_cusr_emph">','</span>',$domdesc).
12323: ' <ul>';
1.612 raeburn 12324: foreach my $rule (@{$ruleorder}) {
12325: if (ref($curr_rules) eq 'ARRAY') {
12326: if (grep(/^\Q$rule\E$/,@{$curr_rules})) {
12327: if (ref($rules->{$rule}) eq 'HASH') {
12328: $output .= '<li>'.$rules->{$rule}{'name'}.': '.
12329: $rules->{$rule}{'desc'}.'</li>';
12330: }
12331: }
12332: }
12333: }
12334: $output .= '</ul>';
12335: }
12336: }
12337: return $output;
12338: }
12339:
12340: sub instrule_disallow_msg {
1.615 raeburn 12341: my ($checkitem,$domdesc,$count,$mode) = @_;
1.612 raeburn 12342: my $response;
12343: my %text = (
12344: item => 'username',
12345: items => 'usernames',
12346: match => 'matches',
12347: do => 'does',
12348: action => 'a username',
12349: one => 'one',
12350: );
12351: if ($count > 1) {
12352: $text{'item'} = 'usernames';
12353: $text{'match'} ='match';
12354: $text{'do'} = 'do';
12355: $text{'action'} = 'usernames',
12356: $text{'one'} = 'ones';
12357: }
12358: if ($checkitem eq 'id') {
12359: $text{'items'} = 'IDs';
12360: $text{'item'} = 'ID';
12361: $text{'action'} = 'an ID';
1.615 raeburn 12362: if ($count > 1) {
12363: $text{'item'} = 'IDs';
12364: $text{'action'} = 'IDs';
12365: }
1.612 raeburn 12366: }
1.674 bisitz 12367: $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 12368: if ($mode eq 'upload') {
12369: if ($checkitem eq 'username') {
12370: $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'}.");
12371: } elsif ($checkitem eq 'id') {
1.674 bisitz 12372: $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 12373: }
1.669 raeburn 12374: } elsif ($mode eq 'selfcreate') {
12375: if ($checkitem eq 'id') {
12376: $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.");
12377: }
1.615 raeburn 12378: } else {
12379: if ($checkitem eq 'username') {
12380: $response .= &mt("You must choose $text{'action'} with a different format -- $text{'one'} that will not conflict with 'official' institutional $text{'items'}.");
12381: } elsif ($checkitem eq 'id') {
12382: $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.");
12383: }
1.612 raeburn 12384: }
12385: return $response;
1.585 raeburn 12386: }
12387:
1.624 raeburn 12388: sub personal_data_fieldtitles {
12389: my %fieldtitles = &Apache::lonlocal::texthash (
12390: id => 'Student/Employee ID',
12391: permanentemail => 'E-mail address',
12392: lastname => 'Last Name',
12393: firstname => 'First Name',
12394: middlename => 'Middle Name',
12395: generation => 'Generation',
12396: gen => 'Generation',
1.765 raeburn 12397: inststatus => 'Affiliation',
1.624 raeburn 12398: );
12399: return %fieldtitles;
12400: }
12401:
1.642 raeburn 12402: sub sorted_inst_types {
12403: my ($dom) = @_;
1.1185 raeburn 12404: my ($usertypes,$order);
12405: my %domdefaults = &Apache::lonnet::get_domain_defaults($dom);
12406: if (ref($domdefaults{'inststatus'}) eq 'HASH') {
12407: $usertypes = $domdefaults{'inststatus'}{'inststatustypes'};
12408: $order = $domdefaults{'inststatus'}{'inststatusorder'};
12409: } else {
12410: ($usertypes,$order) = &Apache::lonnet::retrieve_inst_usertypes($dom);
12411: }
1.642 raeburn 12412: my $othertitle = &mt('All users');
12413: if ($env{'request.course.id'}) {
1.668 raeburn 12414: $othertitle = &mt('Any users');
1.642 raeburn 12415: }
12416: my @types;
12417: if (ref($order) eq 'ARRAY') {
12418: @types = @{$order};
12419: }
12420: if (@types == 0) {
12421: if (ref($usertypes) eq 'HASH') {
12422: @types = sort(keys(%{$usertypes}));
12423: }
12424: }
12425: if (keys(%{$usertypes}) > 0) {
12426: $othertitle = &mt('Other users');
12427: }
12428: return ($othertitle,$usertypes,\@types);
12429: }
12430:
1.645 raeburn 12431: sub get_institutional_codes {
1.1361 raeburn 12432: my ($cdom,$crs,$settings,$allcourses,$LC_code) = @_;
1.645 raeburn 12433: # Get complete list of course sections to update
12434: my @currsections = ();
12435: my @currxlists = ();
1.1361 raeburn 12436: my (%unclutteredsec,%unclutteredlcsec);
1.645 raeburn 12437: my $coursecode = $$settings{'internal.coursecode'};
1.1361 raeburn 12438: my $crskey = $crs.':'.$coursecode;
12439: @{$unclutteredsec{$crskey}} = ();
12440: @{$unclutteredlcsec{$crskey}} = ();
1.645 raeburn 12441:
12442: if ($$settings{'internal.sectionnums'} ne '') {
12443: @currsections = split(/,/,$$settings{'internal.sectionnums'});
12444: }
12445:
12446: if ($$settings{'internal.crosslistings'} ne '') {
12447: @currxlists = split(/,/,$$settings{'internal.crosslistings'});
12448: }
12449:
12450: if (@currxlists > 0) {
1.1361 raeburn 12451: foreach my $xl (@currxlists) {
12452: if ($xl =~ /^([^:]+):(\w*)$/) {
1.645 raeburn 12453: unless (grep/^$1$/,@{$allcourses}) {
1.1263 raeburn 12454: push(@{$allcourses},$1);
1.645 raeburn 12455: $$LC_code{$1} = $2;
12456: }
12457: }
12458: }
12459: }
1.1361 raeburn 12460:
1.645 raeburn 12461: if (@currsections > 0) {
1.1361 raeburn 12462: foreach my $sec (@currsections) {
12463: if ($sec =~ m/^(\w+):(\w*)$/ ) {
12464: my $instsec = $1;
1.645 raeburn 12465: my $lc_sec = $2;
1.1361 raeburn 12466: unless (grep/^\Q$instsec\E$/,@{$unclutteredsec{$crskey}}) {
12467: push(@{$unclutteredsec{$crskey}},$instsec);
12468: push(@{$unclutteredlcsec{$crskey}},$lc_sec);
12469: }
12470: }
12471: }
12472: }
12473:
12474: if (@{$unclutteredsec{$crskey}} > 0) {
12475: my %formattedsec = &Apache::lonnet::auto_instsec_reformat($cdom,'clutter',\%unclutteredsec);
12476: if ((ref($formattedsec{$crskey}) eq 'ARRAY') && (ref($unclutteredlcsec{$crskey}) eq 'ARRAY')) {
12477: for (my $i=0; $i<@{$formattedsec{$crskey}}; $i++) {
12478: my $sec = $coursecode.$formattedsec{$crskey}[$i];
12479: unless (grep/^\Q$sec\E$/,@{$allcourses}) {
1.1263 raeburn 12480: push(@{$allcourses},$sec);
1.1361 raeburn 12481: $$LC_code{$sec} = $unclutteredlcsec{$crskey}[$i];
1.645 raeburn 12482: }
12483: }
12484: }
12485: }
12486: return;
12487: }
12488:
1.971 raeburn 12489: sub get_standard_codeitems {
12490: return ('Year','Semester','Department','Number','Section');
12491: }
12492:
1.112 bowersj2 12493: =pod
12494:
1.780 raeburn 12495: =head1 Slot Helpers
12496:
12497: =over 4
12498:
12499: =item * sorted_slots()
12500:
1.1040 raeburn 12501: Sorts an array of slot names in order of an optional sort key,
12502: default sort is by slot start time (earliest first).
1.780 raeburn 12503:
12504: Inputs:
12505:
12506: =over 4
12507:
12508: slotsarr - Reference to array of unsorted slot names.
12509:
12510: slots - Reference to hash of hash, where outer hash keys are slot names.
12511:
1.1040 raeburn 12512: sortkey - Name of key in inner hash to be sorted on (e.g., starttime).
12513:
1.549 albertel 12514: =back
12515:
1.780 raeburn 12516: Returns:
12517:
12518: =over 4
12519:
1.1040 raeburn 12520: sorted - An array of slot names sorted by a specified sort key
12521: (default sort key is start time of the slot).
1.780 raeburn 12522:
12523: =back
12524:
12525: =cut
12526:
12527:
12528: sub sorted_slots {
1.1040 raeburn 12529: my ($slotsarr,$slots,$sortkey) = @_;
12530: if ($sortkey eq '') {
12531: $sortkey = 'starttime';
12532: }
1.780 raeburn 12533: my @sorted;
12534: if ((ref($slotsarr) eq 'ARRAY') && (ref($slots) eq 'HASH')) {
12535: @sorted =
12536: sort {
12537: if (ref($slots->{$a}) && ref($slots->{$b})) {
1.1040 raeburn 12538: return $slots->{$a}{$sortkey} <=> $slots->{$b}{$sortkey}
1.780 raeburn 12539: }
12540: if (ref($slots->{$a})) { return -1;}
12541: if (ref($slots->{$b})) { return 1;}
12542: return 0;
12543: } @{$slotsarr};
12544: }
12545: return @sorted;
12546: }
12547:
1.1040 raeburn 12548: =pod
12549:
12550: =item * get_future_slots()
12551:
12552: Inputs:
12553:
12554: =over 4
12555:
12556: cnum - course number
12557:
12558: cdom - course domain
12559:
12560: now - current UNIX time
12561:
12562: symb - optional symb
12563:
12564: =back
12565:
12566: Returns:
12567:
12568: =over 4
12569:
12570: sorted_reservable - ref to array of student_schedulable slots currently
12571: reservable, ordered by end date of reservation period.
12572:
12573: reservable_now - ref to hash of student_schedulable slots currently
12574: reservable.
12575:
12576: Keys in inner hash are:
12577: (a) symb: either blank or symb to which slot use is restricted.
1.1250 raeburn 12578: (b) endreserve: end date of reservation period.
12579: (c) uniqueperiod: start,end dates when slot is to be uniquely
12580: selected.
1.1040 raeburn 12581:
12582: sorted_future - ref to array of student_schedulable slots reservable in
12583: the future, ordered by start date of reservation period.
12584:
12585: future_reservable - ref to hash of student_schedulable slots reservable
12586: in the future.
12587:
12588: Keys in inner hash are:
12589: (a) symb: either blank or symb to which slot use is restricted.
1.1250 raeburn 12590: (b) startreserve: start date of reservation period.
12591: (c) uniqueperiod: start,end dates when slot is to be uniquely
12592: selected.
1.1040 raeburn 12593:
12594: =back
12595:
12596: =cut
12597:
12598: sub get_future_slots {
12599: my ($cnum,$cdom,$now,$symb) = @_;
1.1229 raeburn 12600: my $map;
12601: if ($symb) {
12602: ($map) = &Apache::lonnet::decode_symb($symb);
12603: }
1.1040 raeburn 12604: my (%reservable_now,%future_reservable,@sorted_reservable,@sorted_future);
12605: my %slots = &Apache::lonnet::get_course_slots($cnum,$cdom);
12606: foreach my $slot (keys(%slots)) {
12607: next unless($slots{$slot}->{'type'} eq 'schedulable_student');
12608: if ($symb) {
1.1229 raeburn 12609: if ($slots{$slot}->{'symb'} ne '') {
12610: my $canuse;
12611: my %oksymbs;
12612: my @slotsymbs = split(/\s*,\s*/,$slots{$slot}->{'symb'});
12613: map { $oksymbs{$_} = 1; } @slotsymbs;
12614: if ($oksymbs{$symb}) {
12615: $canuse = 1;
12616: } else {
12617: foreach my $item (@slotsymbs) {
12618: if ($item =~ /\.(page|sequence)$/) {
12619: (undef,undef,my $sloturl) = &Apache::lonnet::decode_symb($item);
12620: if (($map ne '') && ($map eq $sloturl)) {
12621: $canuse = 1;
12622: last;
12623: }
12624: }
12625: }
12626: }
12627: next unless ($canuse);
12628: }
1.1040 raeburn 12629: }
12630: if (($slots{$slot}->{'starttime'} > $now) &&
12631: ($slots{$slot}->{'endtime'} > $now)) {
12632: if (($slots{$slot}->{'allowedsections'}) || ($slots{$slot}->{'allowedusers'})) {
12633: my $userallowed = 0;
12634: if ($slots{$slot}->{'allowedsections'}) {
12635: my @allowed_sec = split(',',$slots{$slot}->{'allowedsections'});
12636: if (!defined($env{'request.role.sec'})
12637: && grep(/^No section assigned$/,@allowed_sec)) {
12638: $userallowed=1;
12639: } else {
12640: if (grep(/^\Q$env{'request.role.sec'}\E$/,@allowed_sec)) {
12641: $userallowed=1;
12642: }
12643: }
12644: unless ($userallowed) {
12645: if (defined($env{'request.course.groups'})) {
12646: my @groups = split(/:/,$env{'request.course.groups'});
12647: foreach my $group (@groups) {
12648: if (grep(/^\Q$group\E$/,@allowed_sec)) {
12649: $userallowed=1;
12650: last;
12651: }
12652: }
12653: }
12654: }
12655: }
12656: if ($slots{$slot}->{'allowedusers'}) {
12657: my @allowed_users = split(',',$slots{$slot}->{'allowedusers'});
12658: my $user = $env{'user.name'}.':'.$env{'user.domain'};
12659: if (grep(/^\Q$user\E$/,@allowed_users)) {
12660: $userallowed = 1;
12661: }
12662: }
12663: next unless($userallowed);
12664: }
12665: my $startreserve = $slots{$slot}->{'startreserve'};
12666: my $endreserve = $slots{$slot}->{'endreserve'};
12667: my $symb = $slots{$slot}->{'symb'};
1.1250 raeburn 12668: my $uniqueperiod;
12669: if (ref($slots{$slot}->{'uniqueperiod'}) eq 'ARRAY') {
12670: $uniqueperiod = join(',',@{$slots{$slot}->{'uniqueperiod'}});
12671: }
1.1040 raeburn 12672: if (($startreserve < $now) &&
12673: (!$endreserve || $endreserve > $now)) {
12674: my $lastres = $endreserve;
12675: if (!$lastres) {
12676: $lastres = $slots{$slot}->{'starttime'};
12677: }
12678: $reservable_now{$slot} = {
12679: symb => $symb,
1.1250 raeburn 12680: endreserve => $lastres,
12681: uniqueperiod => $uniqueperiod,
1.1040 raeburn 12682: };
12683: } elsif (($startreserve > $now) &&
12684: (!$endreserve || $endreserve > $startreserve)) {
12685: $future_reservable{$slot} = {
12686: symb => $symb,
1.1250 raeburn 12687: startreserve => $startreserve,
12688: uniqueperiod => $uniqueperiod,
1.1040 raeburn 12689: };
12690: }
12691: }
12692: }
12693: my @unsorted_reservable = keys(%reservable_now);
12694: if (@unsorted_reservable > 0) {
12695: @sorted_reservable =
12696: &sorted_slots(\@unsorted_reservable,\%reservable_now,'endreserve');
12697: }
12698: my @unsorted_future = keys(%future_reservable);
12699: if (@unsorted_future > 0) {
12700: @sorted_future =
12701: &sorted_slots(\@unsorted_future,\%future_reservable,'startreserve');
12702: }
12703: return (\@sorted_reservable,\%reservable_now,\@sorted_future,\%future_reservable);
12704: }
1.780 raeburn 12705:
12706: =pod
12707:
1.1057 foxr 12708: =back
12709:
1.549 albertel 12710: =head1 HTTP Helpers
12711:
12712: =over 4
12713:
1.648 raeburn 12714: =item * &get_unprocessed_cgi($query,$possible_names)
1.112 bowersj2 12715:
1.258 albertel 12716: Modify the %env hash to contain unprocessed CGI form parameters held in
1.112 bowersj2 12717: $query. The parameters listed in $possible_names (an array reference),
1.258 albertel 12718: will be set in $env{'form.name'} if they do not already exist.
1.112 bowersj2 12719:
12720: Typically called with $ENV{'QUERY_STRING'} as the first parameter.
12721: $possible_names is an ref to an array of form element names. As an example:
12722: get_unprocessed_cgi($ENV{'QUERY_STRING'},['uname','udom']);
1.258 albertel 12723: will result in $env{'form.uname'} and $env{'form.udom'} being set.
1.112 bowersj2 12724:
12725: =cut
1.1 albertel 12726:
1.6 albertel 12727: sub get_unprocessed_cgi {
1.25 albertel 12728: my ($query,$possible_names)= @_;
1.26 matthew 12729: # $Apache::lonxml::debug=1;
1.356 albertel 12730: foreach my $pair (split(/&/,$query)) {
12731: my ($name, $value) = split(/=/,$pair);
1.369 www 12732: $name = &unescape($name);
1.25 albertel 12733: if (!defined($possible_names) || (grep {$_ eq $name} @$possible_names)) {
12734: $value =~ tr/+/ /;
12735: $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
1.258 albertel 12736: unless (defined($env{'form.'.$name})) { &add_to_env('form.'.$name,$value) };
1.25 albertel 12737: }
1.16 harris41 12738: }
1.6 albertel 12739: }
12740:
1.112 bowersj2 12741: =pod
12742:
1.648 raeburn 12743: =item * &cacheheader()
1.112 bowersj2 12744:
12745: returns cache-controlling header code
12746:
12747: =cut
12748:
1.7 albertel 12749: sub cacheheader {
1.258 albertel 12750: unless ($env{'request.method'} eq 'GET') { return ''; }
1.216 albertel 12751: my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime);
12752: my $output .='<meta HTTP-EQUIV="Expires" CONTENT="'.$date.'" />
1.7 albertel 12753: <meta HTTP-EQUIV="Cache-control" CONTENT="no-cache" />
12754: <meta HTTP-EQUIV="Pragma" CONTENT="no-cache" />';
1.216 albertel 12755: return $output;
1.7 albertel 12756: }
12757:
1.112 bowersj2 12758: =pod
12759:
1.648 raeburn 12760: =item * &no_cache($r)
1.112 bowersj2 12761:
12762: specifies header code to not have cache
12763:
12764: =cut
12765:
1.9 albertel 12766: sub no_cache {
1.216 albertel 12767: my ($r) = @_;
12768: if ($ENV{'REQUEST_METHOD'} ne 'GET' &&
1.258 albertel 12769: $env{'request.method'} ne 'GET') { return ''; }
1.216 albertel 12770: my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime(time));
12771: $r->no_cache(1);
12772: $r->header_out("Expires" => $date);
12773: $r->header_out("Pragma" => "no-cache");
1.123 www 12774: }
12775:
12776: sub content_type {
1.181 albertel 12777: my ($r,$type,$charset) = @_;
1.299 foxr 12778: if ($r) {
12779: # Note that printout.pl calls this with undef for $r.
12780: &no_cache($r);
12781: }
1.258 albertel 12782: if ($env{'browser.mathml'} && $type eq 'text/html') { $type='text/xml'; }
1.181 albertel 12783: unless ($charset) {
12784: $charset=&Apache::lonlocal::current_encoding;
12785: }
12786: if ($charset) { $type.='; charset='.$charset; }
12787: if ($r) {
12788: $r->content_type($type);
12789: } else {
12790: print("Content-type: $type\n\n");
12791: }
1.9 albertel 12792: }
1.25 albertel 12793:
1.112 bowersj2 12794: =pod
12795:
1.648 raeburn 12796: =item * &add_to_env($name,$value)
1.112 bowersj2 12797:
1.258 albertel 12798: adds $name to the %env hash with value
1.112 bowersj2 12799: $value, if $name already exists, the entry is converted to an array
12800: reference and $value is added to the array.
12801:
12802: =cut
12803:
1.25 albertel 12804: sub add_to_env {
12805: my ($name,$value)=@_;
1.258 albertel 12806: if (defined($env{$name})) {
12807: if (ref($env{$name})) {
1.25 albertel 12808: #already have multiple values
1.258 albertel 12809: push(@{ $env{$name} },$value);
1.25 albertel 12810: } else {
12811: #first time seeing multiple values, convert hash entry to an arrayref
1.258 albertel 12812: my $first=$env{$name};
12813: undef($env{$name});
12814: push(@{ $env{$name} },$first,$value);
1.25 albertel 12815: }
12816: } else {
1.258 albertel 12817: $env{$name}=$value;
1.25 albertel 12818: }
1.31 albertel 12819: }
1.149 albertel 12820:
12821: =pod
12822:
1.648 raeburn 12823: =item * &get_env_multiple($name)
1.149 albertel 12824:
1.258 albertel 12825: gets $name from the %env hash, it seemlessly handles the cases where multiple
1.149 albertel 12826: values may be defined and end up as an array ref.
12827:
12828: returns an array of values
12829:
12830: =cut
12831:
12832: sub get_env_multiple {
12833: my ($name) = @_;
12834: my @values;
1.258 albertel 12835: if (defined($env{$name})) {
1.149 albertel 12836: # exists is it an array
1.258 albertel 12837: if (ref($env{$name})) {
12838: @values=@{ $env{$name} };
1.149 albertel 12839: } else {
1.258 albertel 12840: $values[0]=$env{$name};
1.149 albertel 12841: }
12842: }
12843: return(@values);
12844: }
12845:
1.1249 damieng 12846: # Looks at given dependencies, and returns something depending on the context.
12847: # For coursedocs paste, returns (undef, $counter, $numpathchg, \%existing).
12848: # For syllabus rewrites, returns (undef, $counter, $numpathchg, \%existing, \%mapping).
12849: # For all other contexts, returns ($output, $counter, $numpathchg).
12850: # $output: string with the HTML output. Can contain missing dependencies with an upload form, existing dependencies, and dependencies no longer in use.
12851: # $counter: integer with the number of existing dependencies when no HTML output is returned, and the number of missing dependencies when an HTML output is returned.
12852: # $numpathchg: integer with the number of cleaned up dependency paths.
12853: # \%existing: hash reference clean path -> 1 only for existing dependencies.
12854: # \%mapping: hash reference clean path -> original path for all dependencies.
12855: # @param {string} actionurl - The path to the handler, indicative of the context.
12856: # @param {string} state - Can contain HTML with hidden inputs that will be added to the output form.
12857: # @param {hash reference} allfiles - List of file info from lonnet::extract_embedded_items
12858: # @param {hash reference} codebase - undef, not modified by lonnet::extract_embedded_items ?
12859: # @param {hash reference} args - More parameters ! Possible keys: error_on_invalid_names (boolean), ignore_remote_references (boolean), current_path (string), docs_url (string), docs_title (string), context (string)
12860: # @return {Array} - array depending on the context (not a reference)
1.660 raeburn 12861: sub ask_for_embedded_content {
1.1249 damieng 12862: # NOTE: documentation was added afterwards, it could be wrong
1.660 raeburn 12863: my ($actionurl,$state,$allfiles,$codebase,$args)=@_;
1.1071 raeburn 12864: my (%subdependencies,%dependencies,%mapping,%existing,%newfiles,%pathchanges,
1.1085 raeburn 12865: %currsubfile,%unused,$rem);
1.1071 raeburn 12866: my $counter = 0;
12867: my $numnew = 0;
1.987 raeburn 12868: my $numremref = 0;
12869: my $numinvalid = 0;
12870: my $numpathchg = 0;
12871: my $numexisting = 0;
1.1071 raeburn 12872: my $numunused = 0;
12873: my ($output,$upload_output,$toplevel,$url,$udom,$uname,$getpropath,$cdom,$cnum,
1.1156 raeburn 12874: $fileloc,$filename,$delete_output,$modify_output,$title,$symb,$path,$navmap);
1.1071 raeburn 12875: my $heading = &mt('Upload embedded files');
12876: my $buttontext = &mt('Upload');
12877:
1.1249 damieng 12878: # fills these variables based on the context:
12879: # $navmap, $cdom, $cnum, $udom, $uname, $url, $toplevel, $getpropath,
12880: # $path, $fileloc, $title, $rem, $filename
1.1085 raeburn 12881: if ($env{'request.course.id'}) {
1.1123 raeburn 12882: if ($actionurl eq '/adm/dependencies') {
12883: $navmap = Apache::lonnavmaps::navmap->new();
12884: }
12885: $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
12886: $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
1.1085 raeburn 12887: }
1.1123 raeburn 12888: if (($actionurl eq '/adm/portfolio') ||
12889: ($actionurl eq '/adm/coursegrp_portfolio')) {
1.984 raeburn 12890: my $current_path='/';
12891: if ($env{'form.currentpath'}) {
12892: $current_path = $env{'form.currentpath'};
12893: }
12894: if ($actionurl eq '/adm/coursegrp_portfolio') {
1.1123 raeburn 12895: $udom = $cdom;
12896: $uname = $cnum;
1.984 raeburn 12897: $url = '/userfiles/groups/'.$env{'form.group'}.'/portfolio';
12898: } else {
12899: $udom = $env{'user.domain'};
12900: $uname = $env{'user.name'};
12901: $url = '/userfiles/portfolio';
12902: }
1.987 raeburn 12903: $toplevel = $url.'/';
1.984 raeburn 12904: $url .= $current_path;
12905: $getpropath = 1;
1.987 raeburn 12906: } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank') ||
12907: ($actionurl eq '/adm/imsimport')) {
1.1022 www 12908: my ($udom,$uname,$rest) = ($args->{'current_path'} =~ m{/priv/($match_domain)/($match_username)/?(.*)$});
1.1026 raeburn 12909: $url = $Apache::lonnet::perlvar{'lonDocRoot'}."/priv/$udom/$uname/";
1.987 raeburn 12910: $toplevel = $url;
1.984 raeburn 12911: if ($rest ne '') {
1.987 raeburn 12912: $url .= $rest;
12913: }
12914: } elsif ($actionurl eq '/adm/coursedocs') {
12915: if (ref($args) eq 'HASH') {
1.1071 raeburn 12916: $url = $args->{'docs_url'};
12917: $toplevel = $url;
1.1084 raeburn 12918: if ($args->{'context'} eq 'paste') {
12919: ($cdom,$cnum) = ($url =~ m{^\Q/uploaded/\E($match_domain)/($match_courseid)/});
12920: ($path) =
12921: ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/\E(?:docs|supplemental)/(?:default|\d+)/\d+)/});
12922: $fileloc = &Apache::lonnet::filelocation('',$toplevel);
12923: $fileloc =~ s{^/}{};
12924: }
1.1071 raeburn 12925: }
1.1084 raeburn 12926: } elsif ($actionurl eq '/adm/dependencies') {
1.1071 raeburn 12927: if ($env{'request.course.id'} ne '') {
12928: if (ref($args) eq 'HASH') {
12929: $url = $args->{'docs_url'};
12930: $title = $args->{'docs_title'};
1.1126 raeburn 12931: $toplevel = $url;
12932: unless ($toplevel =~ m{^/}) {
12933: $toplevel = "/$url";
12934: }
1.1085 raeburn 12935: ($rem) = ($toplevel =~ m{^(.+/)[^/]+$});
1.1126 raeburn 12936: if ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/portfolio/syllabus\E)}) {
12937: $path = $1;
12938: } else {
12939: ($path) =
12940: ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/\E(?:docs|supplemental)/(?:default|\d+)/\d+)/});
12941: }
1.1195 raeburn 12942: if ($toplevel=~/^\/*(uploaded|editupload)/) {
12943: $fileloc = $toplevel;
12944: $fileloc=~ s/^\s*(\S+)\s*$/$1/;
12945: my ($udom,$uname,$fname) =
12946: ($fileloc=~ m{^/+(?:uploaded|editupload)/+($match_domain)/+($match_name)/+(.*)$});
12947: $fileloc = propath($udom,$uname).'/userfiles/'.$fname;
12948: } else {
12949: $fileloc = &Apache::lonnet::filelocation('',$toplevel);
12950: }
1.1071 raeburn 12951: $fileloc =~ s{^/}{};
12952: ($filename) = ($fileloc =~ m{.+/([^/]+)$});
12953: $heading = &mt('Status of dependencies in [_1]',"$title ($filename)");
12954: }
1.987 raeburn 12955: }
1.1123 raeburn 12956: } elsif ($actionurl eq "/public/$cdom/$cnum/syllabus") {
12957: $udom = $cdom;
12958: $uname = $cnum;
12959: $url = "/uploaded/$cdom/$cnum/portfolio/syllabus";
12960: $toplevel = $url;
12961: $path = $url;
12962: $fileloc = &Apache::lonnet::filelocation('',$toplevel).'/';
12963: $fileloc =~ s{^/}{};
1.987 raeburn 12964: }
1.1249 damieng 12965:
12966: # parses the dependency paths to get some info
12967: # fills $newfiles, $mapping, $subdependencies, $dependencies
12968: # $newfiles: hash URL -> 1 for new files or external URLs
12969: # (will be completed later)
12970: # $mapping:
12971: # for external URLs: external URL -> external URL
12972: # for relative paths: clean path -> original path
12973: # $subdependencies: hash clean path -> clean file name -> 1 for relative paths in subdirectories
12974: # $dependencies: hash clean or not file name -> 1 for relative paths not in subdirectories
1.1126 raeburn 12975: foreach my $file (keys(%{$allfiles})) {
12976: my $embed_file;
12977: if (($path eq "/uploaded/$cdom/$cnum/portfolio/syllabus") && ($file =~ m{^\Q$path/\E(.+)$})) {
12978: $embed_file = $1;
12979: } else {
12980: $embed_file = $file;
12981: }
1.1158 raeburn 12982: my ($absolutepath,$cleaned_file);
12983: if ($embed_file =~ m{^\w+://}) {
12984: $cleaned_file = $embed_file;
1.1147 raeburn 12985: $newfiles{$cleaned_file} = 1;
12986: $mapping{$cleaned_file} = $embed_file;
1.987 raeburn 12987: } else {
1.1158 raeburn 12988: $cleaned_file = &clean_path($embed_file);
1.987 raeburn 12989: if ($embed_file =~ m{^/}) {
12990: $absolutepath = $embed_file;
12991: }
1.1147 raeburn 12992: if ($cleaned_file =~ m{/}) {
12993: my ($path,$fname) = ($cleaned_file =~ m{^(.+)/([^/]*)$});
1.987 raeburn 12994: $path = &check_for_traversal($path,$url,$toplevel);
12995: my $item = $fname;
12996: if ($path ne '') {
12997: $item = $path.'/'.$fname;
12998: $subdependencies{$path}{$fname} = 1;
12999: } else {
13000: $dependencies{$item} = 1;
13001: }
13002: if ($absolutepath) {
13003: $mapping{$item} = $absolutepath;
13004: } else {
13005: $mapping{$item} = $embed_file;
13006: }
13007: } else {
13008: $dependencies{$embed_file} = 1;
13009: if ($absolutepath) {
1.1147 raeburn 13010: $mapping{$cleaned_file} = $absolutepath;
1.987 raeburn 13011: } else {
1.1147 raeburn 13012: $mapping{$cleaned_file} = $embed_file;
1.987 raeburn 13013: }
13014: }
1.984 raeburn 13015: }
13016: }
1.1249 damieng 13017:
13018: # looks for all existing files in dependency subdirectories (from $subdependencies filled above)
13019: # and lists
13020: # fills $currsubfile, $pathchanges, $existing, $numexisting, $newfiles, $unused
13021: # $currsubfile: hash clean path -> file name -> 1 for all existing files in the path
13022: # $pathchanges: hash clean path -> 1 if the file in subdirectory exists and
13023: # the path had to be cleaned up
13024: # $existing: hash clean path -> 1 if the file exists
13025: # $numexisting: number of keys in $existing
13026: # $newfiles: updated with clean path -> 1 for files in subdirectories that do not exist
13027: # $unused: only for /adm/dependencies, hash clean path -> 1 for existing files in
13028: # dependency subdirectories that are
13029: # not listed as dependencies, with some exceptions using $rem
1.1071 raeburn 13030: my $dirptr = 16384;
1.984 raeburn 13031: foreach my $path (keys(%subdependencies)) {
1.1071 raeburn 13032: $currsubfile{$path} = {};
1.1123 raeburn 13033: if (($actionurl eq '/adm/portfolio') ||
13034: ($actionurl eq '/adm/coursegrp_portfolio')) {
1.1021 raeburn 13035: my ($sublistref,$listerror) =
13036: &Apache::lonnet::dirlist($url.$path,$udom,$uname,$getpropath);
13037: if (ref($sublistref) eq 'ARRAY') {
13038: foreach my $line (@{$sublistref}) {
13039: my ($file_name,$rest) = split(/\&/,$line,2);
1.1071 raeburn 13040: $currsubfile{$path}{$file_name} = 1;
1.1021 raeburn 13041: }
1.984 raeburn 13042: }
1.987 raeburn 13043: } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
1.984 raeburn 13044: if (opendir(my $dir,$url.'/'.$path)) {
13045: my @subdir_list = grep(!/^\./,readdir($dir));
1.1071 raeburn 13046: map {$currsubfile{$path}{$_} = 1;} @subdir_list;
13047: }
1.1084 raeburn 13048: } elsif (($actionurl eq '/adm/dependencies') ||
13049: (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
1.1123 raeburn 13050: ($args->{'context'} eq 'paste')) ||
13051: ($actionurl eq "/public/$cdom/$cnum/syllabus")) {
1.1071 raeburn 13052: if ($env{'request.course.id'} ne '') {
1.1123 raeburn 13053: my $dir;
13054: if ($actionurl eq "/public/$cdom/$cnum/syllabus") {
13055: $dir = $fileloc;
13056: } else {
13057: ($dir) = ($fileloc =~ m{^(.+/)[^/]+$});
13058: }
1.1071 raeburn 13059: if ($dir ne '') {
13060: my ($sublistref,$listerror) =
13061: &Apache::lonnet::dirlist($dir.$path,$cdom,$cnum,$getpropath,undef,'/');
13062: if (ref($sublistref) eq 'ARRAY') {
13063: foreach my $line (@{$sublistref}) {
13064: my ($file_name,$dom,undef,$testdir,undef,undef,undef,undef,$size,
13065: undef,$mtime)=split(/\&/,$line,12);
13066: unless (($testdir&$dirptr) ||
13067: ($file_name =~ /^\.\.?$/)) {
13068: $currsubfile{$path}{$file_name} = [$size,$mtime];
13069: }
13070: }
13071: }
13072: }
1.984 raeburn 13073: }
13074: }
13075: foreach my $file (keys(%{$subdependencies{$path}})) {
1.1071 raeburn 13076: if (exists($currsubfile{$path}{$file})) {
1.987 raeburn 13077: my $item = $path.'/'.$file;
13078: unless ($mapping{$item} eq $item) {
13079: $pathchanges{$item} = 1;
13080: }
13081: $existing{$item} = 1;
13082: $numexisting ++;
13083: } else {
13084: $newfiles{$path.'/'.$file} = 1;
1.984 raeburn 13085: }
13086: }
1.1071 raeburn 13087: if ($actionurl eq '/adm/dependencies') {
13088: foreach my $path (keys(%currsubfile)) {
13089: if (ref($currsubfile{$path}) eq 'HASH') {
13090: foreach my $file (keys(%{$currsubfile{$path}})) {
13091: unless ($subdependencies{$path}{$file}) {
1.1085 raeburn 13092: next if (($rem ne '') &&
13093: (($env{"httpref.$rem"."$path/$file"} ne '') ||
13094: (ref($navmap) &&
13095: (($navmap->getResourceByUrl($rem."$path/$file") ne '') ||
13096: (($file =~ /^(.*\.s?html?)\.bak$/i) &&
13097: ($navmap->getResourceByUrl($rem."$path/$1")))))));
1.1071 raeburn 13098: $unused{$path.'/'.$file} = 1;
13099: }
13100: }
13101: }
13102: }
13103: }
1.984 raeburn 13104: }
1.1249 damieng 13105:
13106: # fills $currfile, hash file name -> 1 or [$size,$mtime]
13107: # for files in $url or $fileloc (target directory) in some contexts
1.987 raeburn 13108: my %currfile;
1.1123 raeburn 13109: if (($actionurl eq '/adm/portfolio') ||
13110: ($actionurl eq '/adm/coursegrp_portfolio')) {
1.1021 raeburn 13111: my ($dirlistref,$listerror) =
13112: &Apache::lonnet::dirlist($url,$udom,$uname,$getpropath);
13113: if (ref($dirlistref) eq 'ARRAY') {
13114: foreach my $line (@{$dirlistref}) {
13115: my ($file_name,$rest) = split(/\&/,$line,2);
13116: $currfile{$file_name} = 1;
13117: }
1.984 raeburn 13118: }
1.987 raeburn 13119: } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
1.984 raeburn 13120: if (opendir(my $dir,$url)) {
1.987 raeburn 13121: my @dir_list = grep(!/^\./,readdir($dir));
1.984 raeburn 13122: map {$currfile{$_} = 1;} @dir_list;
13123: }
1.1084 raeburn 13124: } elsif (($actionurl eq '/adm/dependencies') ||
13125: (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
1.1123 raeburn 13126: ($args->{'context'} eq 'paste')) ||
13127: ($actionurl eq "/public/$cdom/$cnum/syllabus")) {
1.1071 raeburn 13128: if ($env{'request.course.id'} ne '') {
13129: my ($dir) = ($fileloc =~ m{^(.+/)[^/]+$});
13130: if ($dir ne '') {
13131: my ($dirlistref,$listerror) =
13132: &Apache::lonnet::dirlist($dir,$cdom,$cnum,$getpropath,undef,'/');
13133: if (ref($dirlistref) eq 'ARRAY') {
13134: foreach my $line (@{$dirlistref}) {
13135: my ($file_name,$dom,undef,$testdir,undef,undef,undef,undef,
13136: $size,undef,$mtime)=split(/\&/,$line,12);
13137: unless (($testdir&$dirptr) ||
13138: ($file_name =~ /^\.\.?$/)) {
13139: $currfile{$file_name} = [$size,$mtime];
13140: }
13141: }
13142: }
13143: }
13144: }
1.984 raeburn 13145: }
1.1249 damieng 13146: # updates $pathchanges, $existing, $numexisting, $newfiles and $unused for files that
13147: # are not in subdirectories, using $currfile
1.984 raeburn 13148: foreach my $file (keys(%dependencies)) {
1.1071 raeburn 13149: if (exists($currfile{$file})) {
1.987 raeburn 13150: unless ($mapping{$file} eq $file) {
13151: $pathchanges{$file} = 1;
13152: }
13153: $existing{$file} = 1;
13154: $numexisting ++;
13155: } else {
1.984 raeburn 13156: $newfiles{$file} = 1;
13157: }
13158: }
1.1071 raeburn 13159: foreach my $file (keys(%currfile)) {
13160: unless (($file eq $filename) ||
13161: ($file eq $filename.'.bak') ||
13162: ($dependencies{$file})) {
1.1085 raeburn 13163: if ($actionurl eq '/adm/dependencies') {
1.1126 raeburn 13164: unless ($toplevel =~ m{^\Q/uploaded/$cdom/$cnum/portfolio/syllabus\E}) {
13165: next if (($rem ne '') &&
13166: (($env{"httpref.$rem".$file} ne '') ||
13167: (ref($navmap) &&
13168: (($navmap->getResourceByUrl($rem.$file) ne '') ||
13169: (($file =~ /^(.*\.s?html?)\.bak$/i) &&
13170: ($navmap->getResourceByUrl($rem.$1)))))));
13171: }
1.1085 raeburn 13172: }
1.1071 raeburn 13173: $unused{$file} = 1;
13174: }
13175: }
1.1249 damieng 13176:
13177: # returns some results for coursedocs paste and syllabus rewrites ($output is undef)
1.1084 raeburn 13178: if (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
13179: ($args->{'context'} eq 'paste')) {
13180: $counter = scalar(keys(%existing));
13181: $numpathchg = scalar(keys(%pathchanges));
1.1123 raeburn 13182: return ($output,$counter,$numpathchg,\%existing);
13183: } elsif (($actionurl eq "/public/$cdom/$cnum/syllabus") &&
13184: (ref($args) eq 'HASH') && ($args->{'context'} eq 'rewrites')) {
13185: $counter = scalar(keys(%existing));
13186: $numpathchg = scalar(keys(%pathchanges));
13187: return ($output,$counter,$numpathchg,\%existing,\%mapping);
1.1084 raeburn 13188: }
1.1249 damieng 13189:
13190: # returns HTML otherwise, with dependency results and to ask for more uploads
13191:
13192: # $upload_output: missing dependencies (with upload form)
13193: # $modify_output: uploaded dependencies (in use)
13194: # $delete_output: files no longer in use (unused files are not listed for londocs, bug?)
1.984 raeburn 13195: foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%newfiles)) {
1.1071 raeburn 13196: if ($actionurl eq '/adm/dependencies') {
13197: next if ($embed_file =~ m{^\w+://});
13198: }
1.660 raeburn 13199: $upload_output .= &start_data_table_row().
1.1123 raeburn 13200: '<td valign="top"><img src="'.&icon($embed_file).'" /> '.
1.1071 raeburn 13201: '<span class="LC_filename">'.$embed_file.'</span>';
1.987 raeburn 13202: unless ($mapping{$embed_file} eq $embed_file) {
1.1123 raeburn 13203: $upload_output .= '<br /><span class="LC_info" style="font-size:smaller;">'.
13204: &mt('changed from: [_1]',$mapping{$embed_file}).'</span>';
1.987 raeburn 13205: }
1.1123 raeburn 13206: $upload_output .= '</td>';
1.1071 raeburn 13207: if ($args->{'ignore_remote_references'} && $embed_file =~ m{^\w+://}) {
1.1123 raeburn 13208: $upload_output.='<td align="right">'.
13209: '<span class="LC_info LC_fontsize_medium">'.
13210: &mt("URL points to web address").'</span>';
1.987 raeburn 13211: $numremref++;
1.660 raeburn 13212: } elsif ($args->{'error_on_invalid_names'}
13213: && $embed_file ne &Apache::lonnet::clean_filename($embed_file,{'keep_path' => 1,})) {
1.1123 raeburn 13214: $upload_output.='<td align="right"><span class="LC_warning">'.
13215: &mt('Invalid characters').'</span>';
1.987 raeburn 13216: $numinvalid++;
1.660 raeburn 13217: } else {
1.1123 raeburn 13218: $upload_output .= '<td>'.
13219: &embedded_file_element('upload_embedded',$counter,
1.987 raeburn 13220: $embed_file,\%mapping,
1.1071 raeburn 13221: $allfiles,$codebase,'upload');
13222: $counter ++;
13223: $numnew ++;
1.987 raeburn 13224: }
13225: $upload_output .= '</td>'.&Apache::loncommon::end_data_table_row()."\n";
13226: }
13227: foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%existing)) {
1.1071 raeburn 13228: if ($actionurl eq '/adm/dependencies') {
13229: my ($size,$mtime) = &get_dependency_details(\%currfile,\%currsubfile,$embed_file);
13230: $modify_output .= &start_data_table_row().
13231: '<td><a href="'.$path.'/'.$embed_file.'" style="text-decoration:none;">'.
13232: '<img src="'.&icon($embed_file).'" border="0" />'.
13233: ' <span class="LC_filename">'.$embed_file.'</span></a></td>'.
13234: '<td>'.$size.'</td>'.
13235: '<td>'.$mtime.'</td>'.
13236: '<td><label><input type="checkbox" name="mod_upload_dep" '.
13237: 'onclick="toggleBrowse('."'$counter'".')" id="mod_upload_dep_'.
13238: $counter.'" value="'.$counter.'" />'.&mt('Yes').'</label>'.
13239: '<div id="moduploaddep_'.$counter.'" style="display:none;">'.
13240: &embedded_file_element('upload_embedded',$counter,
13241: $embed_file,\%mapping,
13242: $allfiles,$codebase,'modify').
13243: '</div></td>'.
13244: &end_data_table_row()."\n";
13245: $counter ++;
13246: } else {
13247: $upload_output .= &start_data_table_row().
1.1123 raeburn 13248: '<td valign="top"><img src="'.&icon($embed_file).'" /> '.
13249: '<span class="LC_filename">'.$embed_file.'</span></td>'.
13250: '<td align="right"><span class="LC_info LC_fontsize_medium">'.&mt('Already exists').'</span></td>'.
1.1071 raeburn 13251: &Apache::loncommon::end_data_table_row()."\n";
13252: }
13253: }
13254: my $delidx = $counter;
13255: foreach my $oldfile (sort {lc($a) cmp lc($b)} keys(%unused)) {
13256: my ($size,$mtime) = &get_dependency_details(\%currfile,\%currsubfile,$oldfile);
13257: $delete_output .= &start_data_table_row().
13258: '<td><img src="'.&icon($oldfile).'" />'.
13259: ' <span class="LC_filename">'.$oldfile.'</span></td>'.
13260: '<td>'.$size.'</td>'.
13261: '<td>'.$mtime.'</td>'.
13262: '<td><label><input type="checkbox" name="del_upload_dep" '.
13263: ' value="'.$delidx.'" />'.&mt('Yes').'</label>'.
13264: &embedded_file_element('upload_embedded',$delidx,
13265: $oldfile,\%mapping,$allfiles,
13266: $codebase,'delete').'</td>'.
13267: &end_data_table_row()."\n";
13268: $numunused ++;
13269: $delidx ++;
1.987 raeburn 13270: }
13271: if ($upload_output) {
13272: $upload_output = &start_data_table().
13273: $upload_output.
13274: &end_data_table()."\n";
13275: }
1.1071 raeburn 13276: if ($modify_output) {
13277: $modify_output = &start_data_table().
13278: &start_data_table_header_row().
13279: '<th>'.&mt('File').'</th>'.
13280: '<th>'.&mt('Size (KB)').'</th>'.
13281: '<th>'.&mt('Modified').'</th>'.
13282: '<th>'.&mt('Upload replacement?').'</th>'.
13283: &end_data_table_header_row().
13284: $modify_output.
13285: &end_data_table()."\n";
13286: }
13287: if ($delete_output) {
13288: $delete_output = &start_data_table().
13289: &start_data_table_header_row().
13290: '<th>'.&mt('File').'</th>'.
13291: '<th>'.&mt('Size (KB)').'</th>'.
13292: '<th>'.&mt('Modified').'</th>'.
13293: '<th>'.&mt('Delete?').'</th>'.
13294: &end_data_table_header_row().
13295: $delete_output.
13296: &end_data_table()."\n";
13297: }
1.987 raeburn 13298: my $applies = 0;
13299: if ($numremref) {
13300: $applies ++;
13301: }
13302: if ($numinvalid) {
13303: $applies ++;
13304: }
13305: if ($numexisting) {
13306: $applies ++;
13307: }
1.1071 raeburn 13308: if ($counter || $numunused) {
1.987 raeburn 13309: $output = '<form name="upload_embedded" action="'.$actionurl.'"'.
13310: ' method="post" enctype="multipart/form-data">'."\n".
1.1071 raeburn 13311: $state.'<h3>'.$heading.'</h3>';
13312: if ($actionurl eq '/adm/dependencies') {
13313: if ($numnew) {
13314: $output .= '<h4>'.&mt('Missing dependencies').'</h4>'.
13315: '<p>'.&mt('The following files need to be uploaded.').'</p>'."\n".
13316: $upload_output.'<br />'."\n";
13317: }
13318: if ($numexisting) {
13319: $output .= '<h4>'.&mt('Uploaded dependencies (in use)').'</h4>'.
13320: '<p>'.&mt('Upload a new file to replace the one currently in use.').'</p>'."\n".
13321: $modify_output.'<br />'."\n";
13322: $buttontext = &mt('Save changes');
13323: }
13324: if ($numunused) {
13325: $output .= '<h4>'.&mt('Unused files').'</h4>'.
13326: '<p>'.&mt('The following uploaded files are no longer used.').'</p>'."\n".
13327: $delete_output.'<br />'."\n";
13328: $buttontext = &mt('Save changes');
13329: }
13330: } else {
13331: $output .= $upload_output.'<br />'."\n";
13332: }
13333: $output .= '<input type ="hidden" name="number_embedded_items" value="'.
13334: $counter.'" />'."\n";
13335: if ($actionurl eq '/adm/dependencies') {
13336: $output .= '<input type ="hidden" name="number_newemb_items" value="'.
13337: $numnew.'" />'."\n";
13338: } elsif ($actionurl eq '') {
1.987 raeburn 13339: $output .= '<input type="hidden" name="phase" value="three" />';
13340: }
13341: } elsif ($applies) {
13342: $output = '<b>'.&mt('Referenced files').'</b>:<br />';
13343: if ($applies > 1) {
13344: $output .=
1.1123 raeburn 13345: &mt('No dependencies need to be uploaded, as one of the following applies to each reference:').'<ul>';
1.987 raeburn 13346: if ($numremref) {
13347: $output .= '<li>'.&mt('reference is to a URL which points to another server').'</li>'."\n";
13348: }
13349: if ($numinvalid) {
13350: $output .= '<li>'.&mt('reference is to file with a name containing invalid characters').'</li>'."\n";
13351: }
13352: if ($numexisting) {
13353: $output .= '<li>'.&mt('reference is to an existing file at the specified location').'</li>'."\n";
13354: }
13355: $output .= '</ul><br />';
13356: } elsif ($numremref) {
13357: $output .= '<p>'.&mt('None to upload, as all references are to URLs pointing to another server.').'</p>';
13358: } elsif ($numinvalid) {
13359: $output .= '<p>'.&mt('None to upload, as all references are to files with names containing invalid characters.').'</p>';
13360: } elsif ($numexisting) {
13361: $output .= '<p>'.&mt('None to upload, as all references are to existing files.').'</p>';
13362: }
13363: $output .= $upload_output.'<br />';
13364: }
13365: my ($pathchange_output,$chgcount);
1.1071 raeburn 13366: $chgcount = $counter;
1.987 raeburn 13367: if (keys(%pathchanges) > 0) {
13368: foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%pathchanges)) {
1.1071 raeburn 13369: if ($counter) {
1.987 raeburn 13370: $output .= &embedded_file_element('pathchange',$chgcount,
13371: $embed_file,\%mapping,
1.1071 raeburn 13372: $allfiles,$codebase,'change');
1.987 raeburn 13373: } else {
13374: $pathchange_output .=
13375: &start_data_table_row().
13376: '<td><input type ="checkbox" name="namechange" value="'.
13377: $chgcount.'" checked="checked" /></td>'.
13378: '<td>'.$mapping{$embed_file}.'</td>'.
13379: '<td>'.$embed_file.
13380: &embedded_file_element('pathchange',$numpathchg,$embed_file,
1.1071 raeburn 13381: \%mapping,$allfiles,$codebase,'change').
1.987 raeburn 13382: '</td>'.&end_data_table_row();
1.660 raeburn 13383: }
1.987 raeburn 13384: $numpathchg ++;
13385: $chgcount ++;
1.660 raeburn 13386: }
13387: }
1.1127 raeburn 13388: if (($counter) || ($numunused)) {
1.987 raeburn 13389: if ($numpathchg) {
13390: $output .= '<input type ="hidden" name="number_pathchange_items" value="'.
13391: $numpathchg.'" />'."\n";
13392: }
13393: if (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank') ||
13394: ($actionurl eq '/adm/imsimport')) {
13395: $output .= '<input type="hidden" name="phase" value="three" />'."\n";
13396: } elsif ($actionurl eq '/adm/portfolio' || $actionurl eq '/adm/coursegrp_portfolio') {
13397: $output .= '<input type="hidden" name="action" value="upload_embedded" />';
1.1071 raeburn 13398: } elsif ($actionurl eq '/adm/dependencies') {
13399: $output .= '<input type="hidden" name="action" value="process_changes" />';
1.987 raeburn 13400: }
1.1123 raeburn 13401: $output .= '<input type ="submit" value="'.$buttontext.'" />'."\n".'</form>'."\n";
1.987 raeburn 13402: } elsif ($numpathchg) {
13403: my %pathchange = ();
13404: $output .= &modify_html_form('pathchange',$actionurl,$state,\%pathchange,$pathchange_output);
13405: if (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
13406: $output .= '<p>'.&mt('or').'</p>';
1.1123 raeburn 13407: }
1.987 raeburn 13408: }
1.1071 raeburn 13409: return ($output,$counter,$numpathchg);
1.987 raeburn 13410: }
13411:
1.1147 raeburn 13412: =pod
13413:
13414: =item * clean_path($name)
13415:
13416: Performs clean-up of directories, subdirectories and filename in an
13417: embedded object, referenced in an HTML file which is being uploaded
13418: to a course or portfolio, where
13419: "Upload embedded images/multimedia files if HTML file" checkbox was
13420: checked.
13421:
13422: Clean-up is similar to replacements in lonnet::clean_filename()
13423: except each / between sub-directory and next level is preserved.
13424:
13425: =cut
13426:
13427: sub clean_path {
13428: my ($embed_file) = @_;
13429: $embed_file =~s{^/+}{};
13430: my @contents;
13431: if ($embed_file =~ m{/}) {
13432: @contents = split(/\//,$embed_file);
13433: } else {
13434: @contents = ($embed_file);
13435: }
13436: my $lastidx = scalar(@contents)-1;
13437: for (my $i=0; $i<=$lastidx; $i++) {
13438: $contents[$i]=~s{\\}{/}g;
13439: $contents[$i]=~s/\s+/\_/g;
13440: $contents[$i]=~s{[^/\w\.\-]}{}g;
13441: if ($i == $lastidx) {
13442: $contents[$i]=~s/\.(\d+)(?=\.)/_$1/g;
13443: }
13444: }
13445: if ($lastidx > 0) {
13446: return join('/',@contents);
13447: } else {
13448: return $contents[0];
13449: }
13450: }
13451:
1.987 raeburn 13452: sub embedded_file_element {
1.1071 raeburn 13453: my ($context,$num,$embed_file,$mapping,$allfiles,$codebase,$type) = @_;
1.987 raeburn 13454: return unless ((ref($mapping) eq 'HASH') && (ref($allfiles) eq 'HASH') &&
13455: (ref($codebase) eq 'HASH'));
13456: my $output;
1.1071 raeburn 13457: if (($context eq 'upload_embedded') && ($type ne 'delete')) {
1.987 raeburn 13458: $output = '<input name="embedded_item_'.$num.'" type="file" value="" />'."\n";
13459: }
13460: $output .= '<input name="embedded_orig_'.$num.'" type="hidden" value="'.
13461: &escape($embed_file).'" />';
13462: unless (($context eq 'upload_embedded') &&
13463: ($mapping->{$embed_file} eq $embed_file)) {
13464: $output .='
13465: <input name="embedded_ref_'.$num.'" type="hidden" value="'.&escape($mapping->{$embed_file}).'" />';
13466: }
13467: my $attrib;
13468: if (ref($allfiles->{$mapping->{$embed_file}}) eq 'ARRAY') {
13469: $attrib = &escape(join(':',@{$allfiles->{$mapping->{$embed_file}}}));
13470: }
13471: $output .=
13472: "\n\t\t".
13473: '<input name="embedded_attrib_'.$num.'" type="hidden" value="'.
13474: $attrib.'" />';
13475: if (exists($codebase->{$mapping->{$embed_file}})) {
13476: $output .=
13477: "\n\t\t".
13478: '<input name="codebase_'.$num.'" type="hidden" value="'.
13479: &escape($codebase->{$mapping->{$embed_file}}).'" />';
1.984 raeburn 13480: }
1.987 raeburn 13481: return $output;
1.660 raeburn 13482: }
13483:
1.1071 raeburn 13484: sub get_dependency_details {
13485: my ($currfile,$currsubfile,$embed_file) = @_;
13486: my ($size,$mtime,$showsize,$showmtime);
13487: if ((ref($currfile) eq 'HASH') && (ref($currsubfile))) {
13488: if ($embed_file =~ m{/}) {
13489: my ($path,$fname) = split(/\//,$embed_file);
13490: if (ref($currsubfile->{$path}{$fname}) eq 'ARRAY') {
13491: ($size,$mtime) = @{$currsubfile->{$path}{$fname}};
13492: }
13493: } else {
13494: if (ref($currfile->{$embed_file}) eq 'ARRAY') {
13495: ($size,$mtime) = @{$currfile->{$embed_file}};
13496: }
13497: }
13498: $showsize = $size/1024.0;
13499: $showsize = sprintf("%.1f",$showsize);
13500: if ($mtime > 0) {
13501: $showmtime = &Apache::lonlocal::locallocaltime($mtime);
13502: }
13503: }
13504: return ($showsize,$showmtime);
13505: }
13506:
13507: sub ask_embedded_js {
13508: return <<"END";
13509: <script type="text/javascript"">
13510: // <![CDATA[
13511: function toggleBrowse(counter) {
13512: var chkboxid = document.getElementById('mod_upload_dep_'+counter);
13513: var fileid = document.getElementById('embedded_item_'+counter);
13514: var uploaddivid = document.getElementById('moduploaddep_'+counter);
13515: if (chkboxid.checked == true) {
13516: uploaddivid.style.display='block';
13517: } else {
13518: uploaddivid.style.display='none';
13519: fileid.value = '';
13520: }
13521: }
13522: // ]]>
13523: </script>
13524:
13525: END
13526: }
13527:
1.661 raeburn 13528: sub upload_embedded {
13529: my ($context,$dirpath,$uname,$udom,$dir_root,$url_root,$group,$disk_quota,
1.987 raeburn 13530: $current_disk_usage,$hiddenstate,$actionurl) = @_;
13531: my (%pathchange,$output,$modifyform,$footer,$returnflag);
1.661 raeburn 13532: for (my $i=0; $i<$env{'form.number_embedded_items'}; $i++) {
13533: next if (!exists($env{'form.embedded_item_'.$i.'.filename'}));
13534: my $orig_uploaded_filename =
13535: $env{'form.embedded_item_'.$i.'.filename'};
1.987 raeburn 13536: foreach my $type ('orig','ref','attrib','codebase') {
13537: if ($env{'form.embedded_'.$type.'_'.$i} ne '') {
13538: $env{'form.embedded_'.$type.'_'.$i} =
13539: &unescape($env{'form.embedded_'.$type.'_'.$i});
13540: }
13541: }
1.661 raeburn 13542: my ($path,$fname) =
13543: ($env{'form.embedded_orig_'.$i} =~ m{(.*/)([^/]*)});
13544: # no path, whole string is fname
13545: if (!$fname) { $fname = $env{'form.embedded_orig_'.$i} };
13546: $fname = &Apache::lonnet::clean_filename($fname);
13547: # See if there is anything left
13548: next if ($fname eq '');
13549:
13550: # Check if file already exists as a file or directory.
13551: my ($state,$msg);
13552: if ($context eq 'portfolio') {
13553: my $port_path = $dirpath;
13554: if ($group ne '') {
13555: $port_path = "groups/$group/$port_path";
13556: }
1.987 raeburn 13557: ($state,$msg) = &check_for_upload($env{'form.currentpath'}.$path,
13558: $fname,$group,'embedded_item_'.$i,
1.661 raeburn 13559: $dir_root,$port_path,$disk_quota,
13560: $current_disk_usage,$uname,$udom);
13561: if ($state eq 'will_exceed_quota'
1.984 raeburn 13562: || $state eq 'file_locked') {
1.661 raeburn 13563: $output .= $msg;
13564: next;
13565: }
13566: } elsif (($context eq 'author') || ($context eq 'testbank')) {
13567: ($state,$msg) = &check_for_existing($path,$fname,'embedded_item_'.$i);
13568: if ($state eq 'exists') {
13569: $output .= $msg;
13570: next;
13571: }
13572: }
13573: # Check if extension is valid
13574: if (($fname =~ /\.(\w+)$/) &&
13575: (&Apache::loncommon::fileembstyle($1) eq 'hdn')) {
1.1155 bisitz 13576: $output .= &mt('Invalid file extension ([_1]) - reserved for internal use.',$1)
13577: .' '.&mt('Rename the file with a different extension and re-upload.').'<br />';
1.661 raeburn 13578: next;
13579: } elsif (($fname =~ /\.(\w+)$/) &&
13580: (!defined(&Apache::loncommon::fileembstyle($1)))) {
1.987 raeburn 13581: $output .= &mt('Unrecognized file extension ([_1]) - rename the file with a proper extension and re-upload.',$1).'<br />';
1.661 raeburn 13582: next;
13583: } elsif ($fname=~/\.(\d+)\.(\w+)$/) {
1.1120 bisitz 13584: $output .= &mt('Filename not allowed - rename the file to remove the number immediately before the file extension([_1]) and re-upload.',$2).'<br />';
1.661 raeburn 13585: next;
13586: }
13587: $env{'form.embedded_item_'.$i.'.filename'}=$fname;
1.1123 raeburn 13588: my $subdir = $path;
13589: $subdir =~ s{/+$}{};
1.661 raeburn 13590: if ($context eq 'portfolio') {
1.984 raeburn 13591: my $result;
13592: if ($state eq 'existingfile') {
13593: $result=
13594: &Apache::lonnet::userfileupload('embedded_item_'.$i,'existingfile',
1.1123 raeburn 13595: $dirpath.$env{'form.currentpath'}.$subdir);
1.661 raeburn 13596: } else {
1.984 raeburn 13597: $result=
13598: &Apache::lonnet::userfileupload('embedded_item_'.$i,'',
1.987 raeburn 13599: $dirpath.
1.1123 raeburn 13600: $env{'form.currentpath'}.$subdir);
1.984 raeburn 13601: if ($result !~ m|^/uploaded/|) {
13602: $output .= '<span class="LC_error">'
13603: .&mt('An error occurred ([_1]) while trying to upload [_2] for embedded element [_3].'
13604: ,$result,$orig_uploaded_filename,$env{'form.embedded_orig_'.$i})
13605: .'</span><br />';
13606: next;
13607: } else {
1.987 raeburn 13608: $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
13609: $path.$fname.'</span>').'<br />';
1.984 raeburn 13610: }
1.661 raeburn 13611: }
1.1123 raeburn 13612: } elsif (($context eq 'coursedoc') || ($context eq 'syllabus')) {
1.1126 raeburn 13613: my $extendedsubdir = $dirpath.'/'.$subdir;
13614: $extendedsubdir =~ s{/+$}{};
1.987 raeburn 13615: my $result =
1.1126 raeburn 13616: &Apache::lonnet::userfileupload('embedded_item_'.$i,$context,$extendedsubdir);
1.987 raeburn 13617: if ($result !~ m|^/uploaded/|) {
13618: $output .= '<span class="LC_error">'
13619: .&mt('An error occurred ([_1]) while trying to upload [_2] for embedded element [_3].'
13620: ,$result,$orig_uploaded_filename,$env{'form.embedded_orig_'.$i})
13621: .'</span><br />';
13622: next;
13623: } else {
13624: $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
13625: $path.$fname.'</span>').'<br />';
1.1125 raeburn 13626: if ($context eq 'syllabus') {
13627: &Apache::lonnet::make_public_indefinitely($result);
13628: }
1.987 raeburn 13629: }
1.661 raeburn 13630: } else {
13631: # Save the file
13632: my $target = $env{'form.embedded_item_'.$i};
13633: my $fullpath = $dir_root.$dirpath.'/'.$path;
13634: my $dest = $fullpath.$fname;
13635: my $url = $url_root.$dirpath.'/'.$path.$fname;
1.1027 raeburn 13636: my @parts=split(/\//,"$dirpath/$path");
1.661 raeburn 13637: my $count;
13638: my $filepath = $dir_root;
1.1027 raeburn 13639: foreach my $subdir (@parts) {
13640: $filepath .= "/$subdir";
13641: if (!-e $filepath) {
1.661 raeburn 13642: mkdir($filepath,0770);
13643: }
13644: }
13645: my $fh;
13646: if (!open($fh,'>'.$dest)) {
13647: &Apache::lonnet::logthis('Failed to create '.$dest);
13648: $output .= '<span class="LC_error">'.
1.1071 raeburn 13649: &mt('An error occurred while trying to upload [_1] for embedded element [_2].',
13650: $orig_uploaded_filename,$env{'form.embedded_orig_'.$i}).
1.661 raeburn 13651: '</span><br />';
13652: } else {
13653: if (!print $fh $env{'form.embedded_item_'.$i}) {
13654: &Apache::lonnet::logthis('Failed to write to '.$dest);
13655: $output .= '<span class="LC_error">'.
1.1071 raeburn 13656: &mt('An error occurred while writing the file [_1] for embedded element [_2].',
13657: $orig_uploaded_filename,$env{'form.embedded_orig_'.$i}).
1.661 raeburn 13658: '</span><br />';
13659: } else {
1.987 raeburn 13660: $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
13661: $url.'</span>').'<br />';
13662: unless ($context eq 'testbank') {
13663: $footer .= &mt('View embedded file: [_1]',
13664: '<a href="'.$url.'">'.$fname.'</a>').'<br />';
13665: }
13666: }
13667: close($fh);
13668: }
13669: }
13670: if ($env{'form.embedded_ref_'.$i}) {
13671: $pathchange{$i} = 1;
13672: }
13673: }
13674: if ($output) {
13675: $output = '<p>'.$output.'</p>';
13676: }
13677: $output .= &modify_html_form('upload_embedded',$actionurl,$hiddenstate,\%pathchange);
13678: $returnflag = 'ok';
1.1071 raeburn 13679: my $numpathchgs = scalar(keys(%pathchange));
13680: if ($numpathchgs > 0) {
1.987 raeburn 13681: if ($context eq 'portfolio') {
13682: $output .= '<p>'.&mt('or').'</p>';
13683: } elsif ($context eq 'testbank') {
1.1071 raeburn 13684: $output .= '<p>'.&mt('Or [_1]continue[_2] the testbank import without modifying the reference(s).',
13685: '<a href="javascript:document.testbankForm.submit();">','</a>').'</p>';
1.987 raeburn 13686: $returnflag = 'modify_orightml';
13687: }
13688: }
1.1071 raeburn 13689: return ($output.$footer,$returnflag,$numpathchgs);
1.987 raeburn 13690: }
13691:
13692: sub modify_html_form {
13693: my ($context,$actionurl,$hiddenstate,$pathchange,$pathchgtable) = @_;
13694: my $end = 0;
13695: my $modifyform;
13696: if ($context eq 'upload_embedded') {
13697: return unless (ref($pathchange) eq 'HASH');
13698: if ($env{'form.number_embedded_items'}) {
13699: $end += $env{'form.number_embedded_items'};
13700: }
13701: if ($env{'form.number_pathchange_items'}) {
13702: $end += $env{'form.number_pathchange_items'};
13703: }
13704: if ($end) {
13705: for (my $i=0; $i<$end; $i++) {
13706: if ($i < $env{'form.number_embedded_items'}) {
13707: next unless($pathchange->{$i});
13708: }
13709: $modifyform .=
13710: &start_data_table_row().
13711: '<td><input type ="checkbox" name="namechange" value="'.$i.'" '.
13712: 'checked="checked" /></td>'.
13713: '<td>'.$env{'form.embedded_ref_'.$i}.
13714: '<input type="hidden" name="embedded_ref_'.$i.'" value="'.
13715: &escape($env{'form.embedded_ref_'.$i}).'" />'.
13716: '<input type="hidden" name="embedded_codebase_'.$i.'" value="'.
13717: &escape($env{'form.embedded_codebase_'.$i}).'" />'.
13718: '<input type="hidden" name="embedded_attrib_'.$i.'" value="'.
13719: &escape($env{'form.embedded_attrib_'.$i}).'" /></td>'.
13720: '<td>'.$env{'form.embedded_orig_'.$i}.
13721: '<input type="hidden" name="embedded_orig_'.$i.'" value="'.
13722: &escape($env{'form.embedded_orig_'.$i}).'" /></td>'.
13723: &end_data_table_row();
1.1071 raeburn 13724: }
1.987 raeburn 13725: }
13726: } else {
13727: $modifyform = $pathchgtable;
13728: if (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
13729: $hiddenstate .= '<input type="hidden" name="phase" value="four" />';
13730: } elsif (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
13731: $hiddenstate .= '<input type="hidden" name="action" value="modify_orightml" />';
13732: }
13733: }
13734: if ($modifyform) {
1.1071 raeburn 13735: if ($actionurl eq '/adm/dependencies') {
13736: $hiddenstate .= '<input type="hidden" name="action" value="modifyhrefs" />';
13737: }
1.987 raeburn 13738: return '<h3>'.&mt('Changes in content of HTML file required').'</h3>'."\n".
13739: '<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".
13740: '<li>'.&mt('For consistency between the reference(s) and the location of the corresponding stored file within LON-CAPA.').'</li>'."\n".
13741: '<li>'.&mt('To change absolute paths to relative paths, or replace directory traversal via "../" within the original reference.').'</li>'."\n".
13742: '</ol></p>'."\n".'<p>'.
13743: &mt('LON-CAPA can make the required changes to your HTML file.').'</p>'."\n".
13744: '<form method="post" name="refchanger" action="'.$actionurl.'">'.
13745: &start_data_table()."\n".
13746: &start_data_table_header_row().
13747: '<th>'.&mt('Change?').'</th>'.
13748: '<th>'.&mt('Current reference').'</th>'.
13749: '<th>'.&mt('Required reference').'</th>'.
13750: &end_data_table_header_row()."\n".
13751: $modifyform.
13752: &end_data_table().'<br />'."\n".$hiddenstate.
13753: '<input type="submit" name="pathchanges" value="'.&mt('Modify HTML file').'" />'.
13754: '</form>'."\n";
13755: }
13756: return;
13757: }
13758:
13759: sub modify_html_refs {
1.1123 raeburn 13760: my ($context,$dirpath,$uname,$udom,$dir_root,$url) = @_;
1.987 raeburn 13761: my $container;
13762: if ($context eq 'portfolio') {
13763: $container = $env{'form.container'};
13764: } elsif ($context eq 'coursedoc') {
13765: $container = $env{'form.primaryurl'};
1.1071 raeburn 13766: } elsif ($context eq 'manage_dependencies') {
13767: (undef,undef,$container) = &Apache::lonnet::decode_symb($env{'form.symb'});
13768: $container = "/$container";
1.1123 raeburn 13769: } elsif ($context eq 'syllabus') {
13770: $container = $url;
1.987 raeburn 13771: } else {
1.1027 raeburn 13772: $container = $Apache::lonnet::perlvar{'lonDocRoot'}.$env{'form.filename'};
1.987 raeburn 13773: }
13774: my (%allfiles,%codebase,$output,$content);
13775: my @changes = &get_env_multiple('form.namechange');
1.1126 raeburn 13776: unless ((@changes > 0) || ($context eq 'syllabus')) {
1.1071 raeburn 13777: if (wantarray) {
13778: return ('',0,0);
13779: } else {
13780: return;
13781: }
13782: }
13783: if (($context eq 'portfolio') || ($context eq 'coursedoc') ||
1.1123 raeburn 13784: ($context eq 'manage_dependencies') || ($context eq 'syllabus')) {
1.1071 raeburn 13785: unless ($container =~ m{^/uploaded/\Q$udom\E/\Q$uname\E/}) {
13786: if (wantarray) {
13787: return ('',0,0);
13788: } else {
13789: return;
13790: }
13791: }
1.987 raeburn 13792: $content = &Apache::lonnet::getfile($container);
1.1071 raeburn 13793: if ($content eq '-1') {
13794: if (wantarray) {
13795: return ('',0,0);
13796: } else {
13797: return;
13798: }
13799: }
1.987 raeburn 13800: } else {
1.1071 raeburn 13801: unless ($container =~ /^\Q$dir_root\E/) {
13802: if (wantarray) {
13803: return ('',0,0);
13804: } else {
13805: return;
13806: }
13807: }
1.1317 raeburn 13808: if (open(my $fh,'<',$container)) {
1.987 raeburn 13809: $content = join('', <$fh>);
13810: close($fh);
13811: } else {
1.1071 raeburn 13812: if (wantarray) {
13813: return ('',0,0);
13814: } else {
13815: return;
13816: }
1.987 raeburn 13817: }
13818: }
13819: my ($count,$codebasecount) = (0,0);
13820: my $mm = new File::MMagic;
13821: my $mime_type = $mm->checktype_contents($content);
13822: if ($mime_type eq 'text/html') {
13823: my $parse_result =
13824: &Apache::lonnet::extract_embedded_items($container,\%allfiles,
13825: \%codebase,\$content);
13826: if ($parse_result eq 'ok') {
13827: foreach my $i (@changes) {
13828: my $orig = &unescape($env{'form.embedded_orig_'.$i});
13829: my $ref = &unescape($env{'form.embedded_ref_'.$i});
13830: if ($allfiles{$ref}) {
13831: my $newname = $orig;
13832: my ($attrib_regexp,$codebase);
1.1006 raeburn 13833: $attrib_regexp = &unescape($env{'form.embedded_attrib_'.$i});
1.987 raeburn 13834: if ($attrib_regexp =~ /:/) {
13835: $attrib_regexp =~ s/\:/|/g;
13836: }
13837: if ($content =~ m{($attrib_regexp\s*=\s*['"]?)\Q$ref\E(['"]?)}) {
13838: my $numchg = ($content =~ s{($attrib_regexp\s*=\s*['"]?)\Q$ref\E(['"]?)}{$1$newname$2}gi);
13839: $count += $numchg;
1.1123 raeburn 13840: $allfiles{$newname} = $allfiles{$ref};
1.1148 raeburn 13841: delete($allfiles{$ref});
1.987 raeburn 13842: }
13843: if ($env{'form.embedded_codebase_'.$i} ne '') {
1.1006 raeburn 13844: $codebase = &unescape($env{'form.embedded_codebase_'.$i});
1.987 raeburn 13845: my $numchg = ($content =~ s/(codebase\s*=\s*["']?)\Q$codebase\E(["']?)/$1.$2/i); #' stupid emacs
13846: $codebasecount ++;
13847: }
13848: }
13849: }
1.1123 raeburn 13850: my $skiprewrites;
1.987 raeburn 13851: if ($count || $codebasecount) {
13852: my $saveresult;
1.1071 raeburn 13853: if (($context eq 'portfolio') || ($context eq 'coursedoc') ||
1.1123 raeburn 13854: ($context eq 'manage_dependencies') || ($context eq 'syllabus')) {
1.987 raeburn 13855: my $url = &Apache::lonnet::store_edited_file($container,$content,$udom,$uname,\$saveresult);
13856: if ($url eq $container) {
13857: my ($fname) = ($container =~ m{/([^/]+)$});
13858: $output = '<p>'.&mt('Updated [quant,_1,reference] in [_2].',
13859: $count,'<span class="LC_filename">'.
1.1071 raeburn 13860: $fname.'</span>').'</p>';
1.987 raeburn 13861: } else {
13862: $output = '<p class="LC_error">'.
13863: &mt('Error: update failed for: [_1].',
13864: '<span class="LC_filename">'.
13865: $container.'</span>').'</p>';
13866: }
1.1123 raeburn 13867: if ($context eq 'syllabus') {
13868: unless ($saveresult eq 'ok') {
13869: $skiprewrites = 1;
13870: }
13871: }
1.987 raeburn 13872: } else {
1.1317 raeburn 13873: if (open(my $fh,'>',$container)) {
1.987 raeburn 13874: print $fh $content;
13875: close($fh);
13876: $output = '<p>'.&mt('Updated [quant,_1,reference] in [_2].',
13877: $count,'<span class="LC_filename">'.
13878: $container.'</span>').'</p>';
1.661 raeburn 13879: } else {
1.987 raeburn 13880: $output = '<p class="LC_error">'.
13881: &mt('Error: could not update [_1].',
13882: '<span class="LC_filename">'.
13883: $container.'</span>').'</p>';
1.661 raeburn 13884: }
13885: }
13886: }
1.1123 raeburn 13887: if (($context eq 'syllabus') && (!$skiprewrites)) {
13888: my ($actionurl,$state);
13889: $actionurl = "/public/$udom/$uname/syllabus";
13890: my ($ignore,$num,$numpathchanges,$existing,$mapping) =
13891: &ask_for_embedded_content($actionurl,$state,\%allfiles,
13892: \%codebase,
13893: {'context' => 'rewrites',
13894: 'ignore_remote_references' => 1,});
13895: if (ref($mapping) eq 'HASH') {
13896: my $rewrites = 0;
13897: foreach my $key (keys(%{$mapping})) {
13898: next if ($key =~ m{^https?://});
13899: my $ref = $mapping->{$key};
13900: my $newname = "/uploaded/$udom/$uname/portfolio/syllabus/$key";
13901: my $attrib;
13902: if (ref($allfiles{$mapping->{$key}}) eq 'ARRAY') {
13903: $attrib = join('|',@{$allfiles{$mapping->{$key}}});
13904: }
13905: if ($content =~ m{($attrib\s*=\s*['"]?)\Q$ref\E(['"]?)}) {
13906: my $numchg = ($content =~ s{($attrib\s*=\s*['"]?)\Q$ref\E(['"]?)}{$1$newname$2}gi);
13907: $rewrites += $numchg;
13908: }
13909: }
13910: if ($rewrites) {
13911: my $saveresult;
13912: my $url = &Apache::lonnet::store_edited_file($container,$content,$udom,$uname,\$saveresult);
13913: if ($url eq $container) {
13914: my ($fname) = ($container =~ m{/([^/]+)$});
13915: $output .= '<p>'.&mt('Rewrote [quant,_1,link] as [quant,_1,absolute link] in [_2].',
13916: $count,'<span class="LC_filename">'.
13917: $fname.'</span>').'</p>';
13918: } else {
13919: $output .= '<p class="LC_error">'.
13920: &mt('Error: could not update links in [_1].',
13921: '<span class="LC_filename">'.
13922: $container.'</span>').'</p>';
13923:
13924: }
13925: }
13926: }
13927: }
1.987 raeburn 13928: } else {
13929: &logthis('Failed to parse '.$container.
13930: ' to modify references: '.$parse_result);
1.661 raeburn 13931: }
13932: }
1.1071 raeburn 13933: if (wantarray) {
13934: return ($output,$count,$codebasecount);
13935: } else {
13936: return $output;
13937: }
1.661 raeburn 13938: }
13939:
13940: sub check_for_existing {
13941: my ($path,$fname,$element) = @_;
13942: my ($state,$msg);
13943: if (-d $path.'/'.$fname) {
13944: $state = 'exists';
13945: $msg = &mt('Unable to upload [_1]. A directory by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$path);
13946: } elsif (-e $path.'/'.$fname) {
13947: $state = 'exists';
13948: $msg = &mt('Unable to upload [_1]. A file by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$path);
13949: }
13950: if ($state eq 'exists') {
13951: $msg = '<span class="LC_error">'.$msg.'</span><br />';
13952: }
13953: return ($state,$msg);
13954: }
13955:
13956: sub check_for_upload {
13957: my ($path,$fname,$group,$element,$portfolio_root,$port_path,
13958: $disk_quota,$current_disk_usage,$uname,$udom) = @_;
1.985 raeburn 13959: my $filesize = length($env{'form.'.$element});
13960: if (!$filesize) {
13961: my $msg = '<span class="LC_error">'.
13962: &mt('Unable to upload [_1]. (size = [_2] bytes)',
13963: '<span class="LC_filename">'.$fname.'</span>',
13964: $filesize).'<br />'.
1.1007 raeburn 13965: &mt('Either the file you attempted to upload was empty, or your web browser was unable to read its contents.').'<br />'.
1.985 raeburn 13966: '</span>';
13967: return ('zero_bytes',$msg);
13968: }
13969: $filesize = $filesize/1000; #express in k (1024?)
1.661 raeburn 13970: my $getpropath = 1;
1.1021 raeburn 13971: my ($dirlistref,$listerror) =
13972: &Apache::lonnet::dirlist($portfolio_root.$path,$udom,$uname,$getpropath);
1.661 raeburn 13973: my $found_file = 0;
13974: my $locked_file = 0;
1.991 raeburn 13975: my @lockers;
13976: my $navmap;
13977: if ($env{'request.course.id'}) {
13978: $navmap = Apache::lonnavmaps::navmap->new();
13979: }
1.1021 raeburn 13980: if (ref($dirlistref) eq 'ARRAY') {
13981: foreach my $line (@{$dirlistref}) {
13982: my ($file_name,$rest)=split(/\&/,$line,2);
13983: if ($file_name eq $fname){
13984: $file_name = $path.$file_name;
13985: if ($group ne '') {
13986: $file_name = $group.$file_name;
13987: }
13988: $found_file = 1;
13989: if (&Apache::lonnet::is_locked($file_name,$udom,$uname,\@lockers) eq 'true') {
13990: foreach my $lock (@lockers) {
13991: if (ref($lock) eq 'ARRAY') {
13992: my ($symb,$crsid) = @{$lock};
13993: if ($crsid eq $env{'request.course.id'}) {
13994: if (ref($navmap)) {
13995: my $res = $navmap->getBySymb($symb);
13996: foreach my $part (@{$res->parts()}) {
13997: my ($slot_status,$slot_time,$slot_name)=$res->check_for_slot($part);
13998: unless (($slot_status == $res->RESERVED) ||
13999: ($slot_status == $res->RESERVED_LOCATION)) {
14000: $locked_file = 1;
14001: }
1.991 raeburn 14002: }
1.1021 raeburn 14003: } else {
14004: $locked_file = 1;
1.991 raeburn 14005: }
14006: } else {
14007: $locked_file = 1;
14008: }
14009: }
1.1021 raeburn 14010: }
14011: } else {
14012: my @info = split(/\&/,$rest);
14013: my $currsize = $info[6]/1000;
14014: if ($currsize < $filesize) {
14015: my $extra = $filesize - $currsize;
14016: if (($current_disk_usage + $extra) > $disk_quota) {
1.1179 bisitz 14017: my $msg = '<p class="LC_warning">'.
1.1021 raeburn 14018: &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.',
1.1179 bisitz 14019: '<span class="LC_filename">'.$fname.'</span>',$filesize,$currsize).'</p>'.
14020: '<p>'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',
14021: $disk_quota,$current_disk_usage).'</p>';
1.1021 raeburn 14022: return ('will_exceed_quota',$msg);
14023: }
1.984 raeburn 14024: }
14025: }
1.661 raeburn 14026: }
14027: }
14028: }
14029: if (($current_disk_usage + $filesize) > $disk_quota){
1.1179 bisitz 14030: my $msg = '<p class="LC_warning">'.
14031: &mt('Unable to upload [_1]. (size = [_2] kilobytes). Disk quota will be exceeded.','<span class="LC_filename">'.$fname.'</span>',$filesize).'</p>'.
1.1184 raeburn 14032: '<p>'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',$disk_quota,$current_disk_usage).'</p>';
1.661 raeburn 14033: return ('will_exceed_quota',$msg);
14034: } elsif ($found_file) {
14035: if ($locked_file) {
1.1179 bisitz 14036: my $msg = '<p class="LC_warning">';
1.661 raeburn 14037: $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>');
1.1179 bisitz 14038: $msg .= '</p>';
1.661 raeburn 14039: $msg .= &mt('You will be able to rename or delete existing [_1] after a grade has been assigned.','<span class="LC_filename">'.$fname.'</span>');
14040: return ('file_locked',$msg);
14041: } else {
1.1179 bisitz 14042: my $msg = '<p class="LC_error">';
1.984 raeburn 14043: $msg .= &mt(' A file by that name: [_1] was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$port_path.$env{'form.currentpath'});
1.1179 bisitz 14044: $msg .= '</p>';
1.984 raeburn 14045: return ('existingfile',$msg);
1.661 raeburn 14046: }
14047: }
14048: }
14049:
1.987 raeburn 14050: sub check_for_traversal {
14051: my ($path,$url,$toplevel) = @_;
14052: my @parts=split(/\//,$path);
14053: my $cleanpath;
14054: my $fullpath = $url;
14055: for (my $i=0;$i<@parts;$i++) {
14056: next if ($parts[$i] eq '.');
14057: if ($parts[$i] eq '..') {
14058: $fullpath =~ s{([^/]+/)$}{};
14059: } else {
14060: $fullpath .= $parts[$i].'/';
14061: }
14062: }
14063: if ($fullpath =~ /^\Q$url\E(.*)$/) {
14064: $cleanpath = $1;
14065: } elsif ($fullpath =~ /^\Q$toplevel\E(.*)$/) {
14066: my $curr_toprel = $1;
14067: my @parts = split(/\//,$curr_toprel);
14068: my ($url_toprel) = ($url =~ /^\Q$toplevel\E(.*)$/);
14069: my @urlparts = split(/\//,$url_toprel);
14070: my $doubledots;
14071: my $startdiff = -1;
14072: for (my $i=0; $i<@urlparts; $i++) {
14073: if ($startdiff == -1) {
14074: unless ($urlparts[$i] eq $parts[$i]) {
14075: $startdiff = $i;
14076: $doubledots .= '../';
14077: }
14078: } else {
14079: $doubledots .= '../';
14080: }
14081: }
14082: if ($startdiff > -1) {
14083: $cleanpath = $doubledots;
14084: for (my $i=$startdiff; $i<@parts; $i++) {
14085: $cleanpath .= $parts[$i].'/';
14086: }
14087: }
14088: }
14089: $cleanpath =~ s{(/)$}{};
14090: return $cleanpath;
14091: }
1.31 albertel 14092:
1.1053 raeburn 14093: sub is_archive_file {
14094: my ($mimetype) = @_;
14095: if (($mimetype eq 'application/octet-stream') ||
14096: ($mimetype eq 'application/x-stuffit') ||
14097: ($mimetype =~ m{^application/(x\-)?(compressed|tar|zip|tgz|gz|gtar|gzip|gunzip|bz|bz2|bzip2)})) {
14098: return 1;
14099: }
14100: return;
14101: }
14102:
14103: sub decompress_form {
1.1065 raeburn 14104: my ($mimetype,$archiveurl,$action,$noextract,$hiddenelements,$dirlist) = @_;
1.1053 raeburn 14105: my %lt = &Apache::lonlocal::texthash (
14106: this => 'This file is an archive file.',
1.1067 raeburn 14107: camt => 'This file is a Camtasia archive file.',
1.1065 raeburn 14108: itsc => 'Its contents are as follows:',
1.1053 raeburn 14109: youm => 'You may wish to extract its contents.',
14110: extr => 'Extract contents',
1.1067 raeburn 14111: auto => 'LON-CAPA can process the files automatically, or you can decide how each should be handled.',
14112: proa => 'Process automatically?',
1.1053 raeburn 14113: yes => 'Yes',
14114: no => 'No',
1.1067 raeburn 14115: fold => 'Title for folder containing movie',
14116: movi => 'Title for page containing embedded movie',
1.1053 raeburn 14117: );
1.1065 raeburn 14118: my $fileloc = &Apache::lonnet::filelocation(undef,$archiveurl);
1.1067 raeburn 14119: my ($is_camtasia,$topdir,%toplevel,@paths);
1.1065 raeburn 14120: my $info = &list_archive_contents($fileloc,\@paths);
14121: if (@paths) {
14122: foreach my $path (@paths) {
14123: $path =~ s{^/}{};
1.1067 raeburn 14124: if ($path =~ m{^([^/]+)/$}) {
14125: $topdir = $1;
14126: }
1.1065 raeburn 14127: if ($path =~ m{^([^/]+)/}) {
14128: $toplevel{$1} = $path;
14129: } else {
14130: $toplevel{$path} = $path;
14131: }
14132: }
14133: }
1.1067 raeburn 14134: if ($mimetype =~ m{^application/(x\-)?(compressed|zip)}) {
1.1164 raeburn 14135: my @camtasia6 = ("$topdir/","$topdir/index.html",
1.1067 raeburn 14136: "$topdir/media/",
14137: "$topdir/media/$topdir.mp4",
14138: "$topdir/media/FirstFrame.png",
14139: "$topdir/media/player.swf",
14140: "$topdir/media/swfobject.js",
14141: "$topdir/media/expressInstall.swf");
1.1197 raeburn 14142: my @camtasia8_1 = ("$topdir/","$topdir/$topdir.html",
1.1164 raeburn 14143: "$topdir/$topdir.mp4",
14144: "$topdir/$topdir\_config.xml",
14145: "$topdir/$topdir\_controller.swf",
14146: "$topdir/$topdir\_embed.css",
14147: "$topdir/$topdir\_First_Frame.png",
14148: "$topdir/$topdir\_player.html",
14149: "$topdir/$topdir\_Thumbnails.png",
14150: "$topdir/playerProductInstall.swf",
14151: "$topdir/scripts/",
14152: "$topdir/scripts/config_xml.js",
14153: "$topdir/scripts/handlebars.js",
14154: "$topdir/scripts/jquery-1.7.1.min.js",
14155: "$topdir/scripts/jquery-ui-1.8.15.custom.min.js",
14156: "$topdir/scripts/modernizr.js",
14157: "$topdir/scripts/player-min.js",
14158: "$topdir/scripts/swfobject.js",
14159: "$topdir/skins/",
14160: "$topdir/skins/configuration_express.xml",
14161: "$topdir/skins/express_show/",
14162: "$topdir/skins/express_show/player-min.css",
14163: "$topdir/skins/express_show/spritesheet.png");
1.1197 raeburn 14164: my @camtasia8_4 = ("$topdir/","$topdir/$topdir.html",
14165: "$topdir/$topdir.mp4",
14166: "$topdir/$topdir\_config.xml",
14167: "$topdir/$topdir\_controller.swf",
14168: "$topdir/$topdir\_embed.css",
14169: "$topdir/$topdir\_First_Frame.png",
14170: "$topdir/$topdir\_player.html",
14171: "$topdir/$topdir\_Thumbnails.png",
14172: "$topdir/playerProductInstall.swf",
14173: "$topdir/scripts/",
14174: "$topdir/scripts/config_xml.js",
14175: "$topdir/scripts/techsmith-smart-player.min.js",
14176: "$topdir/skins/",
14177: "$topdir/skins/configuration_express.xml",
14178: "$topdir/skins/express_show/",
14179: "$topdir/skins/express_show/spritesheet.min.css",
14180: "$topdir/skins/express_show/spritesheet.png",
14181: "$topdir/skins/express_show/techsmith-smart-player.min.css");
1.1164 raeburn 14182: my @diffs = &compare_arrays(\@paths,\@camtasia6);
1.1067 raeburn 14183: if (@diffs == 0) {
1.1164 raeburn 14184: $is_camtasia = 6;
14185: } else {
1.1197 raeburn 14186: @diffs = &compare_arrays(\@paths,\@camtasia8_1);
1.1164 raeburn 14187: if (@diffs == 0) {
14188: $is_camtasia = 8;
1.1197 raeburn 14189: } else {
14190: @diffs = &compare_arrays(\@paths,\@camtasia8_4);
14191: if (@diffs == 0) {
14192: $is_camtasia = 8;
14193: }
1.1164 raeburn 14194: }
1.1067 raeburn 14195: }
14196: }
14197: my $output;
14198: if ($is_camtasia) {
14199: $output = <<"ENDCAM";
14200: <script type="text/javascript" language="Javascript">
14201: // <![CDATA[
14202:
14203: function camtasiaToggle() {
14204: for (var i=0; i<document.uploaded_decompress.autoextract_camtasia.length; i++) {
14205: if (document.uploaded_decompress.autoextract_camtasia[i].checked) {
1.1164 raeburn 14206: if (document.uploaded_decompress.autoextract_camtasia[i].value == $is_camtasia) {
1.1067 raeburn 14207: document.getElementById('camtasia_titles').style.display='block';
14208: } else {
14209: document.getElementById('camtasia_titles').style.display='none';
14210: }
14211: }
14212: }
14213: return;
14214: }
14215:
14216: // ]]>
14217: </script>
14218: <p>$lt{'camt'}</p>
14219: ENDCAM
1.1065 raeburn 14220: } else {
1.1067 raeburn 14221: $output = '<p>'.$lt{'this'};
14222: if ($info eq '') {
14223: $output .= ' '.$lt{'youm'}.'</p>'."\n";
14224: } else {
14225: $output .= ' '.$lt{'itsc'}.'</p>'."\n".
14226: '<div><pre>'.$info.'</pre></div>';
14227: }
1.1065 raeburn 14228: }
1.1067 raeburn 14229: $output .= '<form name="uploaded_decompress" action="'.$action.'" method="post">'."\n";
1.1065 raeburn 14230: my $duplicates;
14231: my $num = 0;
14232: if (ref($dirlist) eq 'ARRAY') {
14233: foreach my $item (@{$dirlist}) {
14234: if (ref($item) eq 'ARRAY') {
14235: if (exists($toplevel{$item->[0]})) {
14236: $duplicates .=
14237: &start_data_table_row().
14238: '<td><label><input type="radio" name="archive_overwrite_'.$num.'" '.
14239: 'value="0" checked="checked" />'.&mt('No').'</label>'.
14240: ' <label><input type="radio" name="archive_overwrite_'.$num.'" '.
14241: 'value="1" />'.&mt('Yes').'</label>'.
14242: '<input type="hidden" name="archive_overwrite_name_'.$num.'" value="'.$item->[0].'" /></td>'."\n".
14243: '<td>'.$item->[0].'</td>';
14244: if ($item->[2]) {
14245: $duplicates .= '<td>'.&mt('Directory').'</td>';
14246: } else {
14247: $duplicates .= '<td>'.&mt('File').'</td>';
14248: }
14249: $duplicates .= '<td>'.$item->[3].'</td>'.
14250: '<td>'.
14251: &Apache::lonlocal::locallocaltime($item->[4]).
14252: '</td>'.
14253: &end_data_table_row();
14254: $num ++;
14255: }
14256: }
14257: }
14258: }
14259: my $itemcount;
14260: if (@paths > 0) {
14261: $itemcount = scalar(@paths);
14262: } else {
14263: $itemcount = 1;
14264: }
1.1067 raeburn 14265: if ($is_camtasia) {
14266: $output .= $lt{'auto'}.'<br />'.
14267: '<span class="LC_nobreak">'.$lt{'proa'}.'<label>'.
1.1164 raeburn 14268: '<input type="radio" name="autoextract_camtasia" value="'.$is_camtasia.'" onclick="javascript:camtasiaToggle();" checked="checked" />'.
1.1067 raeburn 14269: $lt{'yes'}.'</label> <label>'.
14270: '<input type="radio" name="autoextract_camtasia" value="0" onclick="javascript:camtasiaToggle();" />'.
14271: $lt{'no'}.'</label></span><br />'.
14272: '<div id="camtasia_titles" style="display:block">'.
14273: &Apache::lonhtmlcommon::start_pick_box().
14274: &Apache::lonhtmlcommon::row_title($lt{'fold'}).
14275: '<input type="textbox" name="camtasia_foldername" value="'.$env{'form.comment'}.'" />'."\n".
14276: &Apache::lonhtmlcommon::row_closure().
14277: &Apache::lonhtmlcommon::row_title($lt{'movi'}).
14278: '<input type="textbox" name="camtasia_moviename" value="" />'."\n".
14279: &Apache::lonhtmlcommon::row_closure(1).
14280: &Apache::lonhtmlcommon::end_pick_box().
14281: '</div>';
14282: }
1.1065 raeburn 14283: $output .=
14284: '<input type="hidden" name="archive_overwrite_total" value="'.$num.'" />'.
1.1067 raeburn 14285: '<input type="hidden" name="archive_itemcount" value="'.$itemcount.'" />'.
14286: "\n";
1.1065 raeburn 14287: if ($duplicates ne '') {
14288: $output .= '<p><span class="LC_warning">'.
14289: &mt('Warning: decompression of the archive will overwrite the following items which already exist:').'</span><br />'.
14290: &start_data_table().
14291: &start_data_table_header_row().
14292: '<th>'.&mt('Overwrite?').'</th>'.
14293: '<th>'.&mt('Name').'</th>'.
14294: '<th>'.&mt('Type').'</th>'.
14295: '<th>'.&mt('Size').'</th>'.
14296: '<th>'.&mt('Last modified').'</th>'.
14297: &end_data_table_header_row().
14298: $duplicates.
14299: &end_data_table().
14300: '</p>';
14301: }
1.1067 raeburn 14302: $output .= '<input type="hidden" name="archiveurl" value="'.$archiveurl.'" />'."\n";
1.1053 raeburn 14303: if (ref($hiddenelements) eq 'HASH') {
14304: foreach my $hidden (sort(keys(%{$hiddenelements}))) {
14305: $output .= '<input type="hidden" name="'.$hidden.'" value="'.$hiddenelements->{$hidden}.'" />'."\n";
14306: }
14307: }
14308: $output .= <<"END";
1.1067 raeburn 14309: <br />
1.1053 raeburn 14310: <input type="submit" name="decompress" value="$lt{'extr'}" />
14311: </form>
14312: $noextract
14313: END
14314: return $output;
14315: }
14316:
1.1065 raeburn 14317: sub decompression_utility {
14318: my ($program) = @_;
14319: my @utilities = ('tar','gunzip','bunzip2','unzip');
14320: my $location;
14321: if (grep(/^\Q$program\E$/,@utilities)) {
14322: foreach my $dir ('/bin/','/usr/bin/','/usr/local/bin/','/sbin/',
14323: '/usr/sbin/') {
14324: if (-x $dir.$program) {
14325: $location = $dir.$program;
14326: last;
14327: }
14328: }
14329: }
14330: return $location;
14331: }
14332:
14333: sub list_archive_contents {
14334: my ($file,$pathsref) = @_;
14335: my (@cmd,$output);
14336: my $needsregexp;
14337: if ($file =~ /\.zip$/) {
14338: @cmd = (&decompression_utility('unzip'),"-l");
14339: $needsregexp = 1;
14340: } elsif (($file =~ m/\.tar\.gz$/) ||
14341: ($file =~ /\.tgz$/)) {
14342: @cmd = (&decompression_utility('tar'),"-ztf");
14343: } elsif ($file =~ /\.tar\.bz2$/) {
14344: @cmd = (&decompression_utility('tar'),"-jtf");
14345: } elsif ($file =~ m|\.tar$|) {
14346: @cmd = (&decompression_utility('tar'),"-tf");
14347: }
14348: if (@cmd) {
14349: undef($!);
14350: undef($@);
14351: if (open(my $fh,"-|", @cmd, $file)) {
14352: while (my $line = <$fh>) {
14353: $output .= $line;
14354: chomp($line);
14355: my $item;
14356: if ($needsregexp) {
14357: ($item) = ($line =~ /^\s*\d+\s+[\d\-]+\s+[\d:]+\s*(.+)$/);
14358: } else {
14359: $item = $line;
14360: }
14361: if ($item ne '') {
14362: unless (grep(/^\Q$item\E$/,@{$pathsref})) {
14363: push(@{$pathsref},$item);
14364: }
14365: }
14366: }
14367: close($fh);
14368: }
14369: }
14370: return $output;
14371: }
14372:
1.1053 raeburn 14373: sub decompress_uploaded_file {
14374: my ($file,$dir) = @_;
14375: &Apache::lonnet::appenv({'cgi.file' => $file});
14376: &Apache::lonnet::appenv({'cgi.dir' => $dir});
14377: my $result = &Apache::lonnet::ssi_body('/cgi-bin/decompress.pl');
14378: my ($handle) = ($env{'user.environment'} =~m{/([^/]+)\.id$});
14379: my $lonidsdir = $Apache::lonnet::perlvar{'lonIDsDir'};
14380: &Apache::lonnet::transfer_profile_to_env($lonidsdir,$handle,1);
14381: my $decompressed = $env{'cgi.decompressed'};
14382: &Apache::lonnet::delenv('cgi.file');
14383: &Apache::lonnet::delenv('cgi.dir');
14384: &Apache::lonnet::delenv('cgi.decompressed');
14385: return ($decompressed,$result);
14386: }
14387:
1.1055 raeburn 14388: sub process_decompression {
14389: my ($docudom,$docuname,$file,$destination,$dir_root,$hiddenelem) = @_;
1.1292 raeburn 14390: unless (($dir_root eq '/userfiles') && ($destination =~ m{^(docs|supplemental)/(default|\d+)/\d+$})) {
14391: return '<p class="LC_error">'.&mt('Not extracted.').'<br />'.
14392: &mt('Unexpected file path.').'</p>'."\n";
14393: }
14394: unless (($docudom =~ /^$match_domain$/) && ($docuname =~ /^$match_courseid$/)) {
14395: return '<p class="LC_error">'.&mt('Not extracted.').'<br />'.
14396: &mt('Unexpected course context.').'</p>'."\n";
14397: }
1.1293 raeburn 14398: unless ($file eq &Apache::lonnet::clean_filename($file)) {
1.1292 raeburn 14399: return '<p class="LC_error">'.&mt('Not extracted.').'<br />'.
14400: &mt('Filename contained unexpected characters.').'</p>'."\n";
14401: }
1.1055 raeburn 14402: my ($dir,$error,$warning,$output);
1.1180 raeburn 14403: if ($file !~ /\.(zip|tar|bz2|gz|tar.gz|tar.bz2|tgz)$/i) {
1.1120 bisitz 14404: $error = &mt('Filename not a supported archive file type.').
14405: '<br />'.&mt('Filename should end with one of: [_1].',
1.1055 raeburn 14406: '.zip, .tar, .bz2, .gz, .tar.gz, .tar.bz2, .tgz');
14407: } else {
14408: my $docuhome = &Apache::lonnet::homeserver($docuname,$docudom);
14409: if ($docuhome eq 'no_host') {
14410: $error = &mt('Could not determine home server for course.');
14411: } else {
14412: my @ids=&Apache::lonnet::current_machine_ids();
14413: my $currdir = "$dir_root/$destination";
14414: if (grep(/^\Q$docuhome\E$/,@ids)) {
14415: $dir = &LONCAPA::propath($docudom,$docuname).
14416: "$dir_root/$destination";
14417: } else {
14418: $dir = $Apache::lonnet::perlvar{'lonDocRoot'}.
14419: "$dir_root/$docudom/$docuname/$destination";
14420: unless (&Apache::lonnet::repcopy_userfile("$dir/$file") eq 'ok') {
14421: $error = &mt('Archive file not found.');
14422: }
14423: }
1.1065 raeburn 14424: my (@to_overwrite,@to_skip);
14425: if ($env{'form.archive_overwrite_total'} > 0) {
14426: my $total = $env{'form.archive_overwrite_total'};
14427: for (my $i=0; $i<$total; $i++) {
14428: if ($env{'form.archive_overwrite_'.$i} == 1) {
14429: push(@to_overwrite,$env{'form.archive_overwrite_name_'.$i});
14430: } elsif ($env{'form.archive_overwrite_'.$i} == 0) {
14431: push(@to_skip,$env{'form.archive_overwrite_name_'.$i});
14432: }
14433: }
14434: }
14435: my $numskip = scalar(@to_skip);
1.1292 raeburn 14436: my $numoverwrite = scalar(@to_overwrite);
14437: if (($numskip) && (!$numoverwrite)) {
1.1065 raeburn 14438: $warning = &mt('All items in the archive file already exist, and no overwriting of existing files has been requested.');
14439: } elsif ($dir eq '') {
1.1055 raeburn 14440: $error = &mt('Directory containing archive file unavailable.');
14441: } elsif (!$error) {
1.1065 raeburn 14442: my ($decompressed,$display);
1.1292 raeburn 14443: if (($numskip) || ($numoverwrite)) {
1.1065 raeburn 14444: my $tempdir = time.'_'.$$.int(rand(10000));
14445: mkdir("$dir/$tempdir",0755);
1.1292 raeburn 14446: if (&File::Copy::move("$dir/$file","$dir/$tempdir/$file")) {
14447: ($decompressed,$display) =
14448: &decompress_uploaded_file($file,"$dir/$tempdir");
14449: foreach my $item (@to_skip) {
14450: if (($item ne '') && ($item !~ /\.\./)) {
14451: if (-f "$dir/$tempdir/$item") {
14452: unlink("$dir/$tempdir/$item");
14453: } elsif (-d "$dir/$tempdir/$item") {
1.1300 raeburn 14454: &File::Path::remove_tree("$dir/$tempdir/$item",{ safe => 1 });
1.1292 raeburn 14455: }
14456: }
14457: }
14458: foreach my $item (@to_overwrite) {
14459: if ((-e "$dir/$tempdir/$item") && (-e "$dir/$item")) {
14460: if (($item ne '') && ($item !~ /\.\./)) {
14461: if (-f "$dir/$item") {
14462: unlink("$dir/$item");
14463: } elsif (-d "$dir/$item") {
1.1300 raeburn 14464: &File::Path::remove_tree("$dir/$item",{ safe => 1 });
1.1292 raeburn 14465: }
14466: &File::Copy::move("$dir/$tempdir/$item","$dir/$item");
14467: }
1.1065 raeburn 14468: }
14469: }
1.1292 raeburn 14470: if (&File::Copy::move("$dir/$tempdir/$file","$dir/$file")) {
1.1300 raeburn 14471: &File::Path::remove_tree("$dir/$tempdir",{ safe => 1 });
1.1292 raeburn 14472: }
1.1065 raeburn 14473: }
14474: } else {
14475: ($decompressed,$display) =
14476: &decompress_uploaded_file($file,$dir);
14477: }
1.1055 raeburn 14478: if ($decompressed eq 'ok') {
1.1065 raeburn 14479: $output = '<p class="LC_info">'.
14480: &mt('Files extracted successfully from archive.').
14481: '</p>'."\n";
1.1055 raeburn 14482: my ($warning,$result,@contents);
14483: my ($newdirlistref,$newlisterror) =
14484: &Apache::lonnet::dirlist($currdir,$docudom,
14485: $docuname,1);
14486: my (%is_dir,%changes,@newitems);
14487: my $dirptr = 16384;
1.1065 raeburn 14488: if (ref($newdirlistref) eq 'ARRAY') {
1.1055 raeburn 14489: foreach my $dir_line (@{$newdirlistref}) {
14490: my ($item,undef,undef,$testdir)=split(/\&/,$dir_line,5);
1.1292 raeburn 14491: unless (($item =~ /^\.+$/) || ($item eq $file)) {
1.1055 raeburn 14492: push(@newitems,$item);
14493: if ($dirptr&$testdir) {
14494: $is_dir{$item} = 1;
14495: }
14496: $changes{$item} = 1;
14497: }
14498: }
14499: }
14500: if (keys(%changes) > 0) {
14501: foreach my $item (sort(@newitems)) {
14502: if ($changes{$item}) {
14503: push(@contents,$item);
14504: }
14505: }
14506: }
14507: if (@contents > 0) {
1.1067 raeburn 14508: my $wantform;
14509: unless ($env{'form.autoextract_camtasia'}) {
14510: $wantform = 1;
14511: }
1.1056 raeburn 14512: my (%children,%parent,%dirorder,%titles);
1.1055 raeburn 14513: my ($count,$datatable) = &get_extracted($docudom,$docuname,
14514: $currdir,\%is_dir,
14515: \%children,\%parent,
1.1056 raeburn 14516: \@contents,\%dirorder,
14517: \%titles,$wantform);
1.1055 raeburn 14518: if ($datatable ne '') {
14519: $output .= &archive_options_form('decompressed',$datatable,
14520: $count,$hiddenelem);
1.1065 raeburn 14521: my $startcount = 6;
1.1055 raeburn 14522: $output .= &archive_javascript($startcount,$count,
1.1056 raeburn 14523: \%titles,\%children);
1.1055 raeburn 14524: }
1.1067 raeburn 14525: if ($env{'form.autoextract_camtasia'}) {
1.1164 raeburn 14526: my $version = $env{'form.autoextract_camtasia'};
1.1067 raeburn 14527: my %displayed;
14528: my $total = 1;
14529: $env{'form.archive_directory'} = [];
14530: foreach my $i (sort { $a <=> $b } keys(%dirorder)) {
14531: my $path = join('/',map { $titles{$_}; } @{$dirorder{$i}});
14532: $path =~ s{/$}{};
14533: my $item;
14534: if ($path ne '') {
14535: $item = "$path/$titles{$i}";
14536: } else {
14537: $item = $titles{$i};
14538: }
14539: $env{'form.archive_content_'.$i} = "$dir_root/$destination/$item";
14540: if ($item eq $contents[0]) {
14541: push(@{$env{'form.archive_directory'}},$i);
14542: $env{'form.archive_'.$i} = 'display';
14543: $env{'form.archive_title_'.$i} = $env{'form.camtasia_foldername'};
14544: $displayed{'folder'} = $i;
1.1164 raeburn 14545: } elsif ((($item eq "$contents[0]/index.html") && ($version == 6)) ||
14546: (($item eq "$contents[0]/$contents[0]".'.html') && ($version == 8))) {
1.1067 raeburn 14547: $env{'form.archive_'.$i} = 'display';
14548: $env{'form.archive_title_'.$i} = $env{'form.camtasia_moviename'};
14549: $displayed{'web'} = $i;
14550: } else {
1.1164 raeburn 14551: if ((($item eq "$contents[0]/media") && ($version == 6)) ||
14552: ((($item eq "$contents[0]/scripts") || ($item eq "$contents[0]/skins") ||
14553: ($item eq "$contents[0]/skins/express_show")) && ($version == 8))) {
1.1067 raeburn 14554: push(@{$env{'form.archive_directory'}},$i);
14555: }
14556: $env{'form.archive_'.$i} = 'dependency';
14557: }
14558: $total ++;
14559: }
14560: for (my $i=1; $i<$total; $i++) {
14561: next if ($i == $displayed{'web'});
14562: next if ($i == $displayed{'folder'});
14563: $env{'form.archive_dependent_on_'.$i} = $displayed{'web'};
14564: }
14565: $env{'form.phase'} = 'decompress_cleanup';
14566: $env{'form.archivedelete'} = 1;
14567: $env{'form.archive_count'} = $total-1;
14568: $output .=
14569: &process_extracted_files('coursedocs',$docudom,
14570: $docuname,$destination,
14571: $dir_root,$hiddenelem);
14572: }
1.1055 raeburn 14573: } else {
14574: $warning = &mt('No new items extracted from archive file.');
14575: }
14576: } else {
14577: $output = $display;
14578: $error = &mt('An error occurred during extraction from the archive file.');
14579: }
14580: }
14581: }
14582: }
14583: if ($error) {
14584: $output .= '<p class="LC_error">'.&mt('Not extracted.').'<br />'.
14585: $error.'</p>'."\n";
14586: }
14587: if ($warning) {
14588: $output .= '<p class="LC_warning">'.$warning.'</p>'."\n";
14589: }
14590: return $output;
14591: }
14592:
14593: sub get_extracted {
1.1056 raeburn 14594: my ($docudom,$docuname,$currdir,$is_dir,$children,$parent,$contents,$dirorder,
14595: $titles,$wantform) = @_;
1.1055 raeburn 14596: my $count = 0;
14597: my $depth = 0;
14598: my $datatable;
1.1056 raeburn 14599: my @hierarchy;
1.1055 raeburn 14600: return unless ((ref($is_dir) eq 'HASH') && (ref($children) eq 'HASH') &&
1.1056 raeburn 14601: (ref($parent) eq 'HASH') && (ref($contents) eq 'ARRAY') &&
14602: (ref($dirorder) eq 'HASH') && (ref($titles) eq 'HASH'));
1.1055 raeburn 14603: foreach my $item (@{$contents}) {
14604: $count ++;
1.1056 raeburn 14605: @{$dirorder->{$count}} = @hierarchy;
14606: $titles->{$count} = $item;
1.1055 raeburn 14607: &archive_hierarchy($depth,$count,$parent,$children);
14608: if ($wantform) {
14609: $datatable .= &archive_row($is_dir->{$item},$item,
14610: $currdir,$depth,$count);
14611: }
14612: if ($is_dir->{$item}) {
14613: $depth ++;
1.1056 raeburn 14614: push(@hierarchy,$count);
14615: $parent->{$depth} = $count;
1.1055 raeburn 14616: $datatable .=
14617: &recurse_extracted_archive("$currdir/$item",$docudom,$docuname,
1.1056 raeburn 14618: \$depth,\$count,\@hierarchy,$dirorder,
14619: $children,$parent,$titles,$wantform);
1.1055 raeburn 14620: $depth --;
1.1056 raeburn 14621: pop(@hierarchy);
1.1055 raeburn 14622: }
14623: }
14624: return ($count,$datatable);
14625: }
14626:
14627: sub recurse_extracted_archive {
1.1056 raeburn 14628: my ($currdir,$docudom,$docuname,$depth,$count,$hierarchy,$dirorder,
14629: $children,$parent,$titles,$wantform) = @_;
1.1055 raeburn 14630: my $result='';
1.1056 raeburn 14631: unless ((ref($depth)) && (ref($count)) && (ref($hierarchy) eq 'ARRAY') &&
14632: (ref($children) eq 'HASH') && (ref($parent) eq 'HASH') &&
14633: (ref($dirorder) eq 'HASH')) {
1.1055 raeburn 14634: return $result;
14635: }
14636: my $dirptr = 16384;
14637: my ($newdirlistref,$newlisterror) =
14638: &Apache::lonnet::dirlist($currdir,$docudom,$docuname,1);
14639: if (ref($newdirlistref) eq 'ARRAY') {
14640: foreach my $dir_line (@{$newdirlistref}) {
14641: my ($item,undef,undef,$testdir)=split(/\&/,$dir_line,5);
14642: unless ($item =~ /^\.+$/) {
14643: $$count ++;
1.1056 raeburn 14644: @{$dirorder->{$$count}} = @{$hierarchy};
14645: $titles->{$$count} = $item;
1.1055 raeburn 14646: &archive_hierarchy($$depth,$$count,$parent,$children);
1.1056 raeburn 14647:
1.1055 raeburn 14648: my $is_dir;
14649: if ($dirptr&$testdir) {
14650: $is_dir = 1;
14651: }
14652: if ($wantform) {
14653: $result .= &archive_row($is_dir,$item,$currdir,$$depth,$$count);
14654: }
14655: if ($is_dir) {
14656: $$depth ++;
1.1056 raeburn 14657: push(@{$hierarchy},$$count);
14658: $parent->{$$depth} = $$count;
1.1055 raeburn 14659: $result .=
14660: &recurse_extracted_archive("$currdir/$item",$docudom,
14661: $docuname,$depth,$count,
1.1056 raeburn 14662: $hierarchy,$dirorder,$children,
14663: $parent,$titles,$wantform);
1.1055 raeburn 14664: $$depth --;
1.1056 raeburn 14665: pop(@{$hierarchy});
1.1055 raeburn 14666: }
14667: }
14668: }
14669: }
14670: return $result;
14671: }
14672:
14673: sub archive_hierarchy {
14674: my ($depth,$count,$parent,$children) =@_;
14675: if ((ref($parent) eq 'HASH') && (ref($children) eq 'HASH')) {
14676: if (exists($parent->{$depth})) {
14677: $children->{$parent->{$depth}} .= $count.':';
14678: }
14679: }
14680: return;
14681: }
14682:
14683: sub archive_row {
14684: my ($is_dir,$item,$currdir,$depth,$count) = @_;
14685: my ($name) = ($item =~ m{([^/]+)$});
14686: my %choices = &Apache::lonlocal::texthash (
1.1059 raeburn 14687: 'display' => 'Add as file',
1.1055 raeburn 14688: 'dependency' => 'Include as dependency',
14689: 'discard' => 'Discard',
14690: );
14691: if ($is_dir) {
1.1059 raeburn 14692: $choices{'display'} = &mt('Add as folder');
1.1055 raeburn 14693: }
1.1056 raeburn 14694: my $output = &start_data_table_row().'<td align="right">'.$count.'</td>'."\n";
14695: my $offset = 0;
1.1055 raeburn 14696: foreach my $action ('display','dependency','discard') {
1.1056 raeburn 14697: $offset ++;
1.1065 raeburn 14698: if ($action ne 'display') {
14699: $offset ++;
14700: }
1.1055 raeburn 14701: $output .= '<td><span class="LC_nobreak">'.
14702: '<label><input type="radio" name="archive_'.$count.
14703: '" id="archive_'.$action.'_'.$count.'" value="'.$action.'"';
14704: my $text = $choices{$action};
14705: if ($is_dir) {
14706: $output .= ' onclick="javascript:propagateCheck(this.form,'."'$count'".');"';
14707: if ($action eq 'display') {
1.1059 raeburn 14708: $text = &mt('Add as folder');
1.1055 raeburn 14709: }
1.1056 raeburn 14710: } else {
14711: $output .= ' onclick="javascript:dependencyCheck(this.form,'."$count,$offset".');"';
14712:
14713: }
14714: $output .= ' /> '.$choices{$action}.'</label></span>';
14715: if ($action eq 'dependency') {
14716: $output .= '<div id="arc_depon_'.$count.'" style="display:none;">'."\n".
14717: &mt('Used by:').' <select name="archive_dependent_on_'.$count.'" '.
14718: 'onchange="propagateSelect(this.form,'."$count,$offset".')">'."\n".
14719: '<option value=""></option>'."\n".
14720: '</select>'."\n".
14721: '</div>';
1.1059 raeburn 14722: } elsif ($action eq 'display') {
14723: $output .= '<div id="arc_title_'.$count.'" style="display:none;">'."\n".
14724: &mt('Title:').' <input type="text" name="archive_title_'.$count.'" id="archive_title_'.$count.'" />'."\n".
14725: '</div>';
1.1055 raeburn 14726: }
1.1056 raeburn 14727: $output .= '</td>';
1.1055 raeburn 14728: }
14729: $output .= '<td><input type="hidden" name="archive_content_'.$count.'" value="'.
14730: &HTML::Entities::encode("$currdir/$item",'"<>&').'" />'.(' ' x 2);
14731: for (my $i=0; $i<$depth; $i++) {
14732: $output .= ('<img src="/adm/lonIcons/whitespace1.gif" class="LC_docs_spacer" alt="" />' x2)."\n";
14733: }
14734: if ($is_dir) {
14735: $output .= '<img src="/adm/lonIcons/navmap.folder.open.gif" alt="" /> '."\n".
14736: '<input type="hidden" name="archive_directory" value="'.$count.'" />'."\n";
14737: } else {
14738: $output .= '<input type="hidden" name="archive_file" value="'.$count.'" />'."\n";
14739: }
14740: $output .= ' '.$name.'</td>'."\n".
14741: &end_data_table_row();
14742: return $output;
14743: }
14744:
14745: sub archive_options_form {
1.1065 raeburn 14746: my ($form,$display,$count,$hiddenelem) = @_;
14747: my %lt = &Apache::lonlocal::texthash(
14748: perm => 'Permanently remove archive file?',
14749: hows => 'How should each extracted item be incorporated in the course?',
14750: cont => 'Content actions for all',
14751: addf => 'Add as folder/file',
14752: incd => 'Include as dependency for a displayed file',
14753: disc => 'Discard',
14754: no => 'No',
14755: yes => 'Yes',
14756: save => 'Save',
14757: );
14758: my $output = <<"END";
14759: <form name="$form" method="post" action="">
14760: <p><span class="LC_nobreak">$lt{'perm'}
14761: <label>
14762: <input type="radio" name="archivedelete" value="0" checked="checked" />$lt{'no'}
14763: </label>
14764:
14765: <label>
14766: <input type="radio" name="archivedelete" value="1" />$lt{'yes'}</label>
14767: </span>
14768: </p>
14769: <input type="hidden" name="phase" value="decompress_cleanup" />
14770: <br />$lt{'hows'}
14771: <div class="LC_columnSection">
14772: <fieldset>
14773: <legend>$lt{'cont'}</legend>
14774: <input type="button" value="$lt{'addf'}" onclick="javascript:checkAll(document.$form,'display');" />
14775: <input type="button" value="$lt{'incd'}" onclick="javascript:checkAll(document.$form,'dependency');" />
14776: <input type="button" value="$lt{'disc'}" onclick="javascript:checkAll(document.$form,'discard');" />
14777: </fieldset>
14778: </div>
14779: END
14780: return $output.
1.1055 raeburn 14781: &start_data_table()."\n".
1.1065 raeburn 14782: $display."\n".
1.1055 raeburn 14783: &end_data_table()."\n".
14784: '<input type="hidden" name="archive_count" value="'.$count.'" />'.
14785: $hiddenelem.
1.1065 raeburn 14786: '<br /><input type="submit" name="archive_submit" value="'.$lt{'save'}.'" />'.
1.1055 raeburn 14787: '</form>';
14788: }
14789:
14790: sub archive_javascript {
1.1056 raeburn 14791: my ($startcount,$numitems,$titles,$children) = @_;
14792: return unless ((ref($titles) eq 'HASH') && (ref($children) eq 'HASH'));
1.1059 raeburn 14793: my $maintitle = $env{'form.comment'};
1.1055 raeburn 14794: my $scripttag = <<START;
14795: <script type="text/javascript">
14796: // <![CDATA[
14797:
14798: function checkAll(form,prefix) {
14799: var idstr = new RegExp("^archive_"+prefix+"_\\\\d+\$");
14800: for (var i=0; i < form.elements.length; i++) {
14801: var id = form.elements[i].id;
14802: if ((id != '') && (id != undefined)) {
14803: if (idstr.test(id)) {
14804: if (form.elements[i].type == 'radio') {
14805: form.elements[i].checked = true;
1.1056 raeburn 14806: var nostart = i-$startcount;
1.1059 raeburn 14807: var offset = nostart%7;
14808: var count = (nostart-offset)/7;
1.1056 raeburn 14809: dependencyCheck(form,count,offset);
1.1055 raeburn 14810: }
14811: }
14812: }
14813: }
14814: }
14815:
14816: function propagateCheck(form,count) {
14817: if (count > 0) {
1.1059 raeburn 14818: var startelement = $startcount + ((count-1) * 7);
14819: for (var j=1; j<6; j++) {
14820: if ((j != 2) && (j != 4)) {
1.1056 raeburn 14821: var item = startelement + j;
14822: if (form.elements[item].type == 'radio') {
14823: if (form.elements[item].checked) {
14824: containerCheck(form,count,j);
14825: break;
14826: }
1.1055 raeburn 14827: }
14828: }
14829: }
14830: }
14831: }
14832:
14833: numitems = $numitems
1.1056 raeburn 14834: var titles = new Array(numitems);
14835: var parents = new Array(numitems);
1.1055 raeburn 14836: for (var i=0; i<numitems; i++) {
1.1056 raeburn 14837: parents[i] = new Array;
1.1055 raeburn 14838: }
1.1059 raeburn 14839: var maintitle = '$maintitle';
1.1055 raeburn 14840:
14841: START
14842:
1.1056 raeburn 14843: foreach my $container (sort { $a <=> $b } (keys(%{$children}))) {
14844: my @contents = split(/:/,$children->{$container});
1.1055 raeburn 14845: for (my $i=0; $i<@contents; $i ++) {
14846: $scripttag .= 'parents['.$container.']['.$i.'] = '.$contents[$i]."\n";
14847: }
14848: }
14849:
1.1056 raeburn 14850: foreach my $key (sort { $a <=> $b } (keys(%{$titles}))) {
14851: $scripttag .= "titles[$key] = '".$titles->{$key}."';\n";
14852: }
14853:
1.1055 raeburn 14854: $scripttag .= <<END;
14855:
14856: function containerCheck(form,count,offset) {
14857: if (count > 0) {
1.1056 raeburn 14858: dependencyCheck(form,count,offset);
1.1059 raeburn 14859: var item = (offset+$startcount)+7*(count-1);
1.1055 raeburn 14860: form.elements[item].checked = true;
14861: if(Object.prototype.toString.call(parents[count]) === '[object Array]') {
14862: if (parents[count].length > 0) {
14863: for (var j=0; j<parents[count].length; j++) {
1.1056 raeburn 14864: containerCheck(form,parents[count][j],offset);
14865: }
14866: }
14867: }
14868: }
14869: }
14870:
14871: function dependencyCheck(form,count,offset) {
14872: if (count > 0) {
1.1059 raeburn 14873: var chosen = (offset+$startcount)+7*(count-1);
14874: var depitem = $startcount + ((count-1) * 7) + 4;
1.1056 raeburn 14875: var currtype = form.elements[depitem].type;
14876: if (form.elements[chosen].value == 'dependency') {
14877: document.getElementById('arc_depon_'+count).style.display='block';
14878: form.elements[depitem].options.length = 0;
14879: form.elements[depitem].options[0] = new Option('Select','',true,true);
1.1085 raeburn 14880: for (var i=1; i<=numitems; i++) {
14881: if (i == count) {
14882: continue;
14883: }
1.1059 raeburn 14884: var startelement = $startcount + (i-1) * 7;
14885: for (var j=1; j<6; j++) {
14886: if ((j != 2) && (j!= 4)) {
1.1056 raeburn 14887: var item = startelement + j;
14888: if (form.elements[item].type == 'radio') {
14889: if (form.elements[item].checked) {
14890: if (form.elements[item].value == 'display') {
14891: var n = form.elements[depitem].options.length;
14892: form.elements[depitem].options[n] = new Option(titles[i],i,false,false);
14893: }
14894: }
14895: }
14896: }
14897: }
14898: }
14899: } else {
14900: document.getElementById('arc_depon_'+count).style.display='none';
14901: form.elements[depitem].options.length = 0;
14902: form.elements[depitem].options[0] = new Option('Select','',true,true);
14903: }
1.1059 raeburn 14904: titleCheck(form,count,offset);
1.1056 raeburn 14905: }
14906: }
14907:
14908: function propagateSelect(form,count,offset) {
14909: if (count > 0) {
1.1065 raeburn 14910: var item = (1+offset+$startcount)+7*(count-1);
1.1056 raeburn 14911: var picked = form.elements[item].options[form.elements[item].selectedIndex].value;
14912: if (Object.prototype.toString.call(parents[count]) === '[object Array]') {
14913: if (parents[count].length > 0) {
14914: for (var j=0; j<parents[count].length; j++) {
14915: containerSelect(form,parents[count][j],offset,picked);
1.1055 raeburn 14916: }
14917: }
14918: }
14919: }
14920: }
1.1056 raeburn 14921:
14922: function containerSelect(form,count,offset,picked) {
14923: if (count > 0) {
1.1065 raeburn 14924: var item = (offset+$startcount)+7*(count-1);
1.1056 raeburn 14925: if (form.elements[item].type == 'radio') {
14926: if (form.elements[item].value == 'dependency') {
14927: if (form.elements[item+1].type == 'select-one') {
14928: for (var i=0; i<form.elements[item+1].options.length; i++) {
14929: if (form.elements[item+1].options[i].value == picked) {
14930: form.elements[item+1].selectedIndex = i;
14931: break;
14932: }
14933: }
14934: }
14935: if (Object.prototype.toString.call(parents[count]) === '[object Array]') {
14936: if (parents[count].length > 0) {
14937: for (var j=0; j<parents[count].length; j++) {
14938: containerSelect(form,parents[count][j],offset,picked);
14939: }
14940: }
14941: }
14942: }
14943: }
14944: }
14945: }
14946:
1.1059 raeburn 14947: function titleCheck(form,count,offset) {
14948: if (count > 0) {
14949: var chosen = (offset+$startcount)+7*(count-1);
14950: var depitem = $startcount + ((count-1) * 7) + 2;
14951: var currtype = form.elements[depitem].type;
14952: if (form.elements[chosen].value == 'display') {
14953: document.getElementById('arc_title_'+count).style.display='block';
14954: if ((count==1) && ((parents[count].length > 0) || (numitems == 1))) {
14955: document.getElementById('archive_title_'+count).value=maintitle;
14956: }
14957: } else {
14958: document.getElementById('arc_title_'+count).style.display='none';
14959: if (currtype == 'text') {
14960: document.getElementById('archive_title_'+count).value='';
14961: }
14962: }
14963: }
14964: return;
14965: }
14966:
1.1055 raeburn 14967: // ]]>
14968: </script>
14969: END
14970: return $scripttag;
14971: }
14972:
14973: sub process_extracted_files {
1.1067 raeburn 14974: my ($context,$docudom,$docuname,$destination,$dir_root,$hiddenelem) = @_;
1.1055 raeburn 14975: my $numitems = $env{'form.archive_count'};
1.1294 raeburn 14976: return if ((!$numitems) || ($numitems =~ /\D/));
1.1055 raeburn 14977: my @ids=&Apache::lonnet::current_machine_ids();
14978: my ($prefix,$pathtocheck,$dir,$ishome,$error,$warning,%toplevelitems,%is_dir,
1.1067 raeburn 14979: %folders,%containers,%mapinner,%prompttofetch);
1.1055 raeburn 14980: my $docuhome = &Apache::lonnet::homeserver($docuname,$docudom);
14981: if (grep(/^\Q$docuhome\E$/,@ids)) {
14982: $prefix = &LONCAPA::propath($docudom,$docuname);
14983: $pathtocheck = "$dir_root/$destination";
14984: $dir = $dir_root;
14985: $ishome = 1;
14986: } else {
14987: $prefix = $Apache::lonnet::perlvar{'lonDocRoot'};
14988: $pathtocheck = "$dir_root/$docudom/$docuname/$destination";
1.1294 raeburn 14989: $dir = "$dir_root/$docudom/$docuname";
1.1055 raeburn 14990: }
14991: my $currdir = "$dir_root/$destination";
14992: (my $docstype,$mapinner{'0'}) = ($destination =~ m{^(docs|supplemental)/(\w+)/});
14993: if ($env{'form.folderpath'}) {
14994: my @items = split('&',$env{'form.folderpath'});
14995: $folders{'0'} = $items[-2];
1.1099 raeburn 14996: if ($env{'form.folderpath'} =~ /\:1$/) {
14997: $containers{'0'}='page';
14998: } else {
14999: $containers{'0'}='sequence';
15000: }
1.1055 raeburn 15001: }
15002: my @archdirs = &get_env_multiple('form.archive_directory');
15003: if ($numitems) {
15004: for (my $i=1; $i<=$numitems; $i++) {
15005: my $path = $env{'form.archive_content_'.$i};
15006: if ($path =~ m{^\Q$pathtocheck\E/([^/]+)$}) {
15007: my $item = $1;
15008: $toplevelitems{$item} = $i;
15009: if (grep(/^\Q$i\E$/,@archdirs)) {
15010: $is_dir{$item} = 1;
15011: }
15012: }
15013: }
15014: }
1.1067 raeburn 15015: my ($output,%children,%parent,%titles,%dirorder,$result);
1.1055 raeburn 15016: if (keys(%toplevelitems) > 0) {
15017: my @contents = sort(keys(%toplevelitems));
1.1056 raeburn 15018: (my $count,undef) = &get_extracted($docudom,$docuname,$currdir,\%is_dir,\%children,
15019: \%parent,\@contents,\%dirorder,\%titles);
1.1055 raeburn 15020: }
1.1066 raeburn 15021: my (%referrer,%orphaned,%todelete,%todeletedir,%newdest,%newseqid);
1.1055 raeburn 15022: if ($numitems) {
15023: for (my $i=1; $i<=$numitems; $i++) {
1.1086 raeburn 15024: next if ($env{'form.archive_'.$i} eq 'dependency');
1.1055 raeburn 15025: my $path = $env{'form.archive_content_'.$i};
15026: if ($path =~ /^\Q$pathtocheck\E/) {
15027: if ($env{'form.archive_'.$i} eq 'discard') {
15028: if ($prefix ne '' && $path ne '') {
15029: if (-e $prefix.$path) {
1.1066 raeburn 15030: if ((@archdirs > 0) &&
15031: (grep(/^\Q$i\E$/,@archdirs))) {
15032: $todeletedir{$prefix.$path} = 1;
15033: } else {
15034: $todelete{$prefix.$path} = 1;
15035: }
1.1055 raeburn 15036: }
15037: }
15038: } elsif ($env{'form.archive_'.$i} eq 'display') {
1.1059 raeburn 15039: my ($docstitle,$title,$url,$outer);
1.1055 raeburn 15040: ($title) = ($path =~ m{/([^/]+)$});
1.1059 raeburn 15041: $docstitle = $env{'form.archive_title_'.$i};
15042: if ($docstitle eq '') {
15043: $docstitle = $title;
15044: }
1.1055 raeburn 15045: $outer = 0;
1.1056 raeburn 15046: if (ref($dirorder{$i}) eq 'ARRAY') {
15047: if (@{$dirorder{$i}} > 0) {
15048: foreach my $item (reverse(@{$dirorder{$i}})) {
1.1055 raeburn 15049: if ($env{'form.archive_'.$item} eq 'display') {
15050: $outer = $item;
15051: last;
15052: }
15053: }
15054: }
15055: }
15056: my ($errtext,$fatal) =
15057: &LONCAPA::map::mapread('/uploaded/'.$docudom.'/'.$docuname.
15058: '/'.$folders{$outer}.'.'.
15059: $containers{$outer});
15060: next if ($fatal);
15061: if ((@archdirs > 0) && (grep(/^\Q$i\E$/,@archdirs))) {
15062: if ($context eq 'coursedocs') {
1.1056 raeburn 15063: $mapinner{$i} = time;
1.1055 raeburn 15064: $folders{$i} = 'default_'.$mapinner{$i};
15065: $containers{$i} = 'sequence';
15066: my $url = '/uploaded/'.$docudom.'/'.$docuname.'/'.
15067: $folders{$i}.'.'.$containers{$i};
15068: my $newidx = &LONCAPA::map::getresidx();
15069: $LONCAPA::map::resources[$newidx]=
1.1059 raeburn 15070: $docstitle.':'.$url.':false:normal:res';
1.1055 raeburn 15071: push(@LONCAPA::map::order,$newidx);
15072: my ($outtext,$errtext) =
15073: &LONCAPA::map::storemap('/uploaded/'.$docudom.'/'.
15074: $docuname.'/'.$folders{$outer}.
1.1087 raeburn 15075: '.'.$containers{$outer},1,1);
1.1056 raeburn 15076: $newseqid{$i} = $newidx;
1.1067 raeburn 15077: unless ($errtext) {
1.1294 raeburn 15078: $result .= '<li>'.&mt('Folder: [_1] added to course',
15079: &HTML::Entities::encode($docstitle,'<>&"')).
15080: '</li>'."\n";
1.1067 raeburn 15081: }
1.1055 raeburn 15082: }
15083: } else {
15084: if ($context eq 'coursedocs') {
15085: my $newidx=&LONCAPA::map::getresidx();
15086: my $url = '/uploaded/'.$docudom.'/'.$docuname.'/'.
15087: $docstype.'/'.$mapinner{$outer}.'/'.$newidx.'/'.
15088: $title;
1.1392 raeburn 15089: if (($outer !~ /\D/) &&
15090: (($mapinner{$outer} eq 'default') || ($mapinner{$outer} !~ /\D/)) &&
15091: ($newidx !~ /\D/)) {
1.1294 raeburn 15092: if (!-e "$prefix$dir/$docstype/$mapinner{$outer}") {
15093: mkdir("$prefix$dir/$docstype/$mapinner{$outer}",0755);
15094: }
15095: if (!-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx") {
15096: mkdir("$prefix$dir/$docstype/$mapinner{$outer}/$newidx");
15097: }
15098: if (-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx") {
15099: if (rename("$prefix$path","$prefix$dir/$docstype/$mapinner{$outer}/$newidx/$title")) {
15100: $newdest{$i} = "$prefix$dir/$docstype/$mapinner{$outer}/$newidx";
15101: unless ($ishome) {
15102: my $fetch = "$newdest{$i}/$title";
15103: $fetch =~ s/^\Q$prefix$dir\E//;
15104: $prompttofetch{$fetch} = 1;
15105: }
1.1292 raeburn 15106: }
1.1067 raeburn 15107: }
1.1294 raeburn 15108: $LONCAPA::map::resources[$newidx]=
15109: $docstitle.':'.$url.':false:normal:res';
15110: push(@LONCAPA::map::order, $newidx);
15111: my ($outtext,$errtext)=
15112: &LONCAPA::map::storemap('/uploaded/'.$docudom.'/'.
15113: $docuname.'/'.$folders{$outer}.
15114: '.'.$containers{$outer},1,1);
15115: unless ($errtext) {
15116: if (-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx/$title") {
15117: $result .= '<li>'.&mt('File: [_1] added to course',
15118: &HTML::Entities::encode($docstitle,'<>&"')).
15119: '</li>'."\n";
15120: }
1.1067 raeburn 15121: }
1.1294 raeburn 15122: } else {
15123: $warning .= &mt('Item extracted from archive: [_1] has unexpected path.',
15124: &HTML::Entities::encode($path,'<>&"')).'<br />';
1.1296 raeburn 15125: }
1.1055 raeburn 15126: }
15127: }
1.1086 raeburn 15128: }
15129: } else {
1.1294 raeburn 15130: $warning .= &mt('Item extracted from archive: [_1] has unexpected path.',
15131: &HTML::Entities::encode($path,'<>&"')).'<br />';
1.1086 raeburn 15132: }
15133: }
15134: for (my $i=1; $i<=$numitems; $i++) {
15135: next unless ($env{'form.archive_'.$i} eq 'dependency');
15136: my $path = $env{'form.archive_content_'.$i};
15137: if ($path =~ /^\Q$pathtocheck\E/) {
15138: my ($title) = ($path =~ m{/([^/]+)$});
15139: $referrer{$i} = $env{'form.archive_dependent_on_'.$i};
15140: if ($env{'form.archive_'.$referrer{$i}} eq 'display') {
15141: if (ref($dirorder{$i}) eq 'ARRAY') {
15142: my ($itemidx,$fullpath,$relpath);
15143: if (ref($dirorder{$referrer{$i}}) eq 'ARRAY') {
15144: my $container = $dirorder{$referrer{$i}}->[-1];
1.1056 raeburn 15145: for (my $j=0; $j<@{$dirorder{$i}}; $j++) {
1.1086 raeburn 15146: if ($dirorder{$i}->[$j] eq $container) {
15147: $itemidx = $j;
1.1056 raeburn 15148: }
15149: }
1.1086 raeburn 15150: }
15151: if ($itemidx eq '') {
15152: $itemidx = 0;
15153: }
15154: if (grep(/^\Q$referrer{$i}\E$/,@archdirs)) {
15155: if ($mapinner{$referrer{$i}}) {
15156: $fullpath = "$prefix$dir/$docstype/$mapinner{$referrer{$i}}";
15157: for (my $j=$itemidx; $j<@{$dirorder{$i}}; $j++) {
15158: if (grep(/^\Q$dirorder{$i}->[$j]\E$/,@archdirs)) {
15159: unless (defined($newseqid{$dirorder{$i}->[$j]})) {
15160: $fullpath .= '/'.$titles{$dirorder{$i}->[$j]};
15161: $relpath .= '/'.$titles{$dirorder{$i}->[$j]};
15162: if (!-e $fullpath) {
15163: mkdir($fullpath,0755);
1.1056 raeburn 15164: }
15165: }
1.1086 raeburn 15166: } else {
15167: last;
1.1056 raeburn 15168: }
1.1086 raeburn 15169: }
15170: }
15171: } elsif ($newdest{$referrer{$i}}) {
15172: $fullpath = $newdest{$referrer{$i}};
15173: for (my $j=$itemidx; $j<@{$dirorder{$i}}; $j++) {
15174: if ($env{'form.archive_'.$dirorder{$i}->[$j]} eq 'discard') {
15175: $orphaned{$i} = $env{'form.archive_'.$dirorder{$i}->[$j]};
15176: last;
15177: } elsif (grep(/^\Q$dirorder{$i}->[$j]\E$/,@archdirs)) {
15178: unless (defined($newseqid{$dirorder{$i}->[$j]})) {
15179: $fullpath .= '/'.$titles{$dirorder{$i}->[$j]};
15180: $relpath .= '/'.$titles{$dirorder{$i}->[$j]};
15181: if (!-e $fullpath) {
15182: mkdir($fullpath,0755);
1.1056 raeburn 15183: }
15184: }
1.1086 raeburn 15185: } else {
15186: last;
1.1056 raeburn 15187: }
1.1055 raeburn 15188: }
15189: }
1.1086 raeburn 15190: if ($fullpath ne '') {
15191: if (-e "$prefix$path") {
1.1292 raeburn 15192: unless (rename("$prefix$path","$fullpath/$title")) {
15193: $warning .= &mt('Failed to rename dependency').'<br />';
15194: }
1.1086 raeburn 15195: }
15196: if (-e "$fullpath/$title") {
15197: my $showpath;
15198: if ($relpath ne '') {
15199: $showpath = "$relpath/$title";
15200: } else {
15201: $showpath = "/$title";
15202: }
1.1294 raeburn 15203: $result .= '<li>'.&mt('[_1] included as a dependency',
15204: &HTML::Entities::encode($showpath,'<>&"')).
15205: '</li>'."\n";
1.1292 raeburn 15206: unless ($ishome) {
15207: my $fetch = "$fullpath/$title";
15208: $fetch =~ s/^\Q$prefix$dir\E//;
15209: $prompttofetch{$fetch} = 1;
15210: }
1.1086 raeburn 15211: }
15212: }
1.1055 raeburn 15213: }
1.1086 raeburn 15214: } elsif ($env{'form.archive_'.$referrer{$i}} eq 'discard') {
15215: $warning .= &mt('[_1] is a dependency of [_2], which was discarded.',
1.1294 raeburn 15216: &HTML::Entities::encode($path,'<>&"'),
15217: &HTML::Entities::encode($env{'form.archive_content_'.$referrer{$i}},'<>&"')).
15218: '<br />';
1.1055 raeburn 15219: }
15220: } else {
1.1294 raeburn 15221: $warning .= &mt('Item extracted from archive: [_1] has unexpected path.',
1.1296 raeburn 15222: &HTML::Entities::encode($path)).'<br />';
1.1055 raeburn 15223: }
15224: }
15225: if (keys(%todelete)) {
15226: foreach my $key (keys(%todelete)) {
15227: unlink($key);
1.1066 raeburn 15228: }
15229: }
15230: if (keys(%todeletedir)) {
15231: foreach my $key (keys(%todeletedir)) {
15232: rmdir($key);
15233: }
15234: }
15235: foreach my $dir (sort(keys(%is_dir))) {
15236: if (($pathtocheck ne '') && ($dir ne '')) {
15237: &cleanup_empty_dirs($prefix."$pathtocheck/$dir");
1.1055 raeburn 15238: }
15239: }
1.1067 raeburn 15240: if ($result ne '') {
15241: $output .= '<ul>'."\n".
15242: $result."\n".
15243: '</ul>';
15244: }
15245: unless ($ishome) {
15246: my $replicationfail;
15247: foreach my $item (keys(%prompttofetch)) {
15248: my $fetchresult= &Apache::lonnet::reply('fetchuserfile:'.$item,$docuhome);
15249: unless ($fetchresult eq 'ok') {
15250: $replicationfail .= '<li>'.$item.'</li>'."\n";
15251: }
15252: }
15253: if ($replicationfail) {
15254: $output .= '<p class="LC_error">'.
15255: &mt('Course home server failed to retrieve:').'<ul>'.
15256: $replicationfail.
15257: '</ul></p>';
15258: }
15259: }
1.1055 raeburn 15260: } else {
15261: $warning = &mt('No items found in archive.');
15262: }
15263: if ($error) {
15264: $output .= '<p class="LC_error">'.&mt('Not extracted.').'<br />'.
15265: $error.'</p>'."\n";
15266: }
15267: if ($warning) {
15268: $output .= '<p class="LC_warning">'.$warning.'</p>'."\n";
15269: }
15270: return $output;
15271: }
15272:
1.1066 raeburn 15273: sub cleanup_empty_dirs {
15274: my ($path) = @_;
15275: if (($path ne '') && (-d $path)) {
15276: if (opendir(my $dirh,$path)) {
15277: my @dircontents = grep(!/^\./,readdir($dirh));
15278: my $numitems = 0;
15279: foreach my $item (@dircontents) {
15280: if (-d "$path/$item") {
1.1111 raeburn 15281: &cleanup_empty_dirs("$path/$item");
1.1066 raeburn 15282: if (-e "$path/$item") {
15283: $numitems ++;
15284: }
15285: } else {
15286: $numitems ++;
15287: }
15288: }
15289: if ($numitems == 0) {
15290: rmdir($path);
15291: }
15292: closedir($dirh);
15293: }
15294: }
15295: return;
15296: }
15297:
1.41 ng 15298: =pod
1.45 matthew 15299:
1.1162 raeburn 15300: =item * &get_folder_hierarchy()
1.1068 raeburn 15301:
15302: Provides hierarchy of names of folders/sub-folders containing the current
15303: item,
15304:
15305: Inputs: 3
15306: - $navmap - navmaps object
15307:
15308: - $map - url for map (either the trigger itself, or map containing
15309: the resource, which is the trigger).
15310:
15311: - $showitem - 1 => show title for map itself; 0 => do not show.
15312:
15313: Outputs: 1 @pathitems - array of folder/subfolder names.
15314:
15315: =cut
15316:
15317: sub get_folder_hierarchy {
15318: my ($navmap,$map,$showitem) = @_;
15319: my @pathitems;
15320: if (ref($navmap)) {
15321: my $mapres = $navmap->getResourceByUrl($map);
15322: if (ref($mapres)) {
15323: my $pcslist = $mapres->map_hierarchy();
15324: if ($pcslist ne '') {
15325: my @pcs = split(/,/,$pcslist);
15326: foreach my $pc (@pcs) {
15327: if ($pc == 1) {
1.1129 raeburn 15328: push(@pathitems,&mt('Main Content'));
1.1068 raeburn 15329: } else {
15330: my $res = $navmap->getByMapPc($pc);
15331: if (ref($res)) {
15332: my $title = $res->compTitle();
15333: $title =~ s/\W+/_/g;
15334: if ($title ne '') {
15335: push(@pathitems,$title);
15336: }
15337: }
15338: }
15339: }
15340: }
1.1071 raeburn 15341: if ($showitem) {
15342: if ($mapres->{ID} eq '0.0') {
1.1129 raeburn 15343: push(@pathitems,&mt('Main Content'));
1.1071 raeburn 15344: } else {
15345: my $maptitle = $mapres->compTitle();
15346: $maptitle =~ s/\W+/_/g;
15347: if ($maptitle ne '') {
15348: push(@pathitems,$maptitle);
15349: }
1.1068 raeburn 15350: }
15351: }
15352: }
15353: }
15354: return @pathitems;
15355: }
15356:
15357: =pod
15358:
1.1015 raeburn 15359: =item * &get_turnedin_filepath()
15360:
15361: Determines path in a user's portfolio file for storage of files uploaded
15362: to a specific essayresponse or dropbox item.
15363:
15364: Inputs: 3 required + 1 optional.
15365: $symb is symb for resource, $uname and $udom are for current user (required).
15366: $caller is optional (can be "submission", if routine is called when storing
15367: an upoaded file when "Submit Answer" button was pressed).
15368:
15369: Returns array containing $path and $multiresp.
15370: $path is path in portfolio. $multiresp is 1 if this resource contains more
15371: than one file upload item. Callers of routine should append partid as a
15372: subdirectory to $path in cases where $multiresp is 1.
15373:
15374: Called by: homework/essayresponse.pm and homework/structuretags.pm
15375:
15376: =cut
15377:
15378: sub get_turnedin_filepath {
15379: my ($symb,$uname,$udom,$caller) = @_;
15380: my ($map,$resid,$resurl)=&Apache::lonnet::decode_symb($symb);
15381: my $turnindir;
15382: my %userhash = &Apache::lonnet::userenvironment($udom,$uname,'turnindir');
15383: $turnindir = $userhash{'turnindir'};
15384: my ($path,$multiresp);
15385: if ($turnindir eq '') {
15386: if ($caller eq 'submission') {
15387: $turnindir = &mt('turned in');
15388: $turnindir =~ s/\W+/_/g;
15389: my %newhash = (
15390: 'turnindir' => $turnindir,
15391: );
15392: &Apache::lonnet::put('environment',\%newhash,$udom,$uname);
15393: }
15394: }
15395: if ($turnindir ne '') {
15396: $path = '/'.$turnindir.'/';
15397: my ($multipart,$turnin,@pathitems);
15398: my $navmap = Apache::lonnavmaps::navmap->new();
15399: if (defined($navmap)) {
15400: my $mapres = $navmap->getResourceByUrl($map);
15401: if (ref($mapres)) {
15402: my $pcslist = $mapres->map_hierarchy();
15403: if ($pcslist ne '') {
15404: foreach my $pc (split(/,/,$pcslist)) {
15405: my $res = $navmap->getByMapPc($pc);
15406: if (ref($res)) {
15407: my $title = $res->compTitle();
15408: $title =~ s/\W+/_/g;
15409: if ($title ne '') {
1.1149 raeburn 15410: if (($pc > 1) && (length($title) > 12)) {
15411: $title = substr($title,0,12);
15412: }
1.1015 raeburn 15413: push(@pathitems,$title);
15414: }
15415: }
15416: }
15417: }
15418: my $maptitle = $mapres->compTitle();
15419: $maptitle =~ s/\W+/_/g;
15420: if ($maptitle ne '') {
1.1149 raeburn 15421: if (length($maptitle) > 12) {
15422: $maptitle = substr($maptitle,0,12);
15423: }
1.1015 raeburn 15424: push(@pathitems,$maptitle);
15425: }
15426: unless ($env{'request.state'} eq 'construct') {
15427: my $res = $navmap->getBySymb($symb);
15428: if (ref($res)) {
15429: my $partlist = $res->parts();
15430: my $totaluploads = 0;
15431: if (ref($partlist) eq 'ARRAY') {
15432: foreach my $part (@{$partlist}) {
15433: my @types = $res->responseType($part);
15434: my @ids = $res->responseIds($part);
15435: for (my $i=0; $i < scalar(@ids); $i++) {
15436: if ($types[$i] eq 'essay') {
15437: my $partid = $part.'_'.$ids[$i];
15438: if (&Apache::lonnet::EXT("resource.$partid.uploadedfiletypes") ne '') {
15439: $totaluploads ++;
15440: }
15441: }
15442: }
15443: }
15444: if ($totaluploads > 1) {
15445: $multiresp = 1;
15446: }
15447: }
15448: }
15449: }
15450: } else {
15451: return;
15452: }
15453: } else {
15454: return;
15455: }
15456: my $restitle=&Apache::lonnet::gettitle($symb);
15457: $restitle =~ s/\W+/_/g;
15458: if ($restitle eq '') {
15459: $restitle = ($resurl =~ m{/[^/]+$});
15460: if ($restitle eq '') {
15461: $restitle = time;
15462: }
15463: }
1.1149 raeburn 15464: if (length($restitle) > 12) {
15465: $restitle = substr($restitle,0,12);
15466: }
1.1015 raeburn 15467: push(@pathitems,$restitle);
15468: $path .= join('/',@pathitems);
15469: }
15470: return ($path,$multiresp);
15471: }
15472:
15473: =pod
15474:
1.464 albertel 15475: =back
1.41 ng 15476:
1.112 bowersj2 15477: =head1 CSV Upload/Handling functions
1.38 albertel 15478:
1.41 ng 15479: =over 4
15480:
1.648 raeburn 15481: =item * &upfile_store($r)
1.41 ng 15482:
15483: Store uploaded file, $r should be the HTTP Request object,
1.258 albertel 15484: needs $env{'form.upfile'}
1.41 ng 15485: returns $datatoken to be put into hidden field
15486:
15487: =cut
1.31 albertel 15488:
15489: sub upfile_store {
15490: my $r=shift;
1.258 albertel 15491: $env{'form.upfile'}=~s/\r/\n/gs;
15492: $env{'form.upfile'}=~s/\f/\n/gs;
15493: $env{'form.upfile'}=~s/\n+/\n/gs;
15494: $env{'form.upfile'}=~s/\n+$//gs;
1.31 albertel 15495:
1.1299 raeburn 15496: my $datatoken = &valid_datatoken($env{'user.name'}.'_'.$env{'user.domain'}.
15497: '_enroll_'.$env{'request.course.id'}.'_'.
15498: time.'_'.$$);
15499: return if ($datatoken eq '');
15500:
1.31 albertel 15501: {
1.158 raeburn 15502: my $datafile = $r->dir_config('lonDaemons').
15503: '/tmp/'.$datatoken.'.tmp';
1.1317 raeburn 15504: if ( open(my $fh,'>',$datafile) ) {
1.258 albertel 15505: print $fh $env{'form.upfile'};
1.158 raeburn 15506: close($fh);
15507: }
1.31 albertel 15508: }
15509: return $datatoken;
15510: }
15511:
1.56 matthew 15512: =pod
15513:
1.1290 raeburn 15514: =item * &load_tmp_file($r,$datatoken)
1.41 ng 15515:
15516: Load uploaded file from tmp, $r should be the HTTP Request object,
1.1290 raeburn 15517: $datatoken is the name to assign to the temporary file.
1.258 albertel 15518: sets $env{'form.upfile'} to the contents of the file
1.41 ng 15519:
15520: =cut
1.31 albertel 15521:
15522: sub load_tmp_file {
1.1290 raeburn 15523: my ($r,$datatoken) = @_;
15524: return if ($datatoken eq '');
1.31 albertel 15525: my @studentdata=();
15526: {
1.158 raeburn 15527: my $studentfile = $r->dir_config('lonDaemons').
1.1290 raeburn 15528: '/tmp/'.$datatoken.'.tmp';
1.1317 raeburn 15529: if ( open(my $fh,'<',$studentfile) ) {
1.158 raeburn 15530: @studentdata=<$fh>;
15531: close($fh);
15532: }
1.31 albertel 15533: }
1.258 albertel 15534: $env{'form.upfile'}=join('',@studentdata);
1.31 albertel 15535: }
15536:
1.1290 raeburn 15537: sub valid_datatoken {
15538: my ($datatoken) = @_;
1.1325 raeburn 15539: if ($datatoken =~ /^$match_username\_$match_domain\_enroll_(|$match_domain\_$match_courseid)\_\d+_\d+$/) {
1.1290 raeburn 15540: return $datatoken;
15541: }
15542: return;
15543: }
15544:
1.56 matthew 15545: =pod
15546:
1.648 raeburn 15547: =item * &upfile_record_sep()
1.41 ng 15548:
15549: Separate uploaded file into records
15550: returns array of records,
1.258 albertel 15551: needs $env{'form.upfile'} and $env{'form.upfiletype'}
1.41 ng 15552:
15553: =cut
1.31 albertel 15554:
15555: sub upfile_record_sep {
1.258 albertel 15556: if ($env{'form.upfiletype'} eq 'xml') {
1.31 albertel 15557: } else {
1.248 albertel 15558: my @records;
1.258 albertel 15559: foreach my $line (split(/\n/,$env{'form.upfile'})) {
1.248 albertel 15560: if ($line=~/^\s*$/) { next; }
15561: push(@records,$line);
15562: }
15563: return @records;
1.31 albertel 15564: }
15565: }
15566:
1.56 matthew 15567: =pod
15568:
1.648 raeburn 15569: =item * &record_sep($record)
1.41 ng 15570:
1.258 albertel 15571: Separate a record into fields $record should be an item from the upfile_record_sep(), needs $env{'form.upfiletype'}
1.41 ng 15572:
15573: =cut
15574:
1.263 www 15575: sub takeleft {
15576: my $index=shift;
15577: return substr('0000'.$index,-4,4);
15578: }
15579:
1.31 albertel 15580: sub record_sep {
15581: my $record=shift;
15582: my %components=();
1.258 albertel 15583: if ($env{'form.upfiletype'} eq 'xml') {
15584: } elsif ($env{'form.upfiletype'} eq 'space') {
1.31 albertel 15585: my $i=0;
1.356 albertel 15586: foreach my $field (split(/\s+/,$record)) {
1.31 albertel 15587: $field=~s/^(\"|\')//;
15588: $field=~s/(\"|\')$//;
1.263 www 15589: $components{&takeleft($i)}=$field;
1.31 albertel 15590: $i++;
15591: }
1.258 albertel 15592: } elsif ($env{'form.upfiletype'} eq 'tab') {
1.31 albertel 15593: my $i=0;
1.356 albertel 15594: foreach my $field (split(/\t/,$record)) {
1.31 albertel 15595: $field=~s/^(\"|\')//;
15596: $field=~s/(\"|\')$//;
1.263 www 15597: $components{&takeleft($i)}=$field;
1.31 albertel 15598: $i++;
15599: }
15600: } else {
1.561 www 15601: my $separator=',';
1.480 banghart 15602: if ($env{'form.upfiletype'} eq 'semisv') {
1.561 www 15603: $separator=';';
1.480 banghart 15604: }
1.31 albertel 15605: my $i=0;
1.561 www 15606: # the character we are looking for to indicate the end of a quote or a record
15607: my $looking_for=$separator;
15608: # do not add the characters to the fields
15609: my $ignore=0;
15610: # we just encountered a separator (or the beginning of the record)
15611: my $just_found_separator=1;
15612: # store the field we are working on here
15613: my $field='';
15614: # work our way through all characters in record
15615: foreach my $character ($record=~/(.)/g) {
15616: if ($character eq $looking_for) {
15617: if ($character ne $separator) {
15618: # Found the end of a quote, again looking for separator
15619: $looking_for=$separator;
15620: $ignore=1;
15621: } else {
15622: # Found a separator, store away what we got
15623: $components{&takeleft($i)}=$field;
15624: $i++;
15625: $just_found_separator=1;
15626: $ignore=0;
15627: $field='';
15628: }
15629: next;
15630: }
15631: # single or double quotation marks after a separator indicate beginning of a quote
15632: # we are now looking for the end of the quote and need to ignore separators
15633: if ((($character eq '"') || ($character eq "'")) && ($just_found_separator)) {
15634: $looking_for=$character;
15635: next;
15636: }
15637: # ignore would be true after we reached the end of a quote
15638: if ($ignore) { next; }
15639: if (($just_found_separator) && ($character=~/\s/)) { next; }
15640: $field.=$character;
15641: $just_found_separator=0;
1.31 albertel 15642: }
1.561 www 15643: # catch the very last entry, since we never encountered the separator
15644: $components{&takeleft($i)}=$field;
1.31 albertel 15645: }
15646: return %components;
15647: }
15648:
1.144 matthew 15649: ######################################################
15650: ######################################################
15651:
1.56 matthew 15652: =pod
15653:
1.648 raeburn 15654: =item * &upfile_select_html()
1.41 ng 15655:
1.144 matthew 15656: Return HTML code to select a file from the users machine and specify
15657: the file type.
1.41 ng 15658:
15659: =cut
15660:
1.144 matthew 15661: ######################################################
15662: ######################################################
1.31 albertel 15663: sub upfile_select_html {
1.144 matthew 15664: my %Types = (
15665: csv => &mt('CSV (comma separated values, spreadsheet)'),
1.480 banghart 15666: semisv => &mt('Semicolon separated values'),
1.144 matthew 15667: space => &mt('Space separated'),
15668: tab => &mt('Tabulator separated'),
15669: # xml => &mt('HTML/XML'),
15670: );
15671: my $Str = '<input type="file" name="upfile" size="50" />'.
1.727 riegler 15672: '<br />'.&mt('Type').': <select name="upfiletype">';
1.144 matthew 15673: foreach my $type (sort(keys(%Types))) {
15674: $Str .= '<option value="'.$type.'" >'.$Types{$type}."</option>\n";
15675: }
15676: $Str .= "</select>\n";
15677: return $Str;
1.31 albertel 15678: }
15679:
1.301 albertel 15680: sub get_samples {
15681: my ($records,$toget) = @_;
15682: my @samples=({});
15683: my $got=0;
15684: foreach my $rec (@$records) {
15685: my %temp = &record_sep($rec);
15686: if (! grep(/\S/, values(%temp))) { next; }
15687: if (%temp) {
15688: $samples[$got]=\%temp;
15689: $got++;
15690: if ($got == $toget) { last; }
15691: }
15692: }
15693: return \@samples;
15694: }
15695:
1.144 matthew 15696: ######################################################
15697: ######################################################
15698:
1.56 matthew 15699: =pod
15700:
1.648 raeburn 15701: =item * &csv_print_samples($r,$records)
1.41 ng 15702:
15703: Prints a table of sample values from each column uploaded $r is an
15704: Apache Request ref, $records is an arrayref from
15705: &Apache::loncommon::upfile_record_sep
15706:
15707: =cut
15708:
1.144 matthew 15709: ######################################################
15710: ######################################################
1.31 albertel 15711: sub csv_print_samples {
15712: my ($r,$records) = @_;
1.662 bisitz 15713: my $samples = &get_samples($records,5);
1.301 albertel 15714:
1.594 raeburn 15715: $r->print(&mt('Samples').'<br />'.&start_data_table().
15716: &start_data_table_header_row());
1.356 albertel 15717: foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
1.845 bisitz 15718: $r->print('<th>'.&mt('Column [_1]',($sample+1)).'</th>'); }
1.594 raeburn 15719: $r->print(&end_data_table_header_row());
1.301 albertel 15720: foreach my $hash (@$samples) {
1.594 raeburn 15721: $r->print(&start_data_table_row());
1.356 albertel 15722: foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
1.31 albertel 15723: $r->print('<td>');
1.356 albertel 15724: if (defined($$hash{$sample})) { $r->print($$hash{$sample}); }
1.31 albertel 15725: $r->print('</td>');
15726: }
1.594 raeburn 15727: $r->print(&end_data_table_row());
1.31 albertel 15728: }
1.594 raeburn 15729: $r->print(&end_data_table().'<br />'."\n");
1.31 albertel 15730: }
15731:
1.144 matthew 15732: ######################################################
15733: ######################################################
15734:
1.56 matthew 15735: =pod
15736:
1.648 raeburn 15737: =item * &csv_print_select_table($r,$records,$d)
1.41 ng 15738:
15739: Prints a table to create associations between values and table columns.
1.144 matthew 15740:
1.41 ng 15741: $r is an Apache Request ref,
15742: $records is an arrayref from &Apache::loncommon::upfile_record_sep,
1.174 matthew 15743: $d is an array of 2 element arrays (internal name, displayed name,defaultcol)
1.41 ng 15744:
15745: =cut
15746:
1.144 matthew 15747: ######################################################
15748: ######################################################
1.31 albertel 15749: sub csv_print_select_table {
15750: my ($r,$records,$d) = @_;
1.301 albertel 15751: my $i=0;
15752: my $samples = &get_samples($records,1);
1.144 matthew 15753: $r->print(&mt('Associate columns with student attributes.')."\n".
1.594 raeburn 15754: &start_data_table().&start_data_table_header_row().
1.144 matthew 15755: '<th>'.&mt('Attribute').'</th>'.
1.594 raeburn 15756: '<th>'.&mt('Column').'</th>'.
15757: &end_data_table_header_row()."\n");
1.356 albertel 15758: foreach my $array_ref (@$d) {
15759: my ($value,$display,$defaultcol)=@{ $array_ref };
1.729 raeburn 15760: $r->print(&start_data_table_row().'<td>'.$display.'</td>');
1.31 albertel 15761:
1.875 bisitz 15762: $r->print('<td><select name="f'.$i.'"'.
1.32 matthew 15763: ' onchange="javascript:flip(this.form,'.$i.');">');
1.31 albertel 15764: $r->print('<option value="none"></option>');
1.356 albertel 15765: foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
15766: $r->print('<option value="'.$sample.'"'.
15767: ($sample eq $defaultcol ? ' selected="selected" ' : '').
1.662 bisitz 15768: '>'.&mt('Column [_1]',($sample+1)).'</option>');
1.31 albertel 15769: }
1.594 raeburn 15770: $r->print('</select></td>'.&end_data_table_row()."\n");
1.31 albertel 15771: $i++;
15772: }
1.594 raeburn 15773: $r->print(&end_data_table());
1.31 albertel 15774: $i--;
15775: return $i;
15776: }
1.56 matthew 15777:
1.144 matthew 15778: ######################################################
15779: ######################################################
15780:
1.56 matthew 15781: =pod
1.31 albertel 15782:
1.648 raeburn 15783: =item * &csv_samples_select_table($r,$records,$d)
1.41 ng 15784:
15785: Prints a table of sample values from the upload and can make associate samples to internal names.
15786:
15787: $r is an Apache Request ref,
15788: $records is an arrayref from &Apache::loncommon::upfile_record_sep,
15789: $d is an array of 2 element arrays (internal name, displayed name)
15790:
15791: =cut
15792:
1.144 matthew 15793: ######################################################
15794: ######################################################
1.31 albertel 15795: sub csv_samples_select_table {
15796: my ($r,$records,$d) = @_;
15797: my $i=0;
1.144 matthew 15798: #
1.662 bisitz 15799: my $max_samples = 5;
15800: my $samples = &get_samples($records,$max_samples);
1.594 raeburn 15801: $r->print(&start_data_table().
15802: &start_data_table_header_row().'<th>'.
15803: &mt('Field').'</th><th>'.&mt('Samples').'</th>'.
15804: &end_data_table_header_row());
1.301 albertel 15805:
15806: foreach my $key (sort(keys(%{ $samples->[0] }))) {
1.594 raeburn 15807: $r->print(&start_data_table_row().'<td><select name="f'.$i.'"'.
1.32 matthew 15808: ' onchange="javascript:flip(this.form,'.$i.');">');
1.301 albertel 15809: foreach my $option (@$d) {
15810: my ($value,$display,$defaultcol)=@{ $option };
1.174 matthew 15811: $r->print('<option value="'.$value.'"'.
1.253 albertel 15812: ($i eq $defaultcol ? ' selected="selected" ':'').'>'.
1.174 matthew 15813: $display.'</option>');
1.31 albertel 15814: }
15815: $r->print('</select></td><td>');
1.662 bisitz 15816: foreach my $line (0..($max_samples-1)) {
1.301 albertel 15817: if (defined($samples->[$line]{$key})) {
15818: $r->print($samples->[$line]{$key}."<br />\n");
15819: }
15820: }
1.594 raeburn 15821: $r->print('</td>'.&end_data_table_row());
1.31 albertel 15822: $i++;
15823: }
1.594 raeburn 15824: $r->print(&end_data_table());
1.31 albertel 15825: $i--;
15826: return($i);
1.115 matthew 15827: }
15828:
1.144 matthew 15829: ######################################################
15830: ######################################################
15831:
1.115 matthew 15832: =pod
15833:
1.648 raeburn 15834: =item * &clean_excel_name($name)
1.115 matthew 15835:
15836: Returns a replacement for $name which does not contain any illegal characters.
15837:
15838: =cut
15839:
1.144 matthew 15840: ######################################################
15841: ######################################################
1.115 matthew 15842: sub clean_excel_name {
15843: my ($name) = @_;
15844: $name =~ s/[:\*\?\/\\]//g;
15845: if (length($name) > 31) {
15846: $name = substr($name,0,31);
15847: }
15848: return $name;
1.25 albertel 15849: }
1.84 albertel 15850:
1.85 albertel 15851: =pod
15852:
1.648 raeburn 15853: =item * &check_if_partid_hidden($id,$symb,$udom,$uname)
1.85 albertel 15854:
15855: Returns either 1 or undef
15856:
15857: 1 if the part is to be hidden, undef if it is to be shown
15858:
15859: Arguments are:
15860:
15861: $id the id of the part to be checked
15862: $symb, optional the symb of the resource to check
15863: $udom, optional the domain of the user to check for
15864: $uname, optional the username of the user to check for
15865:
15866: =cut
1.84 albertel 15867:
15868: sub check_if_partid_hidden {
15869: my ($id,$symb,$udom,$uname) = @_;
1.133 albertel 15870: my $hiddenparts=&Apache::lonnet::EXT('resource.0.hiddenparts',
1.84 albertel 15871: $symb,$udom,$uname);
1.141 albertel 15872: my $truth=1;
15873: #if the string starts with !, then the list is the list to show not hide
15874: if ($hiddenparts=~s/^\s*!//) { $truth=undef; }
1.84 albertel 15875: my @hiddenlist=split(/,/,$hiddenparts);
15876: foreach my $checkid (@hiddenlist) {
1.141 albertel 15877: if ($checkid =~ /^\s*\Q$id\E\s*$/) { return $truth; }
1.84 albertel 15878: }
1.141 albertel 15879: return !$truth;
1.84 albertel 15880: }
1.127 matthew 15881:
1.138 matthew 15882:
15883: ############################################################
15884: ############################################################
15885:
15886: =pod
15887:
1.157 matthew 15888: =back
15889:
1.138 matthew 15890: =head1 cgi-bin script and graphing routines
15891:
1.157 matthew 15892: =over 4
15893:
1.648 raeburn 15894: =item * &get_cgi_id()
1.138 matthew 15895:
15896: Inputs: none
15897:
15898: Returns an id which can be used to pass environment variables
15899: to various cgi-bin scripts. These environment variables will
15900: be removed from the users environment after a given time by
15901: the routine &Apache::lonnet::transfer_profile_to_env.
15902:
15903: =cut
15904:
15905: ############################################################
15906: ############################################################
1.152 albertel 15907: my $uniq=0;
1.136 matthew 15908: sub get_cgi_id {
1.154 albertel 15909: $uniq=($uniq+1)%100000;
1.280 albertel 15910: return (time.'_'.$$.'_'.$uniq);
1.136 matthew 15911: }
15912:
1.127 matthew 15913: ############################################################
15914: ############################################################
15915:
15916: =pod
15917:
1.648 raeburn 15918: =item * &DrawBarGraph()
1.127 matthew 15919:
1.138 matthew 15920: Facilitates the plotting of data in a (stacked) bar graph.
15921: Puts plot definition data into the users environment in order for
15922: graph.png to plot it. Returns an <img> tag for the plot.
15923: The bars on the plot are labeled '1','2',...,'n'.
15924:
15925: Inputs:
15926:
15927: =over 4
15928:
15929: =item $Title: string, the title of the plot
15930:
15931: =item $xlabel: string, text describing the X-axis of the plot
15932:
15933: =item $ylabel: string, text describing the Y-axis of the plot
15934:
15935: =item $Max: scalar, the maximum Y value to use in the plot
15936: If $Max is < any data point, the graph will not be rendered.
15937:
1.140 matthew 15938: =item $colors: array ref holding the colors to be used for the data sets when
1.138 matthew 15939: they are plotted. If undefined, default values will be used.
15940:
1.178 matthew 15941: =item $labels: array ref holding the labels to use on the x-axis for the bars.
15942:
1.138 matthew 15943: =item @Values: An array of array references. Each array reference holds data
15944: to be plotted in a stacked bar chart.
15945:
1.239 matthew 15946: =item If the final element of @Values is a hash reference the key/value
15947: pairs will be added to the graph definition.
15948:
1.138 matthew 15949: =back
15950:
15951: Returns:
15952:
15953: An <img> tag which references graph.png and the appropriate identifying
15954: information for the plot.
15955:
1.127 matthew 15956: =cut
15957:
15958: ############################################################
15959: ############################################################
1.134 matthew 15960: sub DrawBarGraph {
1.178 matthew 15961: my ($Title,$xlabel,$ylabel,$Max,$colors,$labels,@Values)=@_;
1.134 matthew 15962: #
15963: if (! defined($colors)) {
15964: $colors = ['#33ff00',
15965: '#0033cc', '#990000', '#aaaa66', '#663399', '#ff9933',
15966: '#66ccff', '#ff9999', '#cccc33', '#660000', '#33cc66',
15967: ];
15968: }
1.228 matthew 15969: my $extra_settings = {};
15970: if (ref($Values[-1]) eq 'HASH') {
15971: $extra_settings = pop(@Values);
15972: }
1.127 matthew 15973: #
1.136 matthew 15974: my $identifier = &get_cgi_id();
15975: my $id = 'cgi.'.$identifier;
1.129 matthew 15976: if (! @Values || ref($Values[0]) ne 'ARRAY') {
1.127 matthew 15977: return '';
15978: }
1.225 matthew 15979: #
15980: my @Labels;
15981: if (defined($labels)) {
15982: @Labels = @$labels;
15983: } else {
15984: for (my $i=0;$i<@{$Values[0]};$i++) {
1.1263 raeburn 15985: push(@Labels,$i+1);
1.225 matthew 15986: }
15987: }
15988: #
1.129 matthew 15989: my $NumBars = scalar(@{$Values[0]});
1.225 matthew 15990: if ($NumBars < scalar(@Labels)) { $NumBars = scalar(@Labels); }
1.129 matthew 15991: my %ValuesHash;
15992: my $NumSets=1;
15993: foreach my $array (@Values) {
15994: next if (! ref($array));
1.136 matthew 15995: $ValuesHash{$id.'.data.'.$NumSets++} =
1.132 matthew 15996: join(',',@$array);
1.129 matthew 15997: }
1.127 matthew 15998: #
1.136 matthew 15999: my ($height,$width,$xskip,$bar_width) = (200,120,1,15);
1.225 matthew 16000: if ($NumBars < 3) {
16001: $width = 120+$NumBars*32;
1.220 matthew 16002: $xskip = 1;
1.225 matthew 16003: $bar_width = 30;
16004: } elsif ($NumBars < 5) {
16005: $width = 120+$NumBars*20;
16006: $xskip = 1;
16007: $bar_width = 20;
1.220 matthew 16008: } elsif ($NumBars < 10) {
1.136 matthew 16009: $width = 120+$NumBars*15;
16010: $xskip = 1;
16011: $bar_width = 15;
16012: } elsif ($NumBars <= 25) {
16013: $width = 120+$NumBars*11;
16014: $xskip = 5;
16015: $bar_width = 8;
16016: } elsif ($NumBars <= 50) {
16017: $width = 120+$NumBars*8;
16018: $xskip = 5;
16019: $bar_width = 4;
16020: } else {
16021: $width = 120+$NumBars*8;
16022: $xskip = 5;
16023: $bar_width = 4;
16024: }
16025: #
1.137 matthew 16026: $Max = 1 if ($Max < 1);
16027: if ( int($Max) < $Max ) {
16028: $Max++;
16029: $Max = int($Max);
16030: }
1.127 matthew 16031: $Title = '' if (! defined($Title));
16032: $xlabel = '' if (! defined($xlabel));
16033: $ylabel = '' if (! defined($ylabel));
1.369 www 16034: $ValuesHash{$id.'.title'} = &escape($Title);
16035: $ValuesHash{$id.'.xlabel'} = &escape($xlabel);
16036: $ValuesHash{$id.'.ylabel'} = &escape($ylabel);
1.137 matthew 16037: $ValuesHash{$id.'.y_max_value'} = $Max;
1.136 matthew 16038: $ValuesHash{$id.'.NumBars'} = $NumBars;
16039: $ValuesHash{$id.'.NumSets'} = $NumSets;
16040: $ValuesHash{$id.'.PlotType'} = 'bar';
16041: $ValuesHash{$id.'.Colors'} = join(',',@{$colors});
16042: $ValuesHash{$id.'.height'} = $height;
16043: $ValuesHash{$id.'.width'} = $width;
16044: $ValuesHash{$id.'.xskip'} = $xskip;
16045: $ValuesHash{$id.'.bar_width'} = $bar_width;
16046: $ValuesHash{$id.'.labels'} = join(',',@Labels);
1.127 matthew 16047: #
1.228 matthew 16048: # Deal with other parameters
16049: while (my ($key,$value) = each(%$extra_settings)) {
16050: $ValuesHash{$id.'.'.$key} = $value;
16051: }
16052: #
1.646 raeburn 16053: &Apache::lonnet::appenv(\%ValuesHash);
1.137 matthew 16054: return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
16055: }
16056:
16057: ############################################################
16058: ############################################################
16059:
16060: =pod
16061:
1.648 raeburn 16062: =item * &DrawXYGraph()
1.137 matthew 16063:
1.138 matthew 16064: Facilitates the plotting of data in an XY graph.
16065: Puts plot definition data into the users environment in order for
16066: graph.png to plot it. Returns an <img> tag for the plot.
16067:
16068: Inputs:
16069:
16070: =over 4
16071:
16072: =item $Title: string, the title of the plot
16073:
16074: =item $xlabel: string, text describing the X-axis of the plot
16075:
16076: =item $ylabel: string, text describing the Y-axis of the plot
16077:
16078: =item $Max: scalar, the maximum Y value to use in the plot
16079: If $Max is < any data point, the graph will not be rendered.
16080:
16081: =item $colors: Array ref containing the hex color codes for the data to be
16082: plotted in. If undefined, default values will be used.
16083:
16084: =item $Xlabels: Array ref containing the labels to be used for the X-axis.
16085:
16086: =item $Ydata: Array ref containing Array refs.
1.185 www 16087: Each of the contained arrays will be plotted as a separate curve.
1.138 matthew 16088:
16089: =item %Values: hash indicating or overriding any default values which are
16090: passed to graph.png.
16091: Possible values are: width, xskip, x_ticks, x_tick_offset, among others.
16092:
16093: =back
16094:
16095: Returns:
16096:
16097: An <img> tag which references graph.png and the appropriate identifying
16098: information for the plot.
16099:
1.137 matthew 16100: =cut
16101:
16102: ############################################################
16103: ############################################################
16104: sub DrawXYGraph {
16105: my ($Title,$xlabel,$ylabel,$Max,$colors,$Xlabels,$Ydata,%Values)=@_;
16106: #
16107: # Create the identifier for the graph
16108: my $identifier = &get_cgi_id();
16109: my $id = 'cgi.'.$identifier;
16110: #
16111: $Title = '' if (! defined($Title));
16112: $xlabel = '' if (! defined($xlabel));
16113: $ylabel = '' if (! defined($ylabel));
16114: my %ValuesHash =
16115: (
1.369 www 16116: $id.'.title' => &escape($Title),
16117: $id.'.xlabel' => &escape($xlabel),
16118: $id.'.ylabel' => &escape($ylabel),
1.137 matthew 16119: $id.'.y_max_value'=> $Max,
16120: $id.'.labels' => join(',',@$Xlabels),
16121: $id.'.PlotType' => 'XY',
16122: );
16123: #
16124: if (defined($colors) && ref($colors) eq 'ARRAY') {
16125: $ValuesHash{$id.'.Colors'} = join(',',@{$colors});
16126: }
16127: #
16128: if (! ref($Ydata) || ref($Ydata) ne 'ARRAY') {
16129: return '';
16130: }
16131: my $NumSets=1;
1.138 matthew 16132: foreach my $array (@{$Ydata}){
1.137 matthew 16133: next if (! ref($array));
16134: $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array);
16135: }
1.138 matthew 16136: $ValuesHash{$id.'.NumSets'} = $NumSets-1;
1.137 matthew 16137: #
16138: # Deal with other parameters
16139: while (my ($key,$value) = each(%Values)) {
16140: $ValuesHash{$id.'.'.$key} = $value;
1.127 matthew 16141: }
16142: #
1.646 raeburn 16143: &Apache::lonnet::appenv(\%ValuesHash);
1.136 matthew 16144: return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
16145: }
16146:
16147: ############################################################
16148: ############################################################
16149:
16150: =pod
16151:
1.648 raeburn 16152: =item * &DrawXYYGraph()
1.138 matthew 16153:
16154: Facilitates the plotting of data in an XY graph with two Y axes.
16155: Puts plot definition data into the users environment in order for
16156: graph.png to plot it. Returns an <img> tag for the plot.
16157:
16158: Inputs:
16159:
16160: =over 4
16161:
16162: =item $Title: string, the title of the plot
16163:
16164: =item $xlabel: string, text describing the X-axis of the plot
16165:
16166: =item $ylabel: string, text describing the Y-axis of the plot
16167:
16168: =item $colors: Array ref containing the hex color codes for the data to be
16169: plotted in. If undefined, default values will be used.
16170:
16171: =item $Xlabels: Array ref containing the labels to be used for the X-axis.
16172:
16173: =item $Ydata1: The first data set
16174:
16175: =item $Min1: The minimum value of the left Y-axis
16176:
16177: =item $Max1: The maximum value of the left Y-axis
16178:
16179: =item $Ydata2: The second data set
16180:
16181: =item $Min2: The minimum value of the right Y-axis
16182:
16183: =item $Max2: The maximum value of the left Y-axis
16184:
16185: =item %Values: hash indicating or overriding any default values which are
16186: passed to graph.png.
16187: Possible values are: width, xskip, x_ticks, x_tick_offset, among others.
16188:
16189: =back
16190:
16191: Returns:
16192:
16193: An <img> tag which references graph.png and the appropriate identifying
16194: information for the plot.
1.136 matthew 16195:
16196: =cut
16197:
16198: ############################################################
16199: ############################################################
1.137 matthew 16200: sub DrawXYYGraph {
16201: my ($Title,$xlabel,$ylabel,$colors,$Xlabels,$Ydata1,$Min1,$Max1,
16202: $Ydata2,$Min2,$Max2,%Values)=@_;
1.136 matthew 16203: #
16204: # Create the identifier for the graph
16205: my $identifier = &get_cgi_id();
16206: my $id = 'cgi.'.$identifier;
16207: #
16208: $Title = '' if (! defined($Title));
16209: $xlabel = '' if (! defined($xlabel));
16210: $ylabel = '' if (! defined($ylabel));
16211: my %ValuesHash =
16212: (
1.369 www 16213: $id.'.title' => &escape($Title),
16214: $id.'.xlabel' => &escape($xlabel),
16215: $id.'.ylabel' => &escape($ylabel),
1.136 matthew 16216: $id.'.labels' => join(',',@$Xlabels),
16217: $id.'.PlotType' => 'XY',
16218: $id.'.NumSets' => 2,
1.137 matthew 16219: $id.'.two_axes' => 1,
16220: $id.'.y1_max_value' => $Max1,
16221: $id.'.y1_min_value' => $Min1,
16222: $id.'.y2_max_value' => $Max2,
16223: $id.'.y2_min_value' => $Min2,
1.136 matthew 16224: );
16225: #
1.137 matthew 16226: if (defined($colors) && ref($colors) eq 'ARRAY') {
16227: $ValuesHash{$id.'.Colors'} = join(',',@{$colors});
16228: }
16229: #
16230: if (! ref($Ydata1) || ref($Ydata1) ne 'ARRAY' ||
16231: ! ref($Ydata2) || ref($Ydata2) ne 'ARRAY'){
1.136 matthew 16232: return '';
16233: }
16234: my $NumSets=1;
1.137 matthew 16235: foreach my $array ($Ydata1,$Ydata2){
1.136 matthew 16236: next if (! ref($array));
16237: $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array);
1.137 matthew 16238: }
16239: #
16240: # Deal with other parameters
16241: while (my ($key,$value) = each(%Values)) {
16242: $ValuesHash{$id.'.'.$key} = $value;
1.136 matthew 16243: }
16244: #
1.646 raeburn 16245: &Apache::lonnet::appenv(\%ValuesHash);
1.130 albertel 16246: return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
1.139 matthew 16247: }
16248:
16249: ############################################################
16250: ############################################################
16251:
16252: =pod
16253:
1.157 matthew 16254: =back
16255:
1.139 matthew 16256: =head1 Statistics helper routines?
16257:
16258: Bad place for them but what the hell.
16259:
1.157 matthew 16260: =over 4
16261:
1.648 raeburn 16262: =item * &chartlink()
1.139 matthew 16263:
16264: Returns a link to the chart for a specific student.
16265:
16266: Inputs:
16267:
16268: =over 4
16269:
16270: =item $linktext: The text of the link
16271:
16272: =item $sname: The students username
16273:
16274: =item $sdomain: The students domain
16275:
16276: =back
16277:
1.157 matthew 16278: =back
16279:
1.139 matthew 16280: =cut
16281:
16282: ############################################################
16283: ############################################################
16284: sub chartlink {
16285: my ($linktext, $sname, $sdomain) = @_;
16286: my $link = '<a href="/adm/statistics?reportSelected=student_assessment'.
1.369 www 16287: '&SelectedStudent='.&escape($sname.':'.$sdomain).
1.219 albertel 16288: '&chartoutputmode='.HTML::Entities::encode('html, with all links').
1.139 matthew 16289: '">'.$linktext.'</a>';
1.153 matthew 16290: }
16291:
16292: #######################################################
16293: #######################################################
16294:
16295: =pod
16296:
16297: =head1 Course Environment Routines
1.157 matthew 16298:
16299: =over 4
1.153 matthew 16300:
1.648 raeburn 16301: =item * &restore_course_settings()
1.153 matthew 16302:
1.648 raeburn 16303: =item * &store_course_settings()
1.153 matthew 16304:
16305: Restores/Store indicated form parameters from the course environment.
16306: Will not overwrite existing values of the form parameters.
16307:
16308: Inputs:
16309: a scalar describing the data (e.g. 'chart', 'problem_analysis')
16310:
16311: a hash ref describing the data to be stored. For example:
16312:
16313: %Save_Parameters = ('Status' => 'scalar',
16314: 'chartoutputmode' => 'scalar',
16315: 'chartoutputdata' => 'scalar',
16316: 'Section' => 'array',
1.373 raeburn 16317: 'Group' => 'array',
1.153 matthew 16318: 'StudentData' => 'array',
16319: 'Maps' => 'array');
16320:
16321: Returns: both routines return nothing
16322:
1.631 raeburn 16323: =back
16324:
1.153 matthew 16325: =cut
16326:
16327: #######################################################
16328: #######################################################
16329: sub store_course_settings {
1.496 albertel 16330: return &store_settings($env{'request.course.id'},@_);
16331: }
16332:
16333: sub store_settings {
1.153 matthew 16334: # save to the environment
16335: # appenv the same items, just to be safe
1.300 albertel 16336: my $udom = $env{'user.domain'};
16337: my $uname = $env{'user.name'};
1.496 albertel 16338: my ($context,$prefix,$Settings) = @_;
1.153 matthew 16339: my %SaveHash;
16340: my %AppHash;
16341: while (my ($setting,$type) = each(%$Settings)) {
1.496 albertel 16342: my $basename = join('.','internal',$context,$prefix,$setting);
1.300 albertel 16343: my $envname = 'environment.'.$basename;
1.258 albertel 16344: if (exists($env{'form.'.$setting})) {
1.153 matthew 16345: # Save this value away
16346: if ($type eq 'scalar' &&
1.258 albertel 16347: (! exists($env{$envname}) ||
16348: $env{$envname} ne $env{'form.'.$setting})) {
16349: $SaveHash{$basename} = $env{'form.'.$setting};
16350: $AppHash{$envname} = $env{'form.'.$setting};
1.153 matthew 16351: } elsif ($type eq 'array') {
16352: my $stored_form;
1.258 albertel 16353: if (ref($env{'form.'.$setting})) {
1.153 matthew 16354: $stored_form = join(',',
16355: map {
1.369 www 16356: &escape($_);
1.258 albertel 16357: } sort(@{$env{'form.'.$setting}}));
1.153 matthew 16358: } else {
16359: $stored_form =
1.369 www 16360: &escape($env{'form.'.$setting});
1.153 matthew 16361: }
16362: # Determine if the array contents are the same.
1.258 albertel 16363: if ($stored_form ne $env{$envname}) {
1.153 matthew 16364: $SaveHash{$basename} = $stored_form;
16365: $AppHash{$envname} = $stored_form;
16366: }
16367: }
16368: }
16369: }
16370: my $put_result = &Apache::lonnet::put('environment',\%SaveHash,
1.300 albertel 16371: $udom,$uname);
1.153 matthew 16372: if ($put_result !~ /^(ok|delayed)/) {
16373: &Apache::lonnet::logthis('unable to save form parameters, '.
16374: 'got error:'.$put_result);
16375: }
16376: # Make sure these settings stick around in this session, too
1.646 raeburn 16377: &Apache::lonnet::appenv(\%AppHash);
1.153 matthew 16378: return;
16379: }
16380:
16381: sub restore_course_settings {
1.499 albertel 16382: return &restore_settings($env{'request.course.id'},@_);
1.496 albertel 16383: }
16384:
16385: sub restore_settings {
16386: my ($context,$prefix,$Settings) = @_;
1.153 matthew 16387: while (my ($setting,$type) = each(%$Settings)) {
1.258 albertel 16388: next if (exists($env{'form.'.$setting}));
1.496 albertel 16389: my $envname = 'environment.internal.'.$context.'.'.$prefix.
1.153 matthew 16390: '.'.$setting;
1.258 albertel 16391: if (exists($env{$envname})) {
1.153 matthew 16392: if ($type eq 'scalar') {
1.258 albertel 16393: $env{'form.'.$setting} = $env{$envname};
1.153 matthew 16394: } elsif ($type eq 'array') {
1.258 albertel 16395: $env{'form.'.$setting} = [
1.153 matthew 16396: map {
1.369 www 16397: &unescape($_);
1.258 albertel 16398: } split(',',$env{$envname})
1.153 matthew 16399: ];
16400: }
16401: }
16402: }
1.127 matthew 16403: }
16404:
1.618 raeburn 16405: #######################################################
16406: #######################################################
16407:
16408: =pod
16409:
16410: =head1 Domain E-mail Routines
16411:
16412: =over 4
16413:
1.648 raeburn 16414: =item * &build_recipient_list()
1.618 raeburn 16415:
1.1144 raeburn 16416: Build recipient lists for following types of e-mail:
1.766 raeburn 16417: (a) Error Reports, (b) Package Updates, (c) lonstatus warnings/errors
1.1144 raeburn 16418: (d) Help requests, (e) Course requests needing approval, (f) loncapa
16419: module change checking, student/employee ID conflict checks, as
16420: generated by lonerrorhandler.pm, CHECKRPMS, loncron,
16421: lonsupportreq.pm, loncoursequeueadmin.pm, searchcat.pl respectively.
1.618 raeburn 16422:
16423: Inputs:
1.619 raeburn 16424: defmail (scalar - email address of default recipient),
1.1144 raeburn 16425: mailing type (scalar: errormail, packagesmail, helpdeskmail,
16426: requestsmail, updatesmail, or idconflictsmail).
16427:
1.619 raeburn 16428: defdom (domain for which to retrieve configuration settings),
1.1144 raeburn 16429:
1.619 raeburn 16430: origmail (scalar - email address of recipient from loncapa.conf,
1.1297 raeburn 16431: i.e., predates configuration by DC via domainprefs.pm
16432:
16433: $requname username of requester (if mailing type is helpdeskmail)
16434:
16435: $requdom domain of requester (if mailing type is helpdeskmail)
16436:
16437: $reqemail e-mail address of requester (if mailing type is helpdeskmail)
16438:
1.618 raeburn 16439:
1.655 raeburn 16440: Returns: comma separated list of addresses to which to send e-mail.
16441:
16442: =back
1.618 raeburn 16443:
16444: =cut
16445:
16446: ############################################################
16447: ############################################################
16448: sub build_recipient_list {
1.1297 raeburn 16449: my ($defmail,$mailing,$defdom,$origmail,$requname,$requdom,$reqemail) = @_;
1.618 raeburn 16450: my @recipients;
1.1270 raeburn 16451: my ($otheremails,$lastresort,$allbcc,$addtext);
1.618 raeburn 16452: my %domconfig =
1.1270 raeburn 16453: &Apache::lonnet::get_dom('configuration',['contacts'],$defdom);
1.618 raeburn 16454: if (ref($domconfig{'contacts'}) eq 'HASH') {
1.766 raeburn 16455: if (exists($domconfig{'contacts'}{$mailing})) {
16456: if (ref($domconfig{'contacts'}{$mailing}) eq 'HASH') {
16457: my @contacts = ('adminemail','supportemail');
16458: foreach my $item (@contacts) {
16459: if ($domconfig{'contacts'}{$mailing}{$item}) {
16460: my $addr = $domconfig{'contacts'}{$item};
16461: if (!grep(/^\Q$addr\E$/,@recipients)) {
16462: push(@recipients,$addr);
16463: }
1.619 raeburn 16464: }
1.1270 raeburn 16465: }
16466: $otheremails = $domconfig{'contacts'}{$mailing}{'others'};
16467: if ($mailing eq 'helpdeskmail') {
16468: if ($domconfig{'contacts'}{$mailing}{'bcc'}) {
16469: my @bccs = split(/,/,$domconfig{'contacts'}{$mailing}{'bcc'});
16470: my @ok_bccs;
16471: foreach my $bcc (@bccs) {
16472: $bcc =~ s/^\s+//g;
16473: $bcc =~ s/\s+$//g;
16474: if ($bcc =~ m/^[^\@]+\@[^\@]+$/) {
16475: if (!(grep(/^\Q$bcc\E$/,@ok_bccs))) {
16476: push(@ok_bccs,$bcc);
16477: }
16478: }
16479: }
16480: if (@ok_bccs > 0) {
16481: $allbcc = join(', ',@ok_bccs);
16482: }
16483: }
16484: $addtext = $domconfig{'contacts'}{$mailing}{'include'};
1.618 raeburn 16485: }
16486: }
1.766 raeburn 16487: } elsif ($origmail ne '') {
1.1270 raeburn 16488: $lastresort = $origmail;
1.618 raeburn 16489: }
1.1297 raeburn 16490: if ($mailing eq 'helpdeskmail') {
16491: if ((ref($domconfig{'contacts'}{'overrides'}) eq 'HASH') &&
16492: (keys(%{$domconfig{'contacts'}{'overrides'}}))) {
16493: my ($inststatus,$inststatus_checked);
16494: if (($env{'user.name'} ne '') && ($env{'user.domain'} ne '') &&
16495: ($env{'user.domain'} ne 'public')) {
16496: $inststatus_checked = 1;
16497: $inststatus = $env{'environment.inststatus'};
16498: }
16499: unless ($inststatus_checked) {
16500: if (($requname ne '') && ($requdom ne '')) {
16501: if (($requname =~ /^$match_username$/) &&
16502: ($requdom =~ /^$match_domain$/) &&
16503: (&Apache::lonnet::domain($requdom))) {
16504: my $requhome = &Apache::lonnet::homeserver($requname,
16505: $requdom);
16506: unless ($requhome eq 'no_host') {
16507: my %userenv = &Apache::lonnet::userenvironment($requdom,$requname,'inststatus');
16508: $inststatus = $userenv{'inststatus'};
16509: $inststatus_checked = 1;
16510: }
16511: }
16512: }
16513: }
16514: unless ($inststatus_checked) {
16515: if ($reqemail =~ /^[^\@]+\@[^\@]+$/) {
16516: my %srch = (srchby => 'email',
16517: srchdomain => $defdom,
16518: srchterm => $reqemail,
16519: srchtype => 'exact');
16520: my %srch_results = &Apache::lonnet::usersearch(\%srch);
16521: foreach my $uname (keys(%srch_results)) {
16522: if (ref($srch_results{$uname}{'inststatus'}) eq 'ARRAY') {
16523: $inststatus = join(',',@{$srch_results{$uname}{'inststatus'}});
16524: $inststatus_checked = 1;
16525: last;
16526: }
16527: }
16528: unless ($inststatus_checked) {
16529: my ($dirsrchres,%srch_results) = &Apache::lonnet::inst_directory_query(\%srch);
16530: if ($dirsrchres eq 'ok') {
16531: foreach my $uname (keys(%srch_results)) {
16532: if (ref($srch_results{$uname}{'inststatus'}) eq 'ARRAY') {
16533: $inststatus = join(',',@{$srch_results{$uname}{'inststatus'}});
16534: $inststatus_checked = 1;
16535: last;
16536: }
16537: }
16538: }
16539: }
16540: }
16541: }
16542: if ($inststatus ne '') {
16543: foreach my $status (split(/\:/,$inststatus)) {
16544: if (ref($domconfig{'contacts'}{'overrides'}{$status}) eq 'HASH') {
16545: my @contacts = ('adminemail','supportemail');
16546: foreach my $item (@contacts) {
16547: if ($domconfig{'contacts'}{'overrides'}{$status}{$item}) {
16548: my $addr = $domconfig{'contacts'}{'overrides'}{$status};
16549: if (!grep(/^\Q$addr\E$/,@recipients)) {
16550: push(@recipients,$addr);
16551: }
16552: }
16553: }
16554: $otheremails = $domconfig{'contacts'}{'overrides'}{$status}{'others'};
16555: if ($domconfig{'contacts'}{'overrides'}{$status}{'bcc'}) {
16556: my @bccs = split(/,/,$domconfig{'contacts'}{'overrides'}{$status}{'bcc'});
16557: my @ok_bccs;
16558: foreach my $bcc (@bccs) {
16559: $bcc =~ s/^\s+//g;
16560: $bcc =~ s/\s+$//g;
16561: if ($bcc =~ m/^[^\@]+\@[^\@]+$/) {
16562: if (!(grep(/^\Q$bcc\E$/,@ok_bccs))) {
16563: push(@ok_bccs,$bcc);
16564: }
16565: }
16566: }
16567: if (@ok_bccs > 0) {
16568: $allbcc = join(', ',@ok_bccs);
16569: }
16570: }
16571: $addtext = $domconfig{'contacts'}{'overrides'}{$status}{'include'};
16572: last;
16573: }
16574: }
16575: }
16576: }
16577: }
1.619 raeburn 16578: } elsif ($origmail ne '') {
1.1270 raeburn 16579: $lastresort = $origmail;
16580: }
1.1297 raeburn 16581: if (($mailing eq 'helpdeskmail') && ($lastresort ne '')) {
1.1270 raeburn 16582: unless (grep(/^\Q$defdom\E$/,&Apache::lonnet::current_machine_domains())) {
16583: my $lonhost = $Apache::lonnet::perlvar{'lonHostID'};
16584: my $machinedom = $Apache::lonnet::perlvar{'lonDefDomain'};
16585: my %what = (
16586: perlvar => 1,
16587: );
16588: my $primary = &Apache::lonnet::domain($defdom,'primary');
16589: if ($primary) {
16590: my $gotaddr;
16591: my ($result,$returnhash) =
16592: &Apache::lonnet::get_remote_globals($primary,{ perlvar => 1 });
16593: if (($result eq 'ok') && (ref($returnhash) eq 'HASH')) {
16594: if ($returnhash->{'lonSupportEMail'} =~ /^[^\@]+\@[^\@]+$/) {
16595: $lastresort = $returnhash->{'lonSupportEMail'};
16596: $gotaddr = 1;
16597: }
16598: }
16599: unless ($gotaddr) {
16600: my $uintdom = &Apache::lonnet::internet_dom($primary);
16601: my $intdom = &Apache::lonnet::internet_dom($lonhost);
16602: unless ($uintdom eq $intdom) {
16603: my %domconfig =
16604: &Apache::lonnet::get_dom('configuration',['contacts'],$machinedom);
16605: if (ref($domconfig{'contacts'}) eq 'HASH') {
16606: if (ref($domconfig{'contacts'}{'otherdomsmail'}) eq 'HASH') {
16607: my @contacts = ('adminemail','supportemail');
16608: foreach my $item (@contacts) {
16609: if ($domconfig{'contacts'}{'otherdomsmail'}{$item}) {
16610: my $addr = $domconfig{'contacts'}{$item};
16611: if (!grep(/^\Q$addr\E$/,@recipients)) {
16612: push(@recipients,$addr);
16613: }
16614: }
16615: }
16616: if ($domconfig{'contacts'}{'otherdomsmail'}{'others'}) {
16617: $otheremails = $domconfig{'contacts'}{'otherdomsmail'}{'others'};
16618: }
16619: if ($domconfig{'contacts'}{'otherdomsmail'}{'bcc'}) {
16620: my @bccs = split(/,/,$domconfig{'contacts'}{'otherdomsmail'}{'bcc'});
16621: my @ok_bccs;
16622: foreach my $bcc (@bccs) {
16623: $bcc =~ s/^\s+//g;
16624: $bcc =~ s/\s+$//g;
16625: if ($bcc =~ m/^[^\@]+\@[^\@]+$/) {
16626: if (!(grep(/^\Q$bcc\E$/,@ok_bccs))) {
16627: push(@ok_bccs,$bcc);
16628: }
16629: }
16630: }
16631: if (@ok_bccs > 0) {
16632: $allbcc = join(', ',@ok_bccs);
16633: }
16634: }
16635: $addtext = $domconfig{'contacts'}{'otherdomsmail'}{'include'};
16636: }
16637: }
16638: }
16639: }
16640: }
16641: }
1.618 raeburn 16642: }
1.688 raeburn 16643: if (defined($defmail)) {
16644: if ($defmail ne '') {
16645: push(@recipients,$defmail);
16646: }
1.618 raeburn 16647: }
16648: if ($otheremails) {
1.619 raeburn 16649: my @others;
16650: if ($otheremails =~ /,/) {
16651: @others = split(/,/,$otheremails);
1.618 raeburn 16652: } else {
1.619 raeburn 16653: push(@others,$otheremails);
16654: }
16655: foreach my $addr (@others) {
16656: if (!grep(/^\Q$addr\E$/,@recipients)) {
16657: push(@recipients,$addr);
16658: }
1.618 raeburn 16659: }
16660: }
1.1298 raeburn 16661: if ($mailing eq 'helpdeskmail') {
1.1270 raeburn 16662: if ((!@recipients) && ($lastresort ne '')) {
16663: push(@recipients,$lastresort);
16664: }
16665: } elsif ($lastresort ne '') {
16666: if (!grep(/^\Q$lastresort\E$/,@recipients)) {
16667: push(@recipients,$lastresort);
16668: }
16669: }
1.1271 raeburn 16670: my $recipientlist = join(',',@recipients);
1.1270 raeburn 16671: if (wantarray) {
16672: return ($recipientlist,$allbcc,$addtext);
16673: } else {
16674: return $recipientlist;
16675: }
1.618 raeburn 16676: }
16677:
1.127 matthew 16678: ############################################################
16679: ############################################################
1.154 albertel 16680:
1.655 raeburn 16681: =pod
16682:
1.1224 musolffc 16683: =over 4
16684:
1.1223 musolffc 16685: =item * &mime_email()
16686:
16687: Sends an email with a possible attachment
16688:
16689: Inputs:
16690:
16691: =over 4
16692:
16693: from - Sender's email address
16694:
1.1343 raeburn 16695: replyto - Reply-To email address
16696:
1.1223 musolffc 16697: to - Email address of recipient
16698:
16699: subject - Subject of email
16700:
16701: body - Body of email
16702:
16703: cc_string - Carbon copy email address
16704:
16705: bcc - Blind carbon copy email address
16706:
16707: attachment_path - Path of file to be attached
16708:
16709: file_name - Name of file to be attached
16710:
16711: attachment_text - The body of an attachment of type "TEXT"
16712:
16713: =back
16714:
16715: =back
16716:
16717: =cut
16718:
16719: ############################################################
16720: ############################################################
16721:
16722: sub mime_email {
1.1343 raeburn 16723: my ($from,$replyto,$to,$subject,$body,$cc_string,$bcc,$attachment_path,
16724: $file_name,$attachment_text) = @_;
16725:
1.1223 musolffc 16726: my $msg = MIME::Lite->new(
16727: From => $from,
16728: To => $to,
16729: Subject => $subject,
16730: Type =>'TEXT',
16731: Data => $body,
16732: );
1.1343 raeburn 16733: if ($replyto ne '') {
16734: $msg->add("Reply-To" => $replyto);
16735: }
1.1223 musolffc 16736: if ($cc_string ne '') {
16737: $msg->add("Cc" => $cc_string);
16738: }
16739: if ($bcc ne '') {
16740: $msg->add("Bcc" => $bcc);
16741: }
16742: $msg->attr("content-type" => "text/plain");
16743: $msg->attr("content-type.charset" => "UTF-8");
16744: # Attach file if given
16745: if ($attachment_path) {
16746: unless ($file_name) {
16747: if ($attachment_path =~ m-/([^/]+)$-) { $file_name = $1; }
16748: }
16749: my ($type, $encoding) = MIME::Types::by_suffix($attachment_path);
16750: $msg->attach(Type => $type,
16751: Path => $attachment_path,
16752: Filename => $file_name
16753: );
16754: # Otherwise attach text if given
16755: } elsif ($attachment_text) {
16756: $msg->attach(Type => 'TEXT',
16757: Data => $attachment_text);
16758: }
16759: # Send it
16760: $msg->send('sendmail');
16761: }
16762:
16763: ############################################################
16764: ############################################################
16765:
16766: =pod
16767:
1.655 raeburn 16768: =head1 Course Catalog Routines
16769:
16770: =over 4
16771:
16772: =item * &gather_categories()
16773:
16774: Converts category definitions - keys of categories hash stored in
16775: coursecategories in configuration.db on the primary library server in a
16776: domain - to an array. Also generates javascript and idx hash used to
16777: generate Domain Coordinator interface for editing Course Categories.
16778:
16779: Inputs:
1.663 raeburn 16780:
1.655 raeburn 16781: categories (reference to hash of category definitions).
1.663 raeburn 16782:
1.655 raeburn 16783: cats (reference to array of arrays/hashes which encapsulates hierarchy of
16784: categories and subcategories).
1.663 raeburn 16785:
1.655 raeburn 16786: idx (reference to hash of counters used in Domain Coordinator interface for
16787: editing Course Categories).
1.663 raeburn 16788:
1.655 raeburn 16789: jsarray (reference to array of categories used to create Javascript arrays for
16790: Domain Coordinator interface for editing Course Categories).
16791:
16792: Returns: nothing
16793:
16794: Side effects: populates cats, idx and jsarray.
16795:
16796: =cut
16797:
16798: sub gather_categories {
16799: my ($categories,$cats,$idx,$jsarray) = @_;
16800: my %counters;
16801: my $num = 0;
16802: foreach my $item (keys(%{$categories})) {
16803: my ($cat,$container,$depth) = map { &unescape($_); } split(/:/,$item);
16804: if ($container eq '' && $depth == 0) {
16805: $cats->[$depth][$categories->{$item}] = $cat;
16806: } else {
16807: $cats->[$depth]{$container}[$categories->{$item}] = $cat;
16808: }
16809: my ($escitem,$tail) = split(/:/,$item,2);
16810: if ($counters{$tail} eq '') {
16811: $counters{$tail} = $num;
16812: $num ++;
16813: }
16814: if (ref($idx) eq 'HASH') {
16815: $idx->{$item} = $counters{$tail};
16816: }
16817: if (ref($jsarray) eq 'ARRAY') {
16818: push(@{$jsarray->[$counters{$tail}]},$item);
16819: }
16820: }
16821: return;
16822: }
16823:
16824: =pod
16825:
16826: =item * &extract_categories()
16827:
16828: Used to generate breadcrumb trails for course categories.
16829:
16830: Inputs:
1.663 raeburn 16831:
1.655 raeburn 16832: categories (reference to hash of category definitions).
1.663 raeburn 16833:
1.655 raeburn 16834: cats (reference to array of arrays/hashes which encapsulates hierarchy of
16835: categories and subcategories).
1.663 raeburn 16836:
1.655 raeburn 16837: trails (reference to array of breacrumb trails for each category).
1.663 raeburn 16838:
1.655 raeburn 16839: allitems (reference to hash - key is category key
16840: (format: escaped(name):escaped(parent category):depth in hierarchy).
1.663 raeburn 16841:
1.655 raeburn 16842: idx (reference to hash of counters used in Domain Coordinator interface for
16843: editing Course Categories).
1.663 raeburn 16844:
1.655 raeburn 16845: jsarray (reference to array of categories used to create Javascript arrays for
16846: Domain Coordinator interface for editing Course Categories).
16847:
1.665 raeburn 16848: subcats (reference to hash of arrays containing all subcategories within each
16849: category, -recursive)
16850:
1.1321 raeburn 16851: maxd (reference to hash used to hold max depth for all top-level categories).
16852:
1.655 raeburn 16853: Returns: nothing
16854:
16855: Side effects: populates trails and allitems hash references.
16856:
16857: =cut
16858:
16859: sub extract_categories {
1.1321 raeburn 16860: my ($categories,$cats,$trails,$allitems,$idx,$jsarray,$subcats,$maxd) = @_;
1.655 raeburn 16861: if (ref($categories) eq 'HASH') {
16862: &gather_categories($categories,$cats,$idx,$jsarray);
16863: if (ref($cats->[0]) eq 'ARRAY') {
16864: for (my $i=0; $i<@{$cats->[0]}; $i++) {
16865: my $name = $cats->[0][$i];
16866: my $item = &escape($name).'::0';
16867: my $trailstr;
16868: if ($name eq 'instcode') {
16869: $trailstr = &mt('Official courses (with institutional codes)');
1.919 raeburn 16870: } elsif ($name eq 'communities') {
16871: $trailstr = &mt('Communities');
1.1239 raeburn 16872: } elsif ($name eq 'placement') {
16873: $trailstr = &mt('Placement Tests');
1.655 raeburn 16874: } else {
16875: $trailstr = $name;
16876: }
16877: if ($allitems->{$item} eq '') {
16878: push(@{$trails},$trailstr);
16879: $allitems->{$item} = scalar(@{$trails})-1;
16880: }
16881: my @parents = ($name);
16882: if (ref($cats->[1]{$name}) eq 'ARRAY') {
16883: for (my $j=0; $j<@{$cats->[1]{$name}}; $j++) {
16884: my $category = $cats->[1]{$name}[$j];
1.665 raeburn 16885: if (ref($subcats) eq 'HASH') {
16886: push(@{$subcats->{$item}},&escape($category).':'.&escape($name).':1');
16887: }
1.1321 raeburn 16888: &recurse_categories($cats,2,$category,$trails,$allitems,\@parents,$subcats,$maxd);
1.665 raeburn 16889: }
16890: } else {
16891: if (ref($subcats) eq 'HASH') {
16892: $subcats->{$item} = [];
1.655 raeburn 16893: }
1.1321 raeburn 16894: if (ref($maxd) eq 'HASH') {
16895: $maxd->{$name} = 1;
16896: }
1.655 raeburn 16897: }
16898: }
16899: }
16900: }
16901: return;
16902: }
16903:
16904: =pod
16905:
1.1162 raeburn 16906: =item * &recurse_categories()
1.655 raeburn 16907:
16908: Recursively used to generate breadcrumb trails for course categories.
16909:
16910: Inputs:
1.663 raeburn 16911:
1.655 raeburn 16912: cats (reference to array of arrays/hashes which encapsulates hierarchy of
16913: categories and subcategories).
1.663 raeburn 16914:
1.655 raeburn 16915: depth (current depth in hierarchy of categories and sub-categories - 0 indexed).
1.663 raeburn 16916:
16917: category (current course category, for which breadcrumb trail is being generated).
16918:
16919: trails (reference to array of breadcrumb trails for each category).
16920:
1.655 raeburn 16921: allitems (reference to hash - key is category key
16922: (format: escaped(name):escaped(parent category):depth in hierarchy).
1.663 raeburn 16923:
1.655 raeburn 16924: parents (array containing containers directories for current category,
16925: back to top level).
16926:
16927: Returns: nothing
16928:
16929: Side effects: populates trails and allitems hash references
16930:
16931: =cut
16932:
16933: sub recurse_categories {
1.1321 raeburn 16934: my ($cats,$depth,$category,$trails,$allitems,$parents,$subcats,$maxd) = @_;
1.655 raeburn 16935: my $shallower = $depth - 1;
16936: if (ref($cats->[$depth]{$category}) eq 'ARRAY') {
16937: for (my $k=0; $k<@{$cats->[$depth]{$category}}; $k++) {
16938: my $name = $cats->[$depth]{$category}[$k];
16939: my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower;
1.1321 raeburn 16940: my $trailstr = join(' » ',(@{$parents},$category));
1.655 raeburn 16941: if ($allitems->{$item} eq '') {
16942: push(@{$trails},$trailstr);
16943: $allitems->{$item} = scalar(@{$trails})-1;
16944: }
16945: my $deeper = $depth+1;
16946: push(@{$parents},$category);
1.665 raeburn 16947: if (ref($subcats) eq 'HASH') {
16948: my $subcat = &escape($name).':'.$category.':'.$depth;
16949: for (my $j=@{$parents}; $j>=0; $j--) {
16950: my $higher;
16951: if ($j > 0) {
16952: $higher = &escape($parents->[$j]).':'.
16953: &escape($parents->[$j-1]).':'.$j;
16954: } else {
16955: $higher = &escape($parents->[$j]).'::'.$j;
16956: }
16957: push(@{$subcats->{$higher}},$subcat);
16958: }
16959: }
16960: &recurse_categories($cats,$deeper,$name,$trails,$allitems,$parents,
1.1321 raeburn 16961: $subcats,$maxd);
1.655 raeburn 16962: pop(@{$parents});
16963: }
16964: } else {
16965: my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower;
1.1321 raeburn 16966: my $trailstr = join(' » ',(@{$parents},$category));
1.655 raeburn 16967: if ($allitems->{$item} eq '') {
16968: push(@{$trails},$trailstr);
16969: $allitems->{$item} = scalar(@{$trails})-1;
16970: }
1.1321 raeburn 16971: if (ref($maxd) eq 'HASH') {
16972: if ($depth > $maxd->{$parents->[0]}) {
16973: $maxd->{$parents->[0]} = $depth;
16974: }
16975: }
1.655 raeburn 16976: }
16977: return;
16978: }
16979:
1.663 raeburn 16980: =pod
16981:
1.1162 raeburn 16982: =item * &assign_categories_table()
1.663 raeburn 16983:
16984: Create a datatable for display of hierarchical categories in a domain,
16985: with checkboxes to allow a course to be categorized.
16986:
16987: Inputs:
16988:
16989: cathash - reference to hash of categories defined for the domain (from
16990: configuration.db)
16991:
16992: currcat - scalar with an & separated list of categories assigned to a course.
16993:
1.919 raeburn 16994: type - scalar contains course type (Course or Community).
16995:
1.1260 raeburn 16996: disabled - scalar (optional) contains disabled="disabled" if input elements are
16997: to be readonly (e.g., Domain Helpdesk role viewing course settings).
16998:
1.663 raeburn 16999: Returns: $output (markup to be displayed)
17000:
17001: =cut
17002:
17003: sub assign_categories_table {
1.1259 raeburn 17004: my ($cathash,$currcat,$type,$disabled) = @_;
1.663 raeburn 17005: my $output;
17006: if (ref($cathash) eq 'HASH') {
1.1321 raeburn 17007: my (@cats,@trails,%allitems,%idx,@jsarray,%maxd,@path,$maxdepth);
17008: &extract_categories($cathash,\@cats,\@trails,\%allitems,\%idx,\@jsarray,\%maxd);
1.663 raeburn 17009: $maxdepth = scalar(@cats);
17010: if (@cats > 0) {
17011: my $itemcount = 0;
17012: if (ref($cats[0]) eq 'ARRAY') {
17013: my @currcategories;
17014: if ($currcat ne '') {
17015: @currcategories = split('&',$currcat);
17016: }
1.919 raeburn 17017: my $table;
1.663 raeburn 17018: for (my $i=0; $i<@{$cats[0]}; $i++) {
17019: my $parent = $cats[0][$i];
1.919 raeburn 17020: next if ($parent eq 'instcode');
17021: if ($type eq 'Community') {
17022: next unless ($parent eq 'communities');
1.1239 raeburn 17023: } elsif ($type eq 'Placement') {
17024: next unless ($parent eq 'placement');
1.919 raeburn 17025: } else {
1.1239 raeburn 17026: next if (($parent eq 'communities') || ($parent eq 'placement'));
1.919 raeburn 17027: }
1.663 raeburn 17028: my $css_class = $itemcount%2?' class="LC_odd_row"':'';
17029: my $item = &escape($parent).'::0';
17030: my $checked = '';
17031: if (@currcategories > 0) {
17032: if (grep(/^\Q$item\E$/,@currcategories)) {
1.772 bisitz 17033: $checked = ' checked="checked"';
1.663 raeburn 17034: }
17035: }
1.919 raeburn 17036: my $parent_title = $parent;
17037: if ($parent eq 'communities') {
17038: $parent_title = &mt('Communities');
1.1239 raeburn 17039: } elsif ($parent eq 'placement') {
17040: $parent_title = &mt('Placement Tests');
1.919 raeburn 17041: }
17042: $table .= '<tr '.$css_class.'><td><span class="LC_nobreak">'.
17043: '<input type="checkbox" name="usecategory" value="'.
1.1259 raeburn 17044: $item.'"'.$checked.$disabled.' />'.$parent_title.'</span>'.
1.919 raeburn 17045: '<input type="hidden" name="catname" value="'.$parent.'" /></td>';
1.663 raeburn 17046: my $depth = 1;
17047: push(@path,$parent);
1.1259 raeburn 17048: $table .= &assign_category_rows($itemcount,\@cats,$depth,$parent,\@path,\@currcategories,$disabled);
1.663 raeburn 17049: pop(@path);
1.919 raeburn 17050: $table .= '</tr><tr><td colspan="'.$maxdepth.'" class="LC_row_separator"></td></tr>';
1.663 raeburn 17051: $itemcount ++;
17052: }
1.919 raeburn 17053: if ($itemcount) {
17054: $output = &Apache::loncommon::start_data_table().
17055: $table.
17056: &Apache::loncommon::end_data_table();
17057: }
1.663 raeburn 17058: }
17059: }
17060: }
17061: return $output;
17062: }
17063:
17064: =pod
17065:
1.1162 raeburn 17066: =item * &assign_category_rows()
1.663 raeburn 17067:
17068: Create a datatable row for display of nested categories in a domain,
17069: with checkboxes to allow a course to be categorized,called recursively.
17070:
17071: Inputs:
17072:
17073: itemcount - track row number for alternating colors
17074:
17075: cats - reference to array of arrays/hashes which encapsulates hierarchy of
17076: categories and subcategories.
17077:
17078: depth - current depth in hierarchy of categories and sub-categories - 0 indexed.
17079:
17080: parent - parent of current category item
17081:
17082: path - Array containing all categories back up through the hierarchy from the
17083: current category to the top level.
17084:
17085: currcategories - reference to array of current categories assigned to the course
17086:
1.1260 raeburn 17087: disabled - scalar (optional) contains disabled="disabled" if input elements are
17088: to be readonly (e.g., Domain Helpdesk role viewing course settings).
17089:
1.663 raeburn 17090: Returns: $output (markup to be displayed).
17091:
17092: =cut
17093:
17094: sub assign_category_rows {
1.1259 raeburn 17095: my ($itemcount,$cats,$depth,$parent,$path,$currcategories,$disabled) = @_;
1.663 raeburn 17096: my ($text,$name,$item,$chgstr);
17097: if (ref($cats) eq 'ARRAY') {
17098: my $maxdepth = scalar(@{$cats});
17099: if (ref($cats->[$depth]) eq 'HASH') {
17100: if (ref($cats->[$depth]{$parent}) eq 'ARRAY') {
17101: my $numchildren = @{$cats->[$depth]{$parent}};
17102: my $css_class = $itemcount%2?' class="LC_odd_row"':'';
1.1145 raeburn 17103: $text .= '<td><table class="LC_data_table">';
1.663 raeburn 17104: for (my $j=0; $j<$numchildren; $j++) {
17105: $name = $cats->[$depth]{$parent}[$j];
17106: $item = &escape($name).':'.&escape($parent).':'.$depth;
17107: my $deeper = $depth+1;
17108: my $checked = '';
17109: if (ref($currcategories) eq 'ARRAY') {
17110: if (@{$currcategories} > 0) {
17111: if (grep(/^\Q$item\E$/,@{$currcategories})) {
1.772 bisitz 17112: $checked = ' checked="checked"';
1.663 raeburn 17113: }
17114: }
17115: }
1.664 raeburn 17116: $text .= '<tr><td><span class="LC_nobreak"><label>'.
17117: '<input type="checkbox" name="usecategory" value="'.
1.1259 raeburn 17118: $item.'"'.$checked.$disabled.' />'.$name.'</label></span>'.
1.675 raeburn 17119: '<input type="hidden" name="catname" value="'.$name.'" />'.
17120: '</td><td>';
1.663 raeburn 17121: if (ref($path) eq 'ARRAY') {
17122: push(@{$path},$name);
1.1259 raeburn 17123: $text .= &assign_category_rows($itemcount,$cats,$deeper,$name,$path,$currcategories,$disabled);
1.663 raeburn 17124: pop(@{$path});
17125: }
17126: $text .= '</td></tr>';
17127: }
17128: $text .= '</table></td>';
17129: }
17130: }
17131: }
17132: return $text;
17133: }
17134:
1.1181 raeburn 17135: =pod
17136:
17137: =back
17138:
17139: =cut
17140:
1.655 raeburn 17141: ############################################################
17142: ############################################################
17143:
17144:
1.443 albertel 17145: sub commit_customrole {
1.1408 raeburn 17146: my ($udom,$uname,$url,$three,$four,$five,$start,$end,$context,$othdomby,$requester) = @_;
1.1399 raeburn 17147: my $result = &Apache::lonnet::assigncustomrole(
1.1408 raeburn 17148: $udom,$uname,$url,$three,$four,$five,$end,$start,undef,undef,
17149: $context,$othdomby,$requester);
1.630 raeburn 17150: my $output = &mt('Assigning custom role').' "'.$five.'" by '.$four.':'.$three.' in '.$url.
1.443 albertel 17151: ($start?', '.&mt('starting').' '.localtime($start):'').
1.1399 raeburn 17152: ($end?', ending '.localtime($end):'').': <b>'.$result.'</b><br />';
17153: if (wantarray) {
17154: return ($output,$result);
17155: } else {
17156: return $output;
17157: }
1.443 albertel 17158: }
17159:
17160: sub commit_standardrole {
1.1408 raeburn 17161: my ($udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context,$credits,
17162: $othdomby,$requester) = @_;
1.1399 raeburn 17163: my ($output,$logmsg,$linefeed,$result);
1.541 raeburn 17164: if ($context eq 'auto') {
17165: $linefeed = "\n";
17166: } else {
17167: $linefeed = "<br />\n";
17168: }
1.443 albertel 17169: if ($three eq 'st') {
1.1399 raeburn 17170: $result = &commit_studentrole(\$logmsg,$udom,$uname,$url,$three,$start,$end,
1.1408 raeburn 17171: $one,$two,$sec,$context,$credits,$othdomby,
17172: $requester);
1.541 raeburn 17173: if (($result =~ /^error/) || ($result eq 'not_in_class') ||
1.626 raeburn 17174: ($result eq 'unknown_course') || ($result eq 'refused')) {
17175: $output = $logmsg.' '.&mt('Error: ').$result."\n";
1.443 albertel 17176: } else {
1.541 raeburn 17177: $output = $logmsg.$linefeed.&mt('Assigning').' '.$three.' in '.$url.
1.443 albertel 17178: ($start?', '.&mt('starting').' '.localtime($start):'').
1.541 raeburn 17179: ($end?', '.&mt('ending').' '.localtime($end):'').': ';
17180: if ($context eq 'auto') {
17181: $output .= $result.$linefeed.&mt('Add to classlist').': ok';
17182: } else {
17183: $output .= '<b>'.$result.'</b>'.$linefeed.
17184: &mt('Add to classlist').': <b>ok</b>';
17185: }
17186: $output .= $linefeed;
1.443 albertel 17187: }
17188: } else {
17189: $output = &mt('Assigning').' '.$three.' in '.$url.
17190: ($start?', '.&mt('starting').' '.localtime($start):'').
1.541 raeburn 17191: ($end?', '.&mt('ending').' '.localtime($end):'').': ';
1.1408 raeburn 17192: $result = &Apache::lonnet::assignrole($udom,$uname,$url,$three,$end,$start,
17193: '','',$context,$othdomby,$requester);
1.541 raeburn 17194: if ($context eq 'auto') {
17195: $output .= $result.$linefeed;
17196: } else {
17197: $output .= '<b>'.$result.'</b>'.$linefeed;
17198: }
1.443 albertel 17199: }
1.1399 raeburn 17200: if (wantarray) {
17201: return ($output,$result);
17202: } else {
17203: return $output;
17204: }
1.443 albertel 17205: }
17206:
17207: sub commit_studentrole {
1.1116 raeburn 17208: my ($logmsg,$udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context,
1.1408 raeburn 17209: $credits,$othdomby,$requester) = @_;
1.626 raeburn 17210: my ($result,$linefeed,$oldsecurl,$newsecurl);
1.541 raeburn 17211: if ($context eq 'auto') {
17212: $linefeed = "\n";
17213: } else {
17214: $linefeed = '<br />'."\n";
17215: }
1.443 albertel 17216: if (defined($one) && defined($two)) {
17217: my $cid=$one.'_'.$two;
17218: my $oldsec=&Apache::lonnet::getsection($udom,$uname,$cid);
17219: my $secchange = 0;
17220: my $expire_role_result;
17221: my $modify_section_result;
1.628 raeburn 17222: if ($oldsec ne '-1') {
17223: if ($oldsec ne $sec) {
1.443 albertel 17224: $secchange = 1;
1.628 raeburn 17225: my $now = time;
1.443 albertel 17226: my $uurl='/'.$cid;
17227: $uurl=~s/\_/\//g;
17228: if ($oldsec) {
17229: $uurl.='/'.$oldsec;
17230: }
1.626 raeburn 17231: $oldsecurl = $uurl;
1.628 raeburn 17232: $expire_role_result =
1.1408 raeburn 17233: &Apache::lonnet::assignrole($udom,$uname,$uurl,'st',$now,
17234: '','','',$context,$othdomby,$requester);
17235: if ($env{'request.course.sec'} ne '') {
1.628 raeburn 17236: if ($expire_role_result eq 'refused') {
17237: my @roles = ('st');
17238: my @statuses = ('previous');
17239: my @roledoms = ($one);
17240: my $withsec = 1;
17241: my %roleshash =
17242: &Apache::lonnet::get_my_roles($uname,$udom,'userroles',
17243: \@statuses,\@roles,\@roledoms,$withsec);
17244: if (defined ($roleshash{$two.':'.$one.':st:'.$oldsec})) {
17245: my ($oldstart,$oldend) =
17246: split(':',$roleshash{$two.':'.$one.':st:'.$oldsec});
17247: if ($oldend > 0 && $oldend <= $now) {
17248: $expire_role_result = 'ok';
17249: }
17250: }
17251: }
17252: }
1.443 albertel 17253: $result = $expire_role_result;
17254: }
17255: }
17256: if (($expire_role_result eq 'ok') || ($secchange == 0)) {
1.1116 raeburn 17257: $modify_section_result =
17258: &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,
17259: undef,undef,undef,$sec,
17260: $end,$start,'','',$cid,
1.1408 raeburn 17261: '',$context,$credits,'',
17262: $othdomby,$requester);
1.443 albertel 17263: if ($modify_section_result =~ /^ok/) {
17264: if ($secchange == 1) {
1.628 raeburn 17265: if ($sec eq '') {
17266: $$logmsg .= &mt('Section for [_1] switched from (possibly expired) old section: [_2] to student role without a section.',$uname,$oldsec).$linefeed;
17267: } else {
17268: $$logmsg .= &mt('Section for [_1] switched from (possibly expired) old section: [_2] to new section: [_3].',$uname,$oldsec,$sec).$linefeed;
17269: }
1.443 albertel 17270: } elsif ($oldsec eq '-1') {
1.628 raeburn 17271: if ($sec eq '') {
17272: $$logmsg .= &mt('New student role without a section for [_1] in course [_2].',$uname,$cid).$linefeed;
17273: } else {
17274: $$logmsg .= &mt('New student role for [_1] in section [_2] in course [_3].',$uname,$sec,$cid).$linefeed;
17275: }
1.443 albertel 17276: } else {
1.628 raeburn 17277: if ($sec eq '') {
17278: $$logmsg .= &mt('Student [_1] assigned to course [_2] without a section.',$uname,$cid).$linefeed;
17279: } else {
17280: $$logmsg .= &mt('Student [_1] assigned to section [_2] in course [_3].',$uname,$sec,$cid).$linefeed;
17281: }
1.443 albertel 17282: }
17283: } else {
1.1115 raeburn 17284: if ($secchange) {
1.628 raeburn 17285: $$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;
17286: } else {
17287: $$logmsg .= &mt('Error when attempting to modify role for [_1] for section: "[_2]" in course [_3] -error:',$uname,$sec,$cid).' '.$modify_section_result.$linefeed;
17288: }
1.443 albertel 17289: }
17290: $result = $modify_section_result;
17291: } elsif ($secchange == 1) {
1.628 raeburn 17292: if ($oldsec eq '') {
1.1103 raeburn 17293: $$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 17294: } else {
17295: $$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;
17296: }
1.626 raeburn 17297: if ($expire_role_result eq 'refused') {
17298: my $newsecurl = '/'.$cid;
17299: $newsecurl =~ s/\_/\//g;
17300: if ($sec ne '') {
17301: $newsecurl.='/'.$sec;
17302: }
17303: if (&Apache::lonnet::allowed('cst',$newsecurl) && !(&Apache::lonnet::allowed('cst',$oldsecurl))) {
17304: if ($sec eq '') {
17305: $$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;
17306: } else {
17307: $$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;
17308: }
17309: }
17310: }
1.443 albertel 17311: }
17312: } else {
1.626 raeburn 17313: $$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 17314: $result = "error: incomplete course id\n";
17315: }
17316: return $result;
17317: }
17318:
1.1108 raeburn 17319: sub show_role_extent {
17320: my ($scope,$context,$role) = @_;
17321: $scope =~ s{^/}{};
17322: my @courseroles = &Apache::lonuserutils::roles_by_context('course',1);
17323: push(@courseroles,'co');
17324: my @authorroles = &Apache::lonuserutils::roles_by_context('author');
17325: if (($context eq 'course') || (grep(/^\Q$role\E/,@courseroles))) {
17326: $scope =~ s{/}{_};
17327: return '<span class="LC_cusr_emph">'.$env{'course.'.$scope.'.description'}.'</span>';
17328: } elsif (($context eq 'author') || (grep(/^\Q$role\E/,@authorroles))) {
17329: my ($audom,$auname) = split(/\//,$scope);
17330: return &mt('[_1] Author Space','<span class="LC_cusr_emph">'.
17331: &Apache::loncommon::plainname($auname,$audom).'</span>');
17332: } else {
17333: $scope =~ s{/$}{};
17334: return &mt('Domain: [_1]','<span class="LC_cusr_emph">'.
17335: &Apache::lonnet::domain($scope,'description').'</span>');
17336: }
17337: }
17338:
1.443 albertel 17339: ############################################################
17340: ############################################################
17341:
1.566 albertel 17342: sub check_clone {
1.578 raeburn 17343: my ($args,$linefeed) = @_;
1.566 albertel 17344: my $cloneid='/'.$args->{'clonedomain'}.'/'.$args->{'clonecourse'};
17345: my ($clonecrsudom,$clonecrsunum)= &LONCAPA::split_courseid($cloneid);
17346: my $clonehome=&Apache::lonnet::homeserver($clonecrsunum,$clonecrsudom);
1.1344 raeburn 17347: my $clonetitle;
17348: my @clonemsg;
1.566 albertel 17349: my $can_clone = 0;
1.944 raeburn 17350: my $lctype = lc($args->{'crstype'});
1.908 raeburn 17351: if ($lctype ne 'community') {
17352: $lctype = 'course';
17353: }
1.566 albertel 17354: if ($clonehome eq 'no_host') {
1.944 raeburn 17355: if ($args->{'crstype'} eq 'Community') {
1.1344 raeburn 17356: push(@clonemsg,({
17357: mt => 'No new community created.',
17358: args => [],
17359: },
17360: {
17361: mt => 'A new community could not be cloned from the specified original - [_1] - because it is a non-existent community.',
17362: args => [$args->{'clonedomain'}.':'.$args->{'clonedomain'}],
17363: }));
1.908 raeburn 17364: } else {
1.1344 raeburn 17365: push(@clonemsg,({
17366: mt => 'No new course created.',
17367: args => [],
17368: },
17369: {
17370: mt => 'A new course could not be cloned from the specified original - [_1] - because it is a non-existent course.',
17371: args => [$args->{'clonecourse'}.':'.$args->{'clonedomain'}],
17372: }));
17373: }
1.566 albertel 17374: } else {
17375: my %clonedesc = &Apache::lonnet::coursedescription($cloneid,{'one_time' => 1});
1.1344 raeburn 17376: $clonetitle = $clonedesc{'description'};
1.944 raeburn 17377: if ($args->{'crstype'} eq 'Community') {
1.908 raeburn 17378: if ($clonedesc{'type'} ne 'Community') {
1.1344 raeburn 17379: push(@clonemsg,({
17380: mt => 'No new community created.',
17381: args => [],
17382: },
17383: {
17384: mt => 'A new community could not be cloned from the specified original - [_1] - because it is a course not a community.',
17385: args => [$args->{'clonecourse'}.':'.$args->{'clonedomain'}],
17386: }));
17387: return ($can_clone,\@clonemsg,$cloneid,$clonehome);
1.908 raeburn 17388: }
17389: }
1.1262 raeburn 17390: if (($env{'request.role.domain'} eq $args->{'clonedomain'}) &&
1.882 raeburn 17391: (&Apache::lonnet::allowed('ccc',$env{'request.role.domain'}))) {
1.566 albertel 17392: $can_clone = 1;
17393: } else {
1.1221 raeburn 17394: my %clonehash = &Apache::lonnet::get('environment',['cloners','internal.coursecode'],
1.566 albertel 17395: $args->{'clonedomain'},$args->{'clonecourse'});
1.1221 raeburn 17396: if ($clonehash{'cloners'} eq '') {
17397: my %domdefs = &Apache::lonnet::get_domain_defaults($args->{'course_domain'});
17398: if ($domdefs{'canclone'}) {
17399: unless ($domdefs{'canclone'} eq 'none') {
17400: if ($domdefs{'canclone'} eq 'domain') {
17401: if ($args->{'ccdomain'} eq $args->{'clonedomain'}) {
17402: $can_clone = 1;
17403: }
17404: } elsif (($clonehash{'internal.coursecode'}) && ($args->{'crscode'}) &&
17405: ($args->{'clonedomain'} eq $args->{'course_domain'})) {
17406: if (&Apache::lonnet::default_instcode_cloning($args->{'clonedomain'},$domdefs{'canclone'},
17407: $clonehash{'internal.coursecode'},$args->{'crscode'})) {
17408: $can_clone = 1;
17409: }
17410: }
17411: }
17412: }
1.578 raeburn 17413: } else {
1.1221 raeburn 17414: my @cloners = split(/,/,$clonehash{'cloners'});
17415: if (grep(/^\*$/,@cloners)) {
1.942 raeburn 17416: $can_clone = 1;
1.1221 raeburn 17417: } elsif (grep(/^\*\:\Q$args->{'ccdomain'}\E$/,@cloners)) {
1.942 raeburn 17418: $can_clone = 1;
1.1225 raeburn 17419: } elsif (grep(/^\Q$args->{'ccuname'}\E:\Q$args->{'ccdomain'}\E$/,@cloners)) {
17420: $can_clone = 1;
1.1221 raeburn 17421: }
17422: unless ($can_clone) {
1.1225 raeburn 17423: if (($clonehash{'internal.coursecode'}) && ($args->{'crscode'}) &&
17424: ($args->{'clonedomain'} eq $args->{'course_domain'})) {
1.1221 raeburn 17425: my (%gotdomdefaults,%gotcodedefaults);
17426: foreach my $cloner (@cloners) {
17427: if (($cloner ne '*') && ($cloner !~ /^\*\:$match_domain$/) &&
17428: ($cloner !~ /^$match_username\:$match_domain$/) && ($cloner ne '')) {
17429: my (%codedefaults,@code_order);
17430: if (ref($gotcodedefaults{$args->{'clonedomain'}}) eq 'HASH') {
17431: if (ref($gotcodedefaults{$args->{'clonedomain'}}{'defaults'}) eq 'HASH') {
17432: %codedefaults = %{$gotcodedefaults{$args->{'clonedomain'}}{'defaults'}};
17433: }
17434: if (ref($gotcodedefaults{$args->{'clonedomain'}}{'order'}) eq 'ARRAY') {
17435: @code_order = @{$gotcodedefaults{$args->{'clonedomain'}}{'order'}};
17436: }
17437: } else {
17438: &Apache::lonnet::auto_instcode_defaults($args->{'clonedomain'},
17439: \%codedefaults,
17440: \@code_order);
17441: $gotcodedefaults{$args->{'clonedomain'}}{'defaults'} = \%codedefaults;
17442: $gotcodedefaults{$args->{'clonedomain'}}{'order'} = \@code_order;
17443: }
17444: if (@code_order > 0) {
17445: if (&Apache::lonnet::check_instcode_cloning(\%codedefaults,\@code_order,
17446: $cloner,$clonehash{'internal.coursecode'},
17447: $args->{'crscode'})) {
17448: $can_clone = 1;
17449: last;
17450: }
17451: }
17452: }
17453: }
17454: }
1.1225 raeburn 17455: }
17456: }
17457: unless ($can_clone) {
17458: my $ccrole = 'cc';
17459: if ($args->{'crstype'} eq 'Community') {
17460: $ccrole = 'co';
17461: }
17462: my %roleshash =
17463: &Apache::lonnet::get_my_roles($args->{'ccuname'},
17464: $args->{'ccdomain'},
17465: 'userroles',['active'],[$ccrole],
17466: [$args->{'clonedomain'}]);
17467: if ($roleshash{$args->{'clonecourse'}.':'.$args->{'clonedomain'}.':'.$ccrole}) {
17468: $can_clone = 1;
17469: } elsif (&Apache::lonnet::is_course_owner($args->{'clonedomain'},$args->{'clonecourse'},
17470: $args->{'ccuname'},$args->{'ccdomain'})) {
17471: $can_clone = 1;
1.1221 raeburn 17472: }
17473: }
17474: unless ($can_clone) {
17475: if ($args->{'crstype'} eq 'Community') {
1.1344 raeburn 17476: push(@clonemsg,({
17477: mt => 'No new community created.',
17478: args => [],
17479: },
17480: {
17481: 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]).',
17482: args => [$args->{'ccuname'}.':'.$args->{'ccdomain'},$clonedesc{'description'}],
17483: }));
1.942 raeburn 17484: } else {
1.1344 raeburn 17485: push(@clonemsg,({
17486: mt => 'No new course created.',
17487: args => [],
17488: },
17489: {
17490: 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]).',
17491: args => [$args->{'ccuname'}.':'.$args->{'ccdomain'},$clonedesc{'description'}],
17492: }));
1.1221 raeburn 17493: }
1.566 albertel 17494: }
1.578 raeburn 17495: }
1.566 albertel 17496: }
1.1344 raeburn 17497: return ($can_clone,\@clonemsg,$cloneid,$clonehome,$clonetitle);
1.566 albertel 17498: }
17499:
1.444 albertel 17500: sub construct_course {
1.1262 raeburn 17501: my ($args,$logmsg,$courseid,$crsudom,$crsunum,$udom,$uname,$context,
1.1344 raeburn 17502: $cnum,$category,$coderef,$callercontext,$user_lh) = @_;
17503: my ($outcome,$msgref,$clonemsgref);
1.541 raeburn 17504: my $linefeed = '<br />'."\n";
17505: if ($context eq 'auto') {
17506: $linefeed = "\n";
17507: }
1.566 albertel 17508:
17509: #
17510: # Are we cloning?
17511: #
1.1344 raeburn 17512: my ($can_clone,$cloneid,$clonehome,$clonetitle);
1.566 albertel 17513: if (($args->{'clonecourse'}) && ($args->{'clonedomain'})) {
1.1344 raeburn 17514: ($can_clone,$clonemsgref,$cloneid,$clonehome,$clonetitle) = &check_clone($args,$linefeed);
1.566 albertel 17515: if (!$can_clone) {
1.1344 raeburn 17516: return (0,$outcome,$clonemsgref);
1.566 albertel 17517: }
17518: }
17519:
1.444 albertel 17520: #
17521: # Open course
17522: #
1.1239 raeburn 17523: my $showncrstype;
17524: if ($args->{'crstype'} eq 'Placement') {
17525: $showncrstype = 'placement test';
17526: } else {
17527: $showncrstype = lc($args->{'crstype'});
17528: }
1.444 albertel 17529: my %cenv=();
17530: $$courseid=&Apache::lonnet::createcourse($args->{'course_domain'},
17531: $args->{'cdescr'},
17532: $args->{'curl'},
17533: $args->{'course_home'},
17534: $args->{'nonstandard'},
17535: $args->{'crscode'},
17536: $args->{'ccuname'}.':'.
17537: $args->{'ccdomain'},
1.882 raeburn 17538: $args->{'crstype'},
1.1344 raeburn 17539: $cnum,$context,$category,
17540: $callercontext);
1.444 albertel 17541:
17542: # Note: The testing routines depend on this being output; see
17543: # Utils::Course. This needs to at least be output as a comment
17544: # if anyone ever decides to not show this, and Utils::Course::new
17545: # will need to be suitably modified.
1.1344 raeburn 17546: if (($callercontext eq 'auto') && ($user_lh ne '')) {
17547: $outcome .= &mt_user($user_lh,'New LON-CAPA [_1] ID: [_2]',$showncrstype,$$courseid).$linefeed;
17548: } else {
17549: $outcome .= &mt('New LON-CAPA [_1] ID: [_2]',$showncrstype,$$courseid).$linefeed;
17550: }
1.943 raeburn 17551: if ($$courseid =~ /^error:/) {
1.1344 raeburn 17552: return (0,$outcome,$clonemsgref);
1.943 raeburn 17553: }
17554:
1.444 albertel 17555: #
17556: # Check if created correctly
17557: #
1.479 albertel 17558: ($$crsudom,$$crsunum)= &LONCAPA::split_courseid($$courseid);
1.444 albertel 17559: my $crsuhome=&Apache::lonnet::homeserver($$crsunum,$$crsudom);
1.943 raeburn 17560: if ($crsuhome eq 'no_host') {
1.1344 raeburn 17561: if (($callercontext eq 'auto') && ($user_lh ne '')) {
17562: $outcome .= &mt_user($user_lh,
17563: 'Course creation failed, unrecognized course home server.');
17564: } else {
17565: $outcome .= &mt('Course creation failed, unrecognized course home server.');
17566: }
17567: $outcome .= $linefeed;
17568: return (0,$outcome,$clonemsgref);
1.943 raeburn 17569: }
1.541 raeburn 17570: $outcome .= &mt('Created on').': '.$crsuhome.$linefeed;
1.566 albertel 17571:
1.444 albertel 17572: #
1.566 albertel 17573: # Do the cloning
17574: #
1.1344 raeburn 17575: my @clonemsg;
1.566 albertel 17576: if ($can_clone && $cloneid) {
1.1344 raeburn 17577: push(@clonemsg,
17578: {
17579: mt => 'Created [_1] by cloning from [_2]',
17580: args => [$showncrstype,$clonetitle],
17581: });
1.566 albertel 17582: my %oldcenv=&Apache::lonnet::dump('environment',$$crsudom,$$crsunum);
1.444 albertel 17583: # Copy all files
1.1344 raeburn 17584: my @info =
17585: &Apache::lonclonecourse::copycoursefiles($cloneid,$$courseid,$args->{'datemode'},
17586: $args->{'dateshift'},$args->{'crscode'},
17587: $args->{'ccuname'}.':'.$args->{'ccdomain'},
17588: $args->{'tinyurls'});
17589: if (@info) {
17590: push(@clonemsg,@info);
17591: }
1.444 albertel 17592: # Restore URL
1.566 albertel 17593: $cenv{'url'}=$oldcenv{'url'};
1.444 albertel 17594: # Restore title
1.566 albertel 17595: $cenv{'description'}=$oldcenv{'description'};
1.955 raeburn 17596: # Restore creation date, creator and creation context.
17597: $cenv{'internal.created'}=$oldcenv{'internal.created'};
17598: $cenv{'internal.creator'}=$oldcenv{'internal.creator'};
17599: $cenv{'internal.creationcontext'}=$oldcenv{'internal.creationcontext'};
1.444 albertel 17600: # Mark as cloned
1.566 albertel 17601: $cenv{'clonedfrom'}=$cloneid;
1.638 www 17602: # Need to clone grading mode
17603: my %newenv=&Apache::lonnet::get('environment',['grading'],$$crsudom,$$crsunum);
17604: $cenv{'grading'}=$newenv{'grading'};
17605: # Do not clone these environment entries
17606: &Apache::lonnet::del('environment',
17607: ['default_enrollment_start_date',
17608: 'default_enrollment_end_date',
17609: 'question.email',
17610: 'policy.email',
17611: 'comment.email',
17612: 'pch.users.denied',
1.725 raeburn 17613: 'plc.users.denied',
17614: 'hidefromcat',
1.1121 raeburn 17615: 'checkforpriv',
1.1355 raeburn 17616: 'categories'],
1.638 www 17617: $$crsudom,$$crsunum);
1.1170 raeburn 17618: if ($args->{'textbook'}) {
17619: $cenv{'internal.textbook'} = $args->{'textbook'};
17620: }
1.444 albertel 17621: }
1.566 albertel 17622:
1.444 albertel 17623: #
17624: # Set environment (will override cloned, if existing)
17625: #
17626: my @sections = ();
17627: my @xlists = ();
17628: if ($args->{'crstype'}) {
17629: $cenv{'type'}=$args->{'crstype'};
17630: }
1.1371 raeburn 17631: if ($args->{'lti'}) {
17632: $cenv{'internal.lti'}=$args->{'lti'};
17633: }
1.444 albertel 17634: if ($args->{'crsid'}) {
17635: $cenv{'courseid'}=$args->{'crsid'};
17636: }
17637: if ($args->{'crscode'}) {
17638: $cenv{'internal.coursecode'}=$args->{'crscode'};
17639: }
17640: if ($args->{'crsquota'} ne '') {
17641: $cenv{'internal.coursequota'}=$args->{'crsquota'};
17642: } else {
17643: $cenv{'internal.coursequota'}=$args->{'crsquota'} = 20;
17644: }
17645: if ($args->{'ccuname'}) {
17646: $cenv{'internal.courseowner'} = $args->{'ccuname'}.
17647: ':'.$args->{'ccdomain'};
17648: } else {
17649: $cenv{'internal.courseowner'} = $args->{'curruser'};
17650: }
1.1116 raeburn 17651: if ($args->{'defaultcredits'}) {
17652: $cenv{'internal.defaultcredits'} = $args->{'defaultcredits'};
17653: }
1.444 albertel 17654: my @badclasses = (); # Used to accumulate sections/crosslistings that did not pass classlist access check for course owner.
1.1412 raeburn 17655: my @oklcsecs = (); # Used to accumulate LON-CAPA sections for validated institutional sections.
1.444 albertel 17656: if ($args->{'crssections'}) {
17657: $cenv{'internal.sectionnums'} = '';
17658: if ($args->{'crssections'} =~ m/,/) {
17659: @sections = split/,/,$args->{'crssections'};
17660: } else {
17661: $sections[0] = $args->{'crssections'};
17662: }
17663: if (@sections > 0) {
17664: foreach my $item (@sections) {
17665: my ($sec,$gp) = split/:/,$item;
17666: my $class = $args->{'crscode'}.$sec;
17667: my $addcheck = &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$class,$cenv{'internal.courseowner'});
17668: $cenv{'internal.sectionnums'} .= $item.',';
1.1412 raeburn 17669: if ($addcheck eq 'ok') {
17670: unless (grep(/^\Q$gp\E$/,@oklcsecs)) {
17671: push(@oklcsecs,$gp);
17672: }
17673: } else {
1.1263 raeburn 17674: push(@badclasses,$class);
1.444 albertel 17675: }
17676: }
17677: $cenv{'internal.sectionnums'} =~ s/,$//;
17678: }
17679: }
17680: # do not hide course coordinator from staff listing,
17681: # even if privileged
17682: $cenv{'nothideprivileged'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
1.1121 raeburn 17683: # add course coordinator's domain to domains to check for privileged users
17684: # if different to course domain
17685: if ($$crsudom ne $args->{'ccdomain'}) {
17686: $cenv{'checkforpriv'} = $args->{'ccdomain'};
17687: }
1.444 albertel 17688: # add crosslistings
17689: if ($args->{'crsxlist'}) {
17690: $cenv{'internal.crosslistings'}='';
17691: if ($args->{'crsxlist'} =~ m/,/) {
17692: @xlists = split/,/,$args->{'crsxlist'};
17693: } else {
17694: $xlists[0] = $args->{'crsxlist'};
17695: }
17696: if (@xlists > 0) {
17697: foreach my $item (@xlists) {
17698: my ($xl,$gp) = split/:/,$item;
17699: my $addcheck = &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$xl,$cenv{'internal.courseowner'});
17700: $cenv{'internal.crosslistings'} .= $item.',';
1.1412 raeburn 17701: if ($addcheck eq 'ok') {
17702: unless (grep(/^\Q$gp\E$/,@oklcsecs)) {
17703: push(@oklcsecs,$gp);
17704: }
17705: } else {
1.1263 raeburn 17706: push(@badclasses,$xl);
1.444 albertel 17707: }
17708: }
17709: $cenv{'internal.crosslistings'} =~ s/,$//;
17710: }
17711: }
17712: if ($args->{'autoadds'}) {
17713: $cenv{'internal.autoadds'}=$args->{'autoadds'};
17714: }
17715: if ($args->{'autodrops'}) {
17716: $cenv{'internal.autodrops'}=$args->{'autodrops'};
17717: }
17718: # check for notification of enrollment changes
17719: my @notified = ();
17720: if ($args->{'notify_owner'}) {
17721: if ($args->{'ccuname'} ne '') {
17722: push(@notified,$args->{'ccuname'}.':'.$args->{'ccdomain'});
17723: }
17724: }
17725: if ($args->{'notify_dc'}) {
17726: if ($uname ne '') {
1.630 raeburn 17727: push(@notified,$uname.':'.$udom);
1.444 albertel 17728: }
17729: }
17730: if (@notified > 0) {
17731: my $notifylist;
17732: if (@notified > 1) {
17733: $notifylist = join(',',@notified);
17734: } else {
17735: $notifylist = $notified[0];
17736: }
17737: $cenv{'internal.notifylist'} = $notifylist;
17738: }
17739: if (@badclasses > 0) {
17740: my %lt=&Apache::lonlocal::texthash(
1.1264 raeburn 17741: 'tclb' => 'The courses listed below were included as sections or crosslistings affiliated with your new LON-CAPA course.',
17742: 'howi' => 'However, if automated course roster updates are enabled for this class, these particular sections/crosslistings are not guaranteed to contribute towards enrollment.',
17743: 'itis' => 'It is possible that rights to access enrollment for these classes will be available through assignment of co-owners.',
1.444 albertel 17744: );
1.1264 raeburn 17745: my $badclass_msg = $lt{'tclb'}.$linefeed.$lt{'howi'}.$linefeed.
17746: &mt('That is because the user identified as the course owner ([_1]) does not have rights to access enrollment in these classes, as determined by the policies of your institution on access to official classlists',$cenv{'internal.courseowner'}).$linefeed.$lt{'itis'};
1.541 raeburn 17747: if ($context eq 'auto') {
17748: $outcome .= $badclass_msg.$linefeed;
1.1261 raeburn 17749: } else {
1.566 albertel 17750: $outcome .= '<div class="LC_warning">'.$badclass_msg.$linefeed.'<ul>'."\n";
1.1261 raeburn 17751: }
17752: foreach my $item (@badclasses) {
1.541 raeburn 17753: if ($context eq 'auto') {
1.1261 raeburn 17754: $outcome .= " - $item\n";
1.541 raeburn 17755: } else {
1.1261 raeburn 17756: $outcome .= "<li>$item</li>\n";
1.541 raeburn 17757: }
1.1261 raeburn 17758: }
17759: if ($context eq 'auto') {
17760: $outcome .= $linefeed;
17761: } else {
17762: $outcome .= "</ul><br /><br /></div>\n";
1.541 raeburn 17763: }
1.444 albertel 17764: }
17765: if ($args->{'no_end_date'}) {
17766: $args->{'endaccess'} = 0;
17767: }
1.1412 raeburn 17768: # If an official course with institutional sections is created by cloning
17769: # an existing course, section-specific hiding of course totals in student's
17770: # view of grades as copied from cloned course, will be checked for valid
17771: # sections.
17772: if (($can_clone && $cloneid) &&
17773: ($cenv{'internal.coursecode'} ne '') &&
17774: ($cenv{'grading'} eq 'standard') &&
17775: ($cenv{'hidetotals'} ne '') &&
17776: ($cenv{'hidetotals'} ne 'all')) {
17777: my @hidesecs;
17778: my $deletehidetotals;
17779: if (@oklcsecs) {
17780: foreach my $sec (split(/,/,$cenv{'hidetotals'})) {
17781: if (grep(/^\Q$sec$/,@oklcsecs)) {
17782: push(@hidesecs,$sec);
17783: }
17784: }
17785: if (@hidesecs) {
17786: $cenv{'hidetotals'} = join(',',@hidesecs);
17787: } else {
17788: $deletehidetotals = 1;
17789: }
17790: } else {
17791: $deletehidetotals = 1;
17792: }
17793: if ($deletehidetotals) {
17794: delete($cenv{'hidetotals'});
17795: &Apache::lonnet::del('environment',['hidetotals'],$$crsudom,$$crsunum);
17796: }
17797: }
1.444 albertel 17798: $cenv{'internal.autostart'}=$args->{'enrollstart'};
17799: $cenv{'internal.autoend'}=$args->{'enrollend'};
17800: $cenv{'default_enrollment_start_date'}=$args->{'startaccess'};
17801: $cenv{'default_enrollment_end_date'}=$args->{'endaccess'};
17802: if ($args->{'showphotos'}) {
17803: $cenv{'internal.showphotos'}=$args->{'showphotos'};
17804: }
17805: $cenv{'internal.authtype'} = $args->{'authtype'};
17806: $cenv{'internal.autharg'} = $args->{'autharg'};
17807: if ( ($cenv{'internal.authtype'} =~ /^krb/) && ($cenv{'internal.autoadds'} == 1)) {
17808: if (! defined($cenv{'internal.autharg'}) || $cenv{'internal.autharg'} eq '') {
1.541 raeburn 17809: 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');
17810: if ($context eq 'auto') {
17811: $outcome .= $krb_msg;
17812: } else {
1.566 albertel 17813: $outcome .= '<span class="LC_error">'.$krb_msg.'</span>';
1.541 raeburn 17814: }
17815: $outcome .= $linefeed;
1.444 albertel 17816: }
17817: }
17818: if (($args->{'ccdomain'}) && ($args->{'ccuname'})) {
17819: if ($args->{'setpolicy'}) {
17820: $cenv{'policy.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
17821: }
17822: if ($args->{'setcontent'}) {
17823: $cenv{'question.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
17824: }
1.1251 raeburn 17825: if ($args->{'setcomment'}) {
17826: $cenv{'comment.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
17827: }
1.444 albertel 17828: }
17829: if ($args->{'reshome'}) {
17830: $cenv{'reshome'}=$args->{'reshome'}.'/';
17831: $cenv{'reshome'}=~s/\/+$/\//;
17832: }
17833: #
17834: # course has keyed access
17835: #
17836: if ($args->{'setkeys'}) {
17837: $cenv{'keyaccess'}='yes';
17838: }
17839: # if specified, key authority is not course, but user
17840: # only active if keyaccess is yes
17841: if ($args->{'keyauth'}) {
1.487 albertel 17842: my ($user,$domain) = split(':',$args->{'keyauth'});
17843: $user = &LONCAPA::clean_username($user);
17844: $domain = &LONCAPA::clean_username($domain);
1.488 foxr 17845: if ($user ne '' && $domain ne '') {
1.487 albertel 17846: $cenv{'keyauth'}=$user.':'.$domain;
1.444 albertel 17847: }
17848: }
17849:
1.1166 raeburn 17850: #
1.1167 raeburn 17851: # generate and store uniquecode (available to course requester), if course should have one.
1.1166 raeburn 17852: #
17853: if ($args->{'uniquecode'}) {
17854: my ($code,$error) = &make_unique_code($$crsudom,$$crsunum);
17855: if ($code) {
17856: $cenv{'internal.uniquecode'} = $code;
1.1167 raeburn 17857: my %crsinfo =
17858: &Apache::lonnet::courseiddump($$crsudom,'.',1,'.','.',$$crsunum,undef,undef,'.');
17859: if (ref($crsinfo{$$crsudom.'_'.$$crsunum}) eq 'HASH') {
17860: $crsinfo{$$crsudom.'_'.$$crsunum}{'uniquecode'} = $code;
17861: my $putres = &Apache::lonnet::courseidput($$crsudom,\%crsinfo,$crsuhome,'notime');
17862: }
1.1166 raeburn 17863: if (ref($coderef)) {
17864: $$coderef = $code;
17865: }
17866: }
17867: }
17868:
1.444 albertel 17869: if ($args->{'disresdis'}) {
17870: $cenv{'pch.roles.denied'}='st';
17871: }
17872: if ($args->{'disablechat'}) {
17873: $cenv{'plc.roles.denied'}='st';
17874: }
17875:
17876: # Record we've not yet viewed the Course Initialization Helper for this
17877: # course
17878: $cenv{'course.helper.not.run'} = 1;
17879: #
17880: # Use new Randomseed
17881: #
17882: $cenv{'rndseed'}=&Apache::lonnet::latest_rnd_algorithm_id();;
17883: $cenv{'receiptalg'}=&Apache::lonnet::latest_receipt_algorithm_id();;
17884: #
17885: # The encryption code and receipt prefix for this course
17886: #
17887: $cenv{'internal.encseed'}=$Apache::lonnet::perlvar{'lonReceipt'}.$$.time.int(rand(9999));
17888: $cenv{'internal.encpref'}=100+int(9*rand(99));
17889: #
17890: # By default, use standard grading
17891: if (!defined($cenv{'grading'})) { $cenv{'grading'} = 'standard'; }
17892:
1.541 raeburn 17893: $outcome .= $linefeed.&mt('Setting environment').': '.
17894: &Apache::lonnet::put('environment',\%cenv,$$crsudom,$$crsunum).$linefeed;
1.444 albertel 17895: #
17896: # Open all assignments
17897: #
17898: if ($args->{'openall'}) {
1.1341 raeburn 17899: my $opendate = time;
17900: if ($args->{'openallfrom'} =~ /^\d+$/) {
17901: $opendate = $args->{'openallfrom'};
17902: }
1.444 albertel 17903: my $storeunder=$$crsudom.'_'.$$crsunum.'.0.opendate';
1.1341 raeburn 17904: my %storecontent = ($storeunder => $opendate,
1.444 albertel 17905: $storeunder.'.type' => 'date_start');
1.1341 raeburn 17906: $outcome .= &mt('All assignments open starting [_1]',
17907: &Apache::lonlocal::locallocaltime($opendate)).': '.
17908: &Apache::lonnet::cput
17909: ('resourcedata',\%storecontent,$$crsudom,$$crsunum).$linefeed;
1.444 albertel 17910: }
17911: #
17912: # Set first page
17913: #
17914: unless (($args->{'nonstandard'}) || ($args->{'firstres'} eq 'blank')
17915: || ($cloneid)) {
17916: $outcome .= &mt('Setting first resource').': ';
1.445 albertel 17917:
17918: my $map = '/uploaded/'.$$crsudom.'/'.$$crsunum.'/default.sequence';
17919: my ($errtext,$fatal)=&LONCAPA::map::mapread($map);
17920:
1.444 albertel 17921: $outcome .= ($fatal?$errtext:'read ok').' - ';
17922: my $title; my $url;
17923: if ($args->{'firstres'} eq 'syl') {
1.690 bisitz 17924: $title=&mt('Syllabus');
1.444 albertel 17925: $url='/public/'.$$crsudom.'/'.$$crsunum.'/syllabus';
17926: } else {
1.963 raeburn 17927: $title=&mt('Table of Contents');
1.444 albertel 17928: $url='/adm/navmaps';
17929: }
1.445 albertel 17930:
17931: $LONCAPA::map::resources[1]=$title.':'.$url.':false:start:res';
17932: (my $outtext,$errtext) = &LONCAPA::map::storemap($map,1);
17933:
17934: if ($errtext) { $fatal=2; }
1.541 raeburn 17935: $outcome .= ($fatal?$errtext:'write ok').$linefeed;
1.444 albertel 17936: }
1.566 albertel 17937:
1.1237 raeburn 17938: #
17939: # Set params for Placement Tests
17940: #
1.1239 raeburn 17941: if ($args->{'crstype'} eq 'Placement') {
17942: my %storecontent;
17943: my $prefix=$$crsudom.'_'.$$crsunum.'.0.';
17944: my %defaults = (
17945: buttonshide => { value => 'yes',
17946: type => 'string_yesno',},
17947: type => { value => 'randomizetry',
17948: type => 'string_questiontype',},
17949: maxtries => { value => 1,
17950: type => 'int_pos',},
17951: problemstatus => { value => 'no',
17952: type => 'string_problemstatus',},
17953: );
17954: foreach my $key (keys(%defaults)) {
17955: $storecontent{$prefix.$key} = $defaults{$key}{'value'};
17956: $storecontent{$prefix.$key.'.type'} = $defaults{$key}{'type'};
17957: }
1.1237 raeburn 17958: &Apache::lonnet::cput
17959: ('resourcedata',\%storecontent,$$crsudom,$$crsunum);
17960: }
17961:
1.1344 raeburn 17962: return (1,$outcome,\@clonemsg);
1.444 albertel 17963: }
17964:
1.1166 raeburn 17965: sub make_unique_code {
17966: my ($cdom,$cnum) = @_;
17967: # get lock on uniquecodes db
17968: my $lockhash = {
17969: $cnum."\0".'uniquecodes' => $env{'user.name'}.
17970: ':'.$env{'user.domain'},
17971: };
17972: my $tries = 0;
17973: my $gotlock = &Apache::lonnet::newput_dom('uniquecodes',$lockhash,$cdom);
17974: my ($code,$error);
17975:
17976: while (($gotlock ne 'ok') && ($tries<3)) {
17977: $tries ++;
17978: sleep 1;
17979: $gotlock = &Apache::lonnet::newput_dom('uniquecodes',$lockhash,$cdom);
17980: }
17981: if ($gotlock eq 'ok') {
17982: my %currcodes = &Apache::lonnet::dump_dom('uniquecodes',$cdom);
17983: my $gotcode;
17984: my $attempts = 0;
17985: while ((!$gotcode) && ($attempts < 100)) {
17986: $code = &generate_code();
17987: if (!exists($currcodes{$code})) {
17988: $gotcode = 1;
17989: unless (&Apache::lonnet::newput_dom('uniquecodes',{ $code => $cnum },$cdom) eq 'ok') {
17990: $error = 'nostore';
17991: }
17992: }
17993: $attempts ++;
17994: }
17995: my @del_lock = ($cnum."\0".'uniquecodes');
17996: my $dellockoutcome = &Apache::lonnet::del_dom('uniquecodes',\@del_lock,$cdom);
17997: } else {
17998: $error = 'nolock';
17999: }
18000: return ($code,$error);
18001: }
18002:
18003: sub generate_code {
18004: my $code;
18005: my @letts = qw(B C D G H J K M N P Q R S T V W X Z);
18006: for (my $i=0; $i<6; $i++) {
18007: my $lettnum = int (rand 2);
18008: my $item = '';
18009: if ($lettnum) {
18010: $item = $letts[int( rand(18) )];
18011: } else {
18012: $item = 1+int( rand(8) );
18013: }
18014: $code .= $item;
18015: }
18016: return $code;
18017: }
18018:
1.444 albertel 18019: ############################################################
18020: ############################################################
18021:
1.1237 raeburn 18022: # Community, Course and Placement Test
1.378 raeburn 18023: sub course_type {
18024: my ($cid) = @_;
18025: if (!defined($cid)) {
18026: $cid = $env{'request.course.id'};
18027: }
1.404 albertel 18028: if (defined($env{'course.'.$cid.'.type'})) {
18029: return $env{'course.'.$cid.'.type'};
1.378 raeburn 18030: } else {
18031: return 'Course';
1.377 raeburn 18032: }
18033: }
1.156 albertel 18034:
1.406 raeburn 18035: sub group_term {
18036: my $crstype = &course_type();
18037: my %names = (
18038: 'Course' => 'group',
1.865 raeburn 18039: 'Community' => 'group',
1.1237 raeburn 18040: 'Placement' => 'group',
1.406 raeburn 18041: );
18042: return $names{$crstype};
18043: }
18044:
1.902 raeburn 18045: sub course_types {
1.1310 raeburn 18046: my @types = ('official','unofficial','community','textbook','placement','lti');
1.902 raeburn 18047: my %typename = (
18048: official => 'Official course',
18049: unofficial => 'Unofficial course',
18050: community => 'Community',
1.1165 raeburn 18051: textbook => 'Textbook course',
1.1237 raeburn 18052: placement => 'Placement test',
1.1310 raeburn 18053: lti => 'LTI provider',
1.902 raeburn 18054: );
18055: return (\@types,\%typename);
18056: }
18057:
1.156 albertel 18058: sub icon {
18059: my ($file)=@_;
1.505 albertel 18060: my $curfext = lc((split(/\./,$file))[-1]);
1.168 albertel 18061: my $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/unknown.gif';
1.156 albertel 18062: my $embstyle = &Apache::loncommon::fileembstyle($curfext);
1.168 albertel 18063: if (!(!defined($embstyle) || $embstyle eq 'unk' || $embstyle eq 'hdn')) {
18064: if (-e $Apache::lonnet::perlvar{'lonDocRoot'}.'/'.
18065: $Apache::lonnet::perlvar{'lonIconsURL'}.'/'.
18066: $curfext.".gif") {
18067: $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/'.
18068: $curfext.".gif";
18069: }
18070: }
1.249 albertel 18071: return &lonhttpdurl($iconname);
1.154 albertel 18072: }
1.84 albertel 18073:
1.575 albertel 18074: sub lonhttpdurl {
1.692 www 18075: #
18076: # Had been used for "small fry" static images on separate port 8080.
18077: # Modify here if lightweight http functionality desired again.
18078: # Currently eliminated due to increasing firewall issues.
18079: #
1.575 albertel 18080: my ($url)=@_;
1.692 www 18081: return $url;
1.215 albertel 18082: }
18083:
1.213 albertel 18084: sub connection_aborted {
18085: my ($r)=@_;
18086: $r->print(" ");$r->rflush();
18087: my $c = $r->connection;
18088: return $c->aborted();
18089: }
18090:
1.221 foxr 18091: # Escapes strings that may have embedded 's that will be put into
1.222 foxr 18092: # strings as 'strings'.
18093: sub escape_single {
1.221 foxr 18094: my ($input) = @_;
1.223 albertel 18095: $input =~ s/\\/\\\\/g; # Escape the \'s..(must be first)>
1.221 foxr 18096: $input =~ s/\'/\\\'/g; # Esacpe the 's....
18097: return $input;
18098: }
1.223 albertel 18099:
1.222 foxr 18100: # Same as escape_single, but escape's "'s This
18101: # can be used for "strings"
18102: sub escape_double {
18103: my ($input) = @_;
18104: $input =~ s/\\/\\\\/g; # Escape the /'s..(must be first)>
18105: $input =~ s/\"/\\\"/g; # Esacpe the "s....
18106: return $input;
18107: }
1.223 albertel 18108:
1.222 foxr 18109: # Escapes the last element of a full URL.
18110: sub escape_url {
18111: my ($url) = @_;
1.238 raeburn 18112: my @urlslices = split(/\//, $url,-1);
1.369 www 18113: my $lastitem = &escape(pop(@urlslices));
1.1203 raeburn 18114: return &HTML::Entities::encode(join('/',@urlslices),"'").'/'.$lastitem;
1.222 foxr 18115: }
1.462 albertel 18116:
1.820 raeburn 18117: sub compare_arrays {
18118: my ($arrayref1,$arrayref2) = @_;
18119: my (@difference,%count);
18120: @difference = ();
18121: %count = ();
18122: if ((ref($arrayref1) eq 'ARRAY') && (ref($arrayref2) eq 'ARRAY')) {
18123: foreach my $element (@{$arrayref1}, @{$arrayref2}) { $count{$element}++; }
18124: foreach my $element (keys(%count)) {
18125: if ($count{$element} == 1) {
18126: push(@difference,$element);
18127: }
18128: }
18129: }
18130: return @difference;
18131: }
18132:
1.1322 raeburn 18133: sub lon_status_items {
18134: my %defaults = (
18135: E => 100,
18136: W => 4,
18137: N => 1,
1.1324 raeburn 18138: U => 5,
1.1322 raeburn 18139: threshold => 200,
18140: sysmail => 2500,
18141: );
18142: my %names = (
18143: E => 'Errors',
18144: W => 'Warnings',
18145: N => 'Notices',
1.1324 raeburn 18146: U => 'Unsent',
1.1322 raeburn 18147: );
18148: return (\%defaults,\%names);
18149: }
18150:
1.817 bisitz 18151: # -------------------------------------------------------- Initialize user login
1.462 albertel 18152: sub init_user_environment {
1.463 albertel 18153: my ($r, $username, $domain, $authhost, $form, $args) = @_;
1.462 albertel 18154: my $lonids=$Apache::lonnet::perlvar{'lonIDsDir'};
18155:
18156: my $public=($username eq 'public' && $domain eq 'public');
18157:
1.1415 raeburn 18158: my ($filename,$cookie,$userroles,$firstaccenv,$timerintenv,
18159: $coauthorenv);
1.462 albertel 18160: my $now=time;
18161:
18162: if ($public) {
18163: my $max_public=100;
18164: my $oldest;
18165: my $oldest_time=0;
18166: for(my $next=1;$next<=$max_public;$next++) {
18167: if (-e $lonids."/publicuser_$next.id") {
18168: my $mtime=(stat($lonids."/publicuser_$next.id"))[9];
18169: if ($mtime<$oldest_time || !$oldest_time) {
18170: $oldest_time=$mtime;
18171: $oldest=$next;
18172: }
18173: } else {
18174: $cookie="publicuser_$next";
18175: last;
18176: }
18177: }
18178: if (!$cookie) { $cookie="publicuser_$oldest"; }
18179: } else {
1.1275 raeburn 18180: # See if old ID present, if so, remove if this isn't a robot,
18181: # killing any existing non-robot sessions
1.463 albertel 18182: if (!$args->{'robot'}) {
18183: opendir(DIR,$lonids);
18184: while ($filename=readdir(DIR)) {
18185: if ($filename=~/^$username\_\d+\_$domain\_$authhost\.id$/) {
1.1320 raeburn 18186: if (tie(my %oldenv,'GDBM_File',"$lonids/$filename",
18187: &GDBM_READER(),0640)) {
1.1295 raeburn 18188: my $linkedfile;
1.1320 raeburn 18189: if (exists($oldenv{'user.linkedenv'})) {
18190: $linkedfile = $oldenv{'user.linkedenv'};
1.1295 raeburn 18191: }
1.1320 raeburn 18192: untie(%oldenv);
18193: if (unlink("$lonids/$filename")) {
18194: if ($linkedfile =~ /^[a-f0-9]+_linked$/) {
18195: if (-l "$lonids/$linkedfile.id") {
18196: unlink("$lonids/$linkedfile.id");
18197: }
1.1295 raeburn 18198: }
18199: }
18200: } else {
18201: unlink($lonids.'/'.$filename);
18202: }
1.463 albertel 18203: }
1.462 albertel 18204: }
1.463 albertel 18205: closedir(DIR);
1.1204 raeburn 18206: # If there is a undeleted lockfile for the user's paste buffer remove it.
18207: my $namespace = 'nohist_courseeditor';
18208: my $lockingkey = 'paste'."\0".'locked_num';
18209: my %lockhash = &Apache::lonnet::get($namespace,[$lockingkey],
18210: $domain,$username);
18211: if (exists($lockhash{$lockingkey})) {
18212: my $delresult = &Apache::lonnet::del($namespace,[$lockingkey],$domain,$username);
18213: unless ($delresult eq 'ok') {
18214: &Apache::lonnet::logthis("Failed to delete paste buffer locking key in $namespace for ".$username.":".$domain." Result was: $delresult");
18215: }
18216: }
1.462 albertel 18217: }
18218: # Give them a new cookie
1.463 albertel 18219: my $id = ($args->{'robot'} ? 'robot'.$args->{'robot'}
1.684 www 18220: : $now.$$.int(rand(10000)));
1.463 albertel 18221: $cookie="$username\_$id\_$domain\_$authhost";
1.462 albertel 18222:
18223: # Initialize roles
18224:
1.1414 raeburn 18225: ($userroles,$firstaccenv,$timerintenv,$coauthorenv) =
1.1062 raeburn 18226: &Apache::lonnet::rolesinit($domain,$username,$authhost);
1.462 albertel 18227: }
18228: # ------------------------------------ Check browser type and MathML capability
18229:
1.1194 raeburn 18230: my ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,$clientunicode,
18231: $clientos,$clientmobile,$clientinfo,$clientosversion) = &decode_user_agent($r);
1.462 albertel 18232:
18233: # ------------------------------------------------------------- Get environment
18234:
18235: my %userenv = &Apache::lonnet::dump('environment',$domain,$username);
18236: my ($tmp) = keys(%userenv);
1.1275 raeburn 18237: if ($tmp =~ /^(con_lost|error|no_such_host)/i) {
1.462 albertel 18238: undef(%userenv);
18239: }
18240: if (($userenv{'interface'}) && (!$form->{'interface'})) {
18241: $form->{'interface'}=$userenv{'interface'};
18242: }
18243: if ($userenv{'texengine'} eq 'ttm') { $clientmathml=1; }
18244:
18245: # --------------- Do not trust query string to be put directly into environment
1.817 bisitz 18246: foreach my $option ('interface','localpath','localres') {
18247: $form->{$option}=~s/[\n\r\=]//gs;
1.462 albertel 18248: }
18249: # --------------------------------------------------------- Write first profile
18250:
18251: {
1.1350 raeburn 18252: my $ip = &Apache::lonnet::get_requestor_ip($r);
1.462 albertel 18253: my %initial_env =
18254: ("user.name" => $username,
18255: "user.domain" => $domain,
18256: "user.home" => $authhost,
18257: "browser.type" => $clientbrowser,
18258: "browser.version" => $clientversion,
18259: "browser.mathml" => $clientmathml,
18260: "browser.unicode" => $clientunicode,
18261: "browser.os" => $clientos,
1.1137 raeburn 18262: "browser.mobile" => $clientmobile,
1.1141 raeburn 18263: "browser.info" => $clientinfo,
1.1194 raeburn 18264: "browser.osversion" => $clientosversion,
1.462 albertel 18265: "server.domain" => $Apache::lonnet::perlvar{'lonDefDomain'},
18266: "request.course.fn" => '',
18267: "request.course.uri" => '',
18268: "request.course.sec" => '',
18269: "request.role" => 'cm',
18270: "request.role.adv" => $env{'user.adv'},
1.1350 raeburn 18271: "request.host" => $ip,);
1.462 albertel 18272:
18273: if ($form->{'localpath'}) {
18274: $initial_env{"browser.localpath"} = $form->{'localpath'};
18275: $initial_env{"browser.localres"} = $form->{'localres'};
18276: }
18277:
18278: if ($form->{'interface'}) {
18279: $form->{'interface'}=~s/\W//gs;
18280: $initial_env{"browser.interface"} = $form->{'interface'};
18281: $env{'browser.interface'}=$form->{'interface'};
18282: }
18283:
1.1157 raeburn 18284: if ($form->{'iptoken'}) {
18285: my $lonhost = $r->dir_config('lonHostID');
18286: $initial_env{"user.noloadbalance"} = $lonhost;
18287: $env{'user.noloadbalance'} = $lonhost;
18288: }
18289:
1.1268 raeburn 18290: if ($form->{'noloadbalance'}) {
18291: my @hosts = &Apache::lonnet::current_machine_ids();
18292: my $hosthere = $form->{'noloadbalance'};
18293: if (grep(/^\Q$hosthere\E$/,@hosts)) {
18294: $initial_env{"user.noloadbalance"} = $hosthere;
18295: $env{'user.noloadbalance'} = $hosthere;
18296: }
18297: }
18298:
1.1016 raeburn 18299: unless ($domain eq 'public') {
1.1273 raeburn 18300: my %is_adv = ( is_adv => $env{'user.adv'} );
18301: my %domdef = &Apache::lonnet::get_domain_defaults($domain);
18302:
1.1414 raeburn 18303: foreach my $tool ('aboutme','blog','webdav','portfolio','portaccess','timezone') {
18304: $userenv{'availabletools.'.$tool} =
1.1273 raeburn 18305: &Apache::lonnet::usertools_access($username,$domain,$tool,'reload',
18306: undef,\%userenv,\%domdef,\%is_adv);
18307: }
1.980 raeburn 18308:
1.1311 raeburn 18309: foreach my $crstype ('official','unofficial','community','textbook','placement','lti') {
1.1273 raeburn 18310: $userenv{'canrequest.'.$crstype} =
18311: &Apache::lonnet::usertools_access($username,$domain,$crstype,
18312: 'reload','requestcourses',
18313: \%userenv,\%domdef,\%is_adv);
18314: }
1.724 raeburn 18315:
1.1418 raeburn 18316: if ((ref($userroles) eq 'HASH') && ($userroles->{'user.author'}) &&
18317: (exists($userroles->{"user.role.au./$domain/"}))) {
18318: if ($userenv{'authoreditors'}) {
18319: $userenv{'editors'} = $userenv{'authoreditors'};
18320: } elsif ($domdef{'editors'} ne '') {
18321: $userenv{'editors'} = $domdef{'editors'};
18322: } else {
18323: $userenv{'editors'} = 'edit,xml';
18324: }
1.1431 raeburn 18325: if ($userenv{'authorarchive'}) {
18326: $userenv{'canarchive'} = 1;
18327: } elsif (($userenv{'authorarchive'} eq '') &&
18328: ($domdef{'archive'})) {
18329: $userenv{'canarchive'} = 1;
18330: }
1.1418 raeburn 18331: }
18332:
1.1273 raeburn 18333: $userenv{'canrequest.author'} =
18334: &Apache::lonnet::usertools_access($username,$domain,'requestauthor',
18335: 'reload','requestauthor',
1.980 raeburn 18336: \%userenv,\%domdef,\%is_adv);
1.1273 raeburn 18337: my %reqauthor = &Apache::lonnet::get('requestauthor',['author_status','author'],
18338: $domain,$username);
18339: my $reqstatus = $reqauthor{'author_status'};
18340: if ($reqstatus eq 'approval' || $reqstatus eq 'approved') {
18341: if (ref($reqauthor{'author'}) eq 'HASH') {
18342: $userenv{'requestauthorqueued'} = $reqstatus.':'.
18343: $reqauthor{'author'}{'timestamp'};
18344: }
1.1092 raeburn 18345: }
1.1287 raeburn 18346: my ($types,$typename) = &course_types();
18347: if (ref($types) eq 'ARRAY') {
18348: my @options = ('approval','validate','autolimit');
18349: my $optregex = join('|',@options);
18350: my (%willtrust,%trustchecked);
18351: foreach my $type (@{$types}) {
18352: my $dom_str = $env{'environment.reqcrsotherdom.'.$type};
18353: if ($dom_str ne '') {
18354: my $updatedstr = '';
18355: my @possdomains = split(',',$dom_str);
18356: foreach my $entry (@possdomains) {
18357: my ($extdom,$extopt) = split(':',$entry);
18358: unless ($trustchecked{$extdom}) {
18359: $willtrust{$extdom} = &Apache::lonnet::will_trust('reqcrs',$domain,$extdom);
18360: $trustchecked{$extdom} = 1;
18361: }
18362: if ($willtrust{$extdom}) {
18363: $updatedstr .= $entry.',';
18364: }
18365: }
18366: $updatedstr =~ s/,$//;
18367: if ($updatedstr) {
18368: $userenv{'reqcrsotherdom.'.$type} = $updatedstr;
18369: } else {
18370: delete($userenv{'reqcrsotherdom.'.$type});
18371: }
18372: }
18373: }
18374: }
1.1092 raeburn 18375: }
1.462 albertel 18376: $env{'user.environment'} = "$lonids/$cookie.id";
1.1062 raeburn 18377:
1.462 albertel 18378: if (tie(my %disk_env,'GDBM_File',"$lonids/$cookie.id",
18379: &GDBM_WRCREAT(),0640)) {
18380: &_add_to_env(\%disk_env,\%initial_env);
18381: &_add_to_env(\%disk_env,\%userenv,'environment.');
18382: &_add_to_env(\%disk_env,$userroles);
1.1062 raeburn 18383: if (ref($firstaccenv) eq 'HASH') {
18384: &_add_to_env(\%disk_env,$firstaccenv);
18385: }
18386: if (ref($timerintenv) eq 'HASH') {
18387: &_add_to_env(\%disk_env,$timerintenv);
18388: }
1.1414 raeburn 18389: if (ref($coauthorenv) eq 'HASH') {
18390: if (keys(%{$coauthorenv})) {
18391: &_add_to_env(\%disk_env,$coauthorenv);
18392: }
18393: }
1.463 albertel 18394: if (ref($args->{'extra_env'})) {
18395: &_add_to_env(\%disk_env,$args->{'extra_env'});
18396: }
1.462 albertel 18397: untie(%disk_env);
18398: } else {
1.705 tempelho 18399: &Apache::lonnet::logthis("<span style=\"color:blue;\">WARNING: ".
18400: 'Could not create environment storage in lonauth: '.$!.'</span>');
1.462 albertel 18401: return 'error: '.$!;
18402: }
18403: }
18404: $env{'request.role'}='cm';
18405: $env{'request.role.adv'}=$env{'user.adv'};
18406: $env{'browser.type'}=$clientbrowser;
18407:
18408: return $cookie;
18409:
18410: }
18411:
18412: sub _add_to_env {
18413: my ($idf,$env_data,$prefix) = @_;
1.676 raeburn 18414: if (ref($env_data) eq 'HASH') {
18415: while (my ($key,$value) = each(%$env_data)) {
18416: $idf->{$prefix.$key} = $value;
18417: $env{$prefix.$key} = $value;
18418: }
1.462 albertel 18419: }
18420: }
18421:
1.685 tempelho 18422: # --- Get the symbolic name of a problem and the url
18423: sub get_symb {
18424: my ($request,$silent) = @_;
1.726 raeburn 18425: (my $url=$env{'form.url'}) =~ s-^https?\://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
1.685 tempelho 18426: my $symb=($env{'form.symb'} ne '' ? $env{'form.symb'} : (&Apache::lonnet::symbread($url)));
18427: if ($symb eq '') {
18428: if (!$silent) {
1.1071 raeburn 18429: if (ref($request)) {
18430: $request->print("Unable to handle ambiguous references:$url:.");
18431: }
1.685 tempelho 18432: return ();
18433: }
18434: }
18435: &Apache::lonenc::check_decrypt(\$symb);
18436: return ($symb);
18437: }
18438:
18439: # --------------------------------------------------------------Get annotation
18440:
18441: sub get_annotation {
18442: my ($symb,$enc) = @_;
18443:
18444: my $key = $symb;
18445: if (!$enc) {
18446: $key =
18447: &Apache::lonnet::clutter((&Apache::lonnet::decode_symb($symb))[2]);
18448: }
18449: my %annotation=&Apache::lonnet::get('nohist_annotations',[$key]);
18450: return $annotation{$key};
18451: }
18452:
18453: sub clean_symb {
1.731 raeburn 18454: my ($symb,$delete_enc) = @_;
1.685 tempelho 18455:
18456: &Apache::lonenc::check_decrypt(\$symb);
18457: my $enc = $env{'request.enc'};
1.731 raeburn 18458: if ($delete_enc) {
1.730 raeburn 18459: delete($env{'request.enc'});
18460: }
1.685 tempelho 18461:
18462: return ($symb,$enc);
18463: }
1.462 albertel 18464:
1.1181 raeburn 18465: ############################################################
18466: ############################################################
18467:
18468: =pod
18469:
18470: =head1 Routines for building display used to search for courses
18471:
18472:
18473: =over 4
18474:
18475: =item * &build_filters()
18476:
18477: Create markup for a table used to set filters to use when selecting
1.1182 raeburn 18478: courses in a domain. Used by lonpickcourse.pm, lonmodifycourse.pm
18479: and quotacheck.pl
18480:
1.1181 raeburn 18481:
18482: Inputs:
18483:
18484: filterlist - anonymous array of fields to include as potential filters
18485:
18486: crstype - course type
18487:
18488: roleelement - fifth arg in selectcourse_link() populates fifth arg in javascript: opencrsbrowser() function, used
18489: to pop-open a course selector (will contain "extra element").
18490:
18491: multelement - if multiple course selections will be allowed, this will be a hidden form element: name: multiple; value: 1
18492:
18493: filter - anonymous hash of criteria and their values
18494:
18495: action - form action
18496:
18497: numfiltersref - ref to scalar (count of number of elements in institutional codes -- e.g., 4 for year, semester, department, and number)
18498:
1.1182 raeburn 18499: caller - caller context (e.g., set to 'modifycourse' when routine is called from lonmodifycourse.pm)
1.1181 raeburn 18500:
18501: cloneruname - username of owner of new course who wants to clone
18502:
18503: clonerudom - domain of owner of new course who wants to clone
18504:
18505: typeelem - text to use for left column in row containing course type (i.e., Course, Community or Course/Community)
18506:
18507: codetitlesref - reference to array of titles of components in institutional codes (official courses)
18508:
18509: codedom - domain
18510:
18511: formname - value of form element named "form".
18512:
18513: fixeddom - domain, if fixed.
18514:
18515: prevphase - value to assign to form element named "phase" when going back to the previous screen
18516:
18517: cnameelement - name of form element in form on opener page which will receive title of selected course
18518:
18519: cnumelement - name of form element in form on opener page which will receive courseID of selected course
18520:
18521: cdomelement - name of form element in form on opener page which will receive domain of selected course
18522:
18523: setroles - includes access constraint identifier when setting a roles-based condition for acces to a portfolio file
18524:
18525: clonetext - hidden form elements containing list of courses cloneable by intended course owner when DC creates a course
18526:
18527: clonewarning - warning message about missing information for intended course owner when DC creates a course
18528:
1.1182 raeburn 18529:
1.1181 raeburn 18530: Returns: $output - HTML for display of search criteria, and hidden form elements.
18531:
1.1182 raeburn 18532:
1.1181 raeburn 18533: Side Effects: None
18534:
18535: =cut
18536:
18537: # ---------------------------------------------- search for courses based on last activity etc.
18538:
18539: sub build_filters {
18540: my ($filterlist,$crstype,$roleelement,$multelement,$filter,$action,
18541: $numtitlesref,$caller,$cloneruname,$clonerudom,$typeelement,
18542: $codetitlesref,$codedom,$formname,$fixeddom,$prevphase,
18543: $cnameelement,$cnumelement,$cdomelement,$setroles,
18544: $clonetext,$clonewarning) = @_;
1.1182 raeburn 18545: my ($list,$jscript);
1.1181 raeburn 18546: my $onchange = 'javascript:updateFilters(this)';
18547: my ($domainselectform,$sincefilterform,$createdfilterform,
18548: $ownerdomselectform,$persondomselectform,$instcodeform,
18549: $typeselectform,$instcodetitle);
18550: if ($formname eq '') {
18551: $formname = $caller;
18552: }
18553: foreach my $item (@{$filterlist}) {
18554: unless (($item eq 'descriptfilter') || ($item eq 'instcodefilter') ||
18555: ($item eq 'sincefilter') || ($item eq 'createdfilter')) {
18556: if ($item eq 'domainfilter') {
18557: $filter->{$item} = &LONCAPA::clean_domain($filter->{$item});
18558: } elsif ($item eq 'coursefilter') {
18559: $filter->{$item} = &LONCAPA::clean_courseid($filter->{$item});
18560: } elsif ($item eq 'ownerfilter') {
18561: $filter->{$item} = &LONCAPA::clean_username($filter->{$item});
18562: } elsif ($item eq 'ownerdomfilter') {
18563: $filter->{'ownerdomfilter'} =
18564: &LONCAPA::clean_domain($filter->{$item});
18565: $ownerdomselectform = &select_dom_form($filter->{'ownerdomfilter'},
18566: 'ownerdomfilter',1);
18567: } elsif ($item eq 'personfilter') {
18568: $filter->{$item} = &LONCAPA::clean_username($filter->{$item});
18569: } elsif ($item eq 'persondomfilter') {
18570: $persondomselectform = &select_dom_form($filter->{'persondomfilter'},
18571: 'persondomfilter',1);
18572: } else {
18573: $filter->{$item} =~ s/\W//g;
18574: }
18575: if (!$filter->{$item}) {
18576: $filter->{$item} = '';
18577: }
18578: }
18579: if ($item eq 'domainfilter') {
18580: my $allow_blank = 1;
18581: if ($formname eq 'portform') {
18582: $allow_blank=0;
18583: } elsif ($formname eq 'studentform') {
18584: $allow_blank=0;
18585: }
18586: if ($fixeddom) {
18587: $domainselectform = '<input type="hidden" name="domainfilter"'.
18588: ' value="'.$codedom.'" />'.
18589: &Apache::lonnet::domain($codedom,'description');
18590: } else {
18591: $domainselectform = &select_dom_form($filter->{$item},
18592: 'domainfilter',
18593: $allow_blank,'',$onchange);
18594: }
18595: } else {
18596: $list->{$item} = &HTML::Entities::encode($filter->{$item},'<>&"');
18597: }
18598: }
18599:
18600: # last course activity filter and selection
18601: $sincefilterform = &timebased_select_form('sincefilter',$filter);
18602:
18603: # course created filter and selection
18604: if (exists($filter->{'createdfilter'})) {
18605: $createdfilterform = &timebased_select_form('createdfilter',$filter);
18606: }
18607:
1.1239 raeburn 18608: my $prefix = $crstype;
18609: if ($crstype eq 'Placement') {
18610: $prefix = 'Placement Test'
18611: }
1.1181 raeburn 18612: my %lt = &Apache::lonlocal::texthash(
1.1239 raeburn 18613: 'cac' => "$prefix Activity",
18614: 'ccr' => "$prefix Created",
18615: 'cde' => "$prefix Title",
18616: 'cdo' => "$prefix Domain",
1.1181 raeburn 18617: 'ins' => 'Institutional Code',
18618: 'inc' => 'Institutional Categorization',
1.1239 raeburn 18619: 'cow' => "$prefix Owner/Co-owner",
18620: 'cop' => "$prefix Personnel Includes",
1.1181 raeburn 18621: 'cog' => 'Type',
18622: );
18623:
18624: if (($formname eq 'ccrs') || ($formname eq 'requestcrs')) {
18625: my $typeval = 'Course';
18626: if ($crstype eq 'Community') {
18627: $typeval = 'Community';
1.1239 raeburn 18628: } elsif ($crstype eq 'Placement') {
18629: $typeval = 'Placement';
1.1181 raeburn 18630: }
18631: $typeselectform = '<input type="hidden" name="type" value="'.$typeval.'" />';
18632: } else {
18633: $typeselectform = '<select name="type" size="1"';
18634: if ($onchange) {
18635: $typeselectform .= ' onchange="'.$onchange.'"';
18636: }
18637: $typeselectform .= '>'."\n";
1.1237 raeburn 18638: foreach my $posstype ('Course','Community','Placement') {
1.1239 raeburn 18639: my $shown;
18640: if ($posstype eq 'Placement') {
18641: $shown = &mt('Placement Test');
18642: } else {
18643: $shown = &mt($posstype);
18644: }
1.1181 raeburn 18645: $typeselectform.='<option value="'.$posstype.'"'.
1.1239 raeburn 18646: ($posstype eq $crstype ? ' selected="selected" ' : ''). ">".$shown."</option>\n";
1.1181 raeburn 18647: }
18648: $typeselectform.="</select>";
18649: }
18650:
18651: my ($cloneableonlyform,$cloneabletitle);
18652: if (exists($filter->{'cloneableonly'})) {
18653: my $cloneableon = '';
18654: my $cloneableoff = ' checked="checked"';
18655: if ($filter->{'cloneableonly'}) {
18656: $cloneableon = $cloneableoff;
18657: $cloneableoff = '';
18658: }
18659: $cloneableonlyform = '<span class="LC_nobreak"><label><input type="radio" name="cloneableonly" value="1" '.$cloneableon.'/> '.&mt('Required').'</label>'.(' 'x3).'<label><input type="radio" name="cloneableonly" value="" '.$cloneableoff.' /> '.&mt('No restriction').'</label></span>';
18660: if ($formname eq 'ccrs') {
1.1187 bisitz 18661: $cloneabletitle = &mt('Cloneable for [_1]',$cloneruname.':'.$clonerudom);
1.1181 raeburn 18662: } else {
18663: $cloneabletitle = &mt('Cloneable by you');
18664: }
18665: }
18666: my $officialjs;
18667: if ($crstype eq 'Course') {
18668: if (exists($filter->{'instcodefilter'})) {
1.1182 raeburn 18669: # if (($fixeddom) || ($formname eq 'requestcrs') ||
18670: # ($formname eq 'modifycourse') || ($formname eq 'filterpicker')) {
18671: if ($codedom) {
1.1181 raeburn 18672: $officialjs = 1;
18673: ($instcodeform,$jscript,$$numtitlesref) =
18674: &Apache::courseclassifier::instcode_selectors($codedom,'filterpicker',
18675: $officialjs,$codetitlesref);
18676: if ($jscript) {
1.1182 raeburn 18677: $jscript = '<script type="text/javascript">'."\n".
18678: '// <![CDATA['."\n".
18679: $jscript."\n".
18680: '// ]]>'."\n".
18681: '</script>'."\n";
1.1181 raeburn 18682: }
18683: }
18684: if ($instcodeform eq '') {
18685: $instcodeform =
18686: '<input type="text" name="instcodefilter" size="10" value="'.
18687: $list->{'instcodefilter'}.'" />';
18688: $instcodetitle = $lt{'ins'};
18689: } else {
18690: $instcodetitle = $lt{'inc'};
18691: }
18692: if ($fixeddom) {
18693: $instcodetitle .= '<br />('.$codedom.')';
18694: }
18695: }
18696: }
18697: my $output = qq|
18698: <form method="post" name="filterpicker" action="$action">
18699: <input type="hidden" name="form" value="$formname" />
18700: |;
18701: if ($formname eq 'modifycourse') {
18702: $output .= '<input type="hidden" name="phase" value="courselist" />'."\n".
18703: '<input type="hidden" name="prevphase" value="'.
18704: $prevphase.'" />'."\n";
1.1198 musolffc 18705: } elsif ($formname eq 'quotacheck') {
18706: $output .= qq|
18707: <input type="hidden" name="sortby" value="" />
18708: <input type="hidden" name="sortorder" value="" />
18709: |;
18710: } else {
1.1181 raeburn 18711: my $name_input;
18712: if ($cnameelement ne '') {
18713: $name_input = '<input type="hidden" name="cnameelement" value="'.
18714: $cnameelement.'" />';
18715: }
18716: $output .= qq|
1.1182 raeburn 18717: <input type="hidden" name="cnumelement" value="$cnumelement" />
18718: <input type="hidden" name="cdomelement" value="$cdomelement" />
1.1181 raeburn 18719: $name_input
18720: $roleelement
18721: $multelement
18722: $typeelement
18723: |;
18724: if ($formname eq 'portform') {
18725: $output .= '<input type="hidden" name="setroles" value="'.$setroles.'" />'."\n";
18726: }
18727: }
18728: if ($fixeddom) {
18729: $output .= '<input type="hidden" name="fixeddom" value="'.$fixeddom.'" />'."\n";
18730: }
18731: $output .= "<br />\n".&Apache::lonhtmlcommon::start_pick_box();
18732: if ($sincefilterform) {
18733: $output .= &Apache::lonhtmlcommon::row_title($lt{'cac'})
18734: .$sincefilterform
18735: .&Apache::lonhtmlcommon::row_closure();
18736: }
18737: if ($createdfilterform) {
18738: $output .= &Apache::lonhtmlcommon::row_title($lt{'ccr'})
18739: .$createdfilterform
18740: .&Apache::lonhtmlcommon::row_closure();
18741: }
18742: if ($domainselectform) {
18743: $output .= &Apache::lonhtmlcommon::row_title($lt{'cdo'})
18744: .$domainselectform
18745: .&Apache::lonhtmlcommon::row_closure();
18746: }
18747: if ($typeselectform) {
18748: if (($formname eq 'ccrs') || ($formname eq 'requestcrs')) {
18749: $output .= $typeselectform;
18750: } else {
18751: $output .= &Apache::lonhtmlcommon::row_title($lt{'cog'})
18752: .$typeselectform
18753: .&Apache::lonhtmlcommon::row_closure();
18754: }
18755: }
18756: if ($instcodeform) {
18757: $output .= &Apache::lonhtmlcommon::row_title($instcodetitle)
18758: .$instcodeform
18759: .&Apache::lonhtmlcommon::row_closure();
18760: }
18761: if (exists($filter->{'ownerfilter'})) {
18762: $output .= &Apache::lonhtmlcommon::row_title($lt{'cow'}).
18763: '<table><tr><td>'.&mt('Username').'<br />'.
18764: '<input type="text" name="ownerfilter" size="20" value="'.
18765: $list->{'ownerfilter'}.'" /></td><td>'.&mt('Domain').'<br />'.
18766: $ownerdomselectform.'</td></tr></table>'.
18767: &Apache::lonhtmlcommon::row_closure();
18768: }
18769: if (exists($filter->{'personfilter'})) {
18770: $output .= &Apache::lonhtmlcommon::row_title($lt{'cop'}).
18771: '<table><tr><td>'.&mt('Username').'<br />'.
18772: '<input type="text" name="personfilter" size="20" value="'.
18773: $list->{'personfilter'}.'" /></td><td>'.&mt('Domain').'<br />'.
18774: $persondomselectform.'</td></tr></table>'.
18775: &Apache::lonhtmlcommon::row_closure();
18776: }
18777: if (exists($filter->{'coursefilter'})) {
18778: $output .= &Apache::lonhtmlcommon::row_title(&mt('LON-CAPA course ID'))
18779: .'<input type="text" name="coursefilter" size="25" value="'
18780: .$list->{'coursefilter'}.'" />'
18781: .&Apache::lonhtmlcommon::row_closure();
18782: }
18783: if ($cloneableonlyform) {
18784: $output .= &Apache::lonhtmlcommon::row_title($cloneabletitle).
18785: $cloneableonlyform.&Apache::lonhtmlcommon::row_closure();
18786: }
18787: if (exists($filter->{'descriptfilter'})) {
18788: $output .= &Apache::lonhtmlcommon::row_title($lt{'cde'})
18789: .'<input type="text" name="descriptfilter" size="40" value="'
18790: .$list->{'descriptfilter'}.'" />'
18791: .&Apache::lonhtmlcommon::row_closure(1);
18792: }
18793: $output .= &Apache::lonhtmlcommon::end_pick_box().'<p>'.$clonetext."\n".
18794: '<input type="hidden" name="updater" value="" />'."\n".
18795: '<input type="submit" name="gosearch" value="'.
18796: &mt('Search').'" /></p>'."\n".'</form>'."\n".'<hr />'."\n";
18797: return $jscript.$clonewarning.$output;
18798: }
18799:
18800: =pod
18801:
18802: =item * &timebased_select_form()
18803:
1.1182 raeburn 18804: Create markup for a dropdown list used to select a time-based
1.1181 raeburn 18805: filter e.g., Course Activity, Course Created, when searching for courses
18806: or communities
18807:
18808: Inputs:
18809:
18810: item - name of form element (sincefilter or createdfilter)
18811:
18812: filter - anonymous hash of criteria and their values
18813:
18814: Returns: HTML for a select box contained a blank, then six time selections,
18815: with value set in incoming form variables currently selected.
18816:
18817: Side Effects: None
18818:
18819: =cut
18820:
18821: sub timebased_select_form {
18822: my ($item,$filter) = @_;
18823: if (ref($filter) eq 'HASH') {
18824: $filter->{$item} =~ s/[^\d-]//g;
18825: if (!$filter->{$item}) { $filter->{$item}=-1; }
18826: return &select_form(
18827: $filter->{$item},
18828: $item,
18829: { '-1' => '',
18830: '86400' => &mt('today'),
18831: '604800' => &mt('last week'),
18832: '2592000' => &mt('last month'),
18833: '7776000' => &mt('last three months'),
18834: '15552000' => &mt('last six months'),
18835: '31104000' => &mt('last year'),
18836: 'select_form_order' =>
18837: ['-1','86400','604800','2592000','7776000',
18838: '15552000','31104000']});
18839: }
18840: }
18841:
18842: =pod
18843:
18844: =item * &js_changer()
18845:
18846: Create script tag containing Javascript used to submit course search form
1.1183 raeburn 18847: when course type or domain is changed, and also to hide 'Searching ...' on
18848: page load completion for page showing search result.
1.1181 raeburn 18849:
18850: Inputs: None
18851:
1.1183 raeburn 18852: Returns: markup containing updateFilters() and hideSearching() javascript functions.
1.1181 raeburn 18853:
18854: Side Effects: None
18855:
18856: =cut
18857:
18858: sub js_changer {
18859: return <<ENDJS;
18860: <script type="text/javascript">
18861: // <![CDATA[
18862: function updateFilters(caller) {
18863: if (typeof(caller) != "undefined") {
18864: document.filterpicker.updater.value = caller.name;
18865: }
18866: document.filterpicker.submit();
18867: }
1.1183 raeburn 18868:
18869: function hideSearching() {
18870: if (document.getElementById('searching')) {
18871: document.getElementById('searching').style.display = 'none';
18872: }
18873: return;
18874: }
18875:
1.1181 raeburn 18876: // ]]>
18877: </script>
18878:
18879: ENDJS
18880: }
18881:
18882: =pod
18883:
1.1182 raeburn 18884: =item * &search_courses()
18885:
18886: Process selected filters form course search form and pass to lonnet::courseiddump
18887: to retrieve a hash for which keys are courseIDs which match the selected filters.
18888:
18889: Inputs:
18890:
18891: dom - domain being searched
18892:
18893: type - course type ('Course' or 'Community' or '.' if any).
18894:
18895: filter - anonymous hash of criteria and their values
18896:
18897: numtitles - for institutional codes - number of categories
18898:
18899: cloneruname - optional username of new course owner
18900:
18901: clonerudom - optional domain of new course owner
18902:
1.1221 raeburn 18903: domcloner - optional "domcloner" flag; has value=1 if user has ccc priv in domain being filtered by,
1.1182 raeburn 18904: (used when DC is using course creation form)
18905:
18906: codetitles - reference to array of titles of components in institutional codes (official courses).
18907:
1.1221 raeburn 18908: cc_clone - escaped comma separated list of courses for which course cloner has active CC role
18909: (and so can clone automatically)
18910:
18911: reqcrsdom - domain of new course, where search_courses is used to identify potential courses to clone
18912:
18913: reqinstcode - institutional code of new course, where search_courses is used to identify potential
18914: courses to clone
1.1182 raeburn 18915:
18916: Returns: %courses - hash of courses satisfying search criteria, keys = course IDs, values are corresponding colon-separated escaped description, institutional code, owner and type.
18917:
18918:
18919: Side Effects: None
18920:
18921: =cut
18922:
18923:
18924: sub search_courses {
1.1221 raeburn 18925: my ($dom,$type,$filter,$numtitles,$cloneruname,$clonerudom,$domcloner,$codetitles,
18926: $cc_clone,$reqcrsdom,$reqinstcode) = @_;
1.1182 raeburn 18927: my (%courses,%showcourses,$cloner);
18928: if (($filter->{'ownerfilter'} ne '') ||
18929: ($filter->{'ownerdomfilter'} ne '')) {
18930: $filter->{'combownerfilter'} = $filter->{'ownerfilter'}.':'.
18931: $filter->{'ownerdomfilter'};
18932: }
18933: foreach my $item ('descriptfilter','coursefilter','combownerfilter') {
18934: if (!$filter->{$item}) {
18935: $filter->{$item}='.';
18936: }
18937: }
18938: my $now = time;
18939: my $timefilter =
18940: ($filter->{'sincefilter'}==-1?1:$now-$filter->{'sincefilter'});
18941: my ($createdbefore,$createdafter);
18942: if (($filter->{'createdfilter'} ne '') && ($filter->{'createdfilter'} !=-1)) {
18943: $createdbefore = $now;
18944: $createdafter = $now-$filter->{'createdfilter'};
18945: }
18946: my ($instcodefilter,$regexpok);
18947: if ($numtitles) {
18948: if ($env{'form.official'} eq 'on') {
18949: $instcodefilter =
18950: &Apache::courseclassifier::instcode_search_str($dom,$numtitles,$codetitles);
18951: $regexpok = 1;
18952: } elsif ($env{'form.official'} eq 'off') {
18953: $instcodefilter = &Apache::courseclassifier::instcode_search_str($dom,$numtitles,$codetitles);
18954: unless ($instcodefilter eq '') {
18955: $regexpok = -1;
18956: }
18957: }
18958: } else {
18959: $instcodefilter = $filter->{'instcodefilter'};
18960: }
18961: if ($instcodefilter eq '') { $instcodefilter = '.'; }
18962: if ($type eq '') { $type = '.'; }
18963:
18964: if (($clonerudom ne '') && ($cloneruname ne '')) {
18965: $cloner = $cloneruname.':'.$clonerudom;
18966: }
18967: %courses = &Apache::lonnet::courseiddump($dom,
18968: $filter->{'descriptfilter'},
18969: $timefilter,
18970: $instcodefilter,
18971: $filter->{'combownerfilter'},
18972: $filter->{'coursefilter'},
18973: undef,undef,$type,$regexpok,undef,undef,
1.1221 raeburn 18974: undef,undef,$cloner,$cc_clone,
1.1182 raeburn 18975: $filter->{'cloneableonly'},
18976: $createdbefore,$createdafter,undef,
1.1221 raeburn 18977: $domcloner,undef,$reqcrsdom,$reqinstcode);
1.1182 raeburn 18978: if (($filter->{'personfilter'} ne '') && ($filter->{'persondomfilter'} ne '')) {
18979: my $ccrole;
18980: if ($type eq 'Community') {
18981: $ccrole = 'co';
18982: } else {
18983: $ccrole = 'cc';
18984: }
18985: my %rolehash = &Apache::lonnet::get_my_roles($filter->{'personfilter'},
18986: $filter->{'persondomfilter'},
18987: 'userroles',undef,
18988: [$ccrole,'in','ad','ep','ta','cr'],
18989: $dom);
18990: foreach my $role (keys(%rolehash)) {
18991: my ($cnum,$cdom,$courserole) = split(':',$role);
18992: my $cid = $cdom.'_'.$cnum;
18993: if (exists($courses{$cid})) {
18994: if (ref($courses{$cid}) eq 'HASH') {
18995: if (ref($courses{$cid}{roles}) eq 'ARRAY') {
18996: if (!grep(/^\Q$courserole\E$/,@{$courses{$cid}{roles}})) {
1.1263 raeburn 18997: push(@{$courses{$cid}{roles}},$courserole);
1.1182 raeburn 18998: }
18999: } else {
19000: $courses{$cid}{roles} = [$courserole];
19001: }
19002: $showcourses{$cid} = $courses{$cid};
19003: }
19004: }
19005: }
19006: %courses = %showcourses;
19007: }
19008: return %courses;
19009: }
19010:
19011: =pod
19012:
1.1181 raeburn 19013: =back
19014:
1.1207 raeburn 19015: =head1 Routines for version requirements for current course.
19016:
19017: =over 4
19018:
19019: =item * &check_release_required()
19020:
19021: Compares required LON-CAPA version with version on server, and
19022: if required version is newer looks for a server with the required version.
19023:
19024: Looks first at servers in user's owen domain; if none suitable, looks at
19025: servers in course's domain are permitted to host sessions for user's domain.
19026:
19027: Inputs:
19028:
19029: $loncaparev - Version on current server (format: Major.Minor.Subrelease-datestamp)
19030:
19031: $courseid - Course ID of current course
19032:
19033: $rolecode - User's current role in course (for switchserver query string).
19034:
19035: $required - LON-CAPA version needed by course (format: Major.Minor).
19036:
19037:
19038: Returns:
19039:
19040: $switchserver - query string tp append to /adm/switchserver call (if
19041: current server's LON-CAPA version is too old.
19042:
19043: $warning - Message is displayed if no suitable server could be found.
19044:
19045: =cut
19046:
19047: sub check_release_required {
19048: my ($loncaparev,$courseid,$rolecode,$required) = @_;
19049: my ($switchserver,$warning);
19050: if ($required ne '') {
19051: my ($reqdmajor,$reqdminor) = ($required =~ /^(\d+)\.(\d+)$/);
19052: my ($major,$minor) = ($loncaparev =~ /^\'?(\d+)\.(\d+)\.[\w.\-]+\'?$/);
19053: if ($reqdmajor ne '' && $reqdminor ne '') {
19054: my $otherserver;
19055: if (($major eq '' && $minor eq '') ||
19056: (($reqdmajor > $major) || (($reqdmajor == $major) && ($reqdminor > $minor)))) {
19057: my ($userdomserver) = &Apache::lonnet::choose_server($env{'user.domain'},undef,$required,1);
19058: my $switchlcrev =
19059: &Apache::lonnet::get_server_loncaparev($env{'user.domain'},
19060: $userdomserver);
19061: my ($swmajor,$swminor) = ($switchlcrev =~ /^\'?(\d+)\.(\d+)\.[\w.\-]+\'?$/);
19062: if (($swmajor eq '' && $swminor eq '') || ($reqdmajor > $swmajor) ||
19063: (($reqdmajor == $swmajor) && ($reqdminor > $swminor))) {
19064: my $cdom = $env{'course.'.$courseid.'.domain'};
19065: if ($cdom ne $env{'user.domain'}) {
19066: my ($coursedomserver,$coursehostname) = &Apache::lonnet::choose_server($cdom,undef,$required,1);
19067: my $serverhomeID = &Apache::lonnet::get_server_homeID($coursehostname);
19068: my $serverhomedom = &Apache::lonnet::host_domain($serverhomeID);
19069: my %defdomdefaults = &Apache::lonnet::get_domain_defaults($serverhomedom);
19070: my %udomdefaults = &Apache::lonnet::get_domain_defaults($env{'user.domain'});
19071: my $remoterev = &Apache::lonnet::get_server_loncaparev($serverhomedom,$coursedomserver);
19072: my $canhost =
19073: &Apache::lonnet::can_host_session($env{'user.domain'},
19074: $coursedomserver,
19075: $remoterev,
19076: $udomdefaults{'remotesessions'},
19077: $defdomdefaults{'hostedsessions'});
19078:
19079: if ($canhost) {
19080: $otherserver = $coursedomserver;
19081: } else {
19082: $warning = &mt('Requires LON-CAPA version [_1].',$env{'course.'.$courseid.'.internal.releaserequired'}).'<br />'. &mt("No suitable server could be found amongst servers in either your own domain or in the course's domain.");
19083: }
19084: } else {
19085: $warning = &mt('Requires LON-CAPA version [_1].',$env{'course.'.$courseid.'.internal.releaserequired'}).'<br />'.&mt("No suitable server could be found amongst servers in your own domain (which is also the course's domain).");
19086: }
19087: } else {
19088: $otherserver = $userdomserver;
19089: }
19090: }
19091: if ($otherserver ne '') {
19092: $switchserver = 'otherserver='.$otherserver.'&role='.$rolecode;
19093: }
19094: }
19095: }
19096: return ($switchserver,$warning);
19097: }
19098:
19099: =pod
19100:
19101: =item * &check_release_result()
19102:
19103: Inputs:
19104:
19105: $switchwarning - Warning message if no suitable server found to host session.
19106:
19107: $switchserver - query string to append to /adm/switchserver containing lonHostID
19108: and current role.
19109:
19110: Returns: HTML to display with information about requirement to switch server.
19111: Either displaying warning with link to Roles/Courses screen or
19112: display link to switchserver.
19113:
1.1181 raeburn 19114: =cut
19115:
1.1207 raeburn 19116: sub check_release_result {
19117: my ($switchwarning,$switchserver) = @_;
19118: my $output = &start_page('Selected course unavailable on this server').
1.1463 raeburn 19119: '<div class="LC_landmark" role="main"><p class="LC_warning">';
1.1207 raeburn 19120: if ($switchwarning) {
19121: $output .= $switchwarning.'<br /><a href="/adm/roles">';
19122: if (&show_course()) {
19123: $output .= &mt('Display courses');
19124: } else {
19125: $output .= &mt('Display roles');
19126: }
19127: $output .= '</a>';
19128: } elsif ($switchserver) {
19129: $output .= &mt('This course requires a newer version of LON-CAPA than is installed on this server.').
19130: '<br />'.
19131: '<a href="/adm/switchserver?'.$switchserver.'">'.
19132: &mt('Switch Server').
19133: '</a>';
19134: }
1.1463 raeburn 19135: $output .= '</p></div>'.&end_page();
1.1207 raeburn 19136: return $output;
19137: }
19138:
19139: =pod
19140:
19141: =item * &needs_coursereinit()
19142:
19143: Determine if course contents stored for user's session needs to be
19144: refreshed, because content has changed since "Big Hash" last tied.
19145:
19146: Check for change is made if time last checked is more than 10 minutes ago
19147: (by default).
19148:
19149: Inputs:
19150:
19151: $loncaparev - Version on current server (format: Major.Minor.Subrelease-datestamp)
19152:
19153: $interval (optional) - Time which may elapse (in s) between last check for content
19154: change in current course. (default: 600 s).
19155:
19156: Returns: an array; first element is:
19157:
19158: =over 4
19159:
19160: 'switch' - if content updates mean user's session
19161: needs to be switched to a server running a newer LON-CAPA version
19162:
19163: 'update' - if course session needs to be refreshed (i.e., Big Hash needs to be reloaded)
19164: on current server hosting user's session
19165:
19166: '' - if no action required.
19167:
19168: =back
19169:
19170: If first item element is 'switch':
19171:
19172: second item is $switchwarning - Warning message if no suitable server found to host session.
19173:
19174: third item is $switchserver - query string to append to /adm/switchserver containing lonHostID
19175: and current role.
19176:
19177: otherwise: no other elements returned.
19178:
19179: =back
19180:
19181: =cut
19182:
19183: sub needs_coursereinit {
19184: my ($loncaparev,$interval) = @_;
19185: return() unless ($env{'request.course.id'} && $env{'request.course.tied'});
19186: my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
19187: my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
19188: my $now = time;
19189: if ($interval eq '') {
19190: $interval = 600;
19191: }
19192: if (($now-$env{'request.course.timechecked'})>$interval) {
1.1282 raeburn 19193: &Apache::lonnet::appenv({'request.course.timechecked'=>$now});
1.1372 raeburn 19194: my $blocked = &blocking_status('reinit',undef,$cnum,$cdom,undef,1);
1.1282 raeburn 19195: if ($blocked) {
19196: return ();
19197: }
1.1391 raeburn 19198: my $update;
19199: my $lastmainchange = &Apache::lonnet::get_coursechange($cdom,$cnum);
19200: my $lastsuppchange = &Apache::lonnet::get_suppchange($cdom,$cnum);
19201: if ($lastmainchange > $env{'request.course.tied'}) {
19202: my ($needswitch,$switchwarning,$switchserver) = &switch_for_update($loncaparev,$cdom,$cnum);
19203: if ($needswitch) {
19204: return ('switch',$switchwarning,$switchserver);
19205: }
19206: $update = 'main';
19207: }
19208: if ($lastsuppchange > $env{'request.course.suppupdated'}) {
19209: if ($update) {
19210: $update = 'both';
19211: } else {
19212: my ($needswitch,$switchwarning,$switchserver) = &switch_for_update($loncaparev,$cdom,$cnum);
19213: if ($needswitch) {
19214: return ('switch',$switchwarning,$switchserver);
19215: } else {
19216: $update = 'supp';
1.1207 raeburn 19217: }
19218: }
1.1391 raeburn 19219: }
1.1453 raeburn 19220: return ($update);
1.1391 raeburn 19221: }
19222: return ();
19223: }
19224:
19225: sub switch_for_update {
19226: my ($loncaparev,$cdom,$cnum) = @_;
19227: my %curr_reqd_hash = &Apache::lonnet::userenvironment($cdom,$cnum,'internal.releaserequired');
19228: if ($curr_reqd_hash{'internal.releaserequired'} ne '') {
19229: my $required = $env{'course.'.$cdom.'_'.$cnum.'.internal.releaserequired'};
19230: if ($curr_reqd_hash{'internal.releaserequired'} ne $required) {
19231: &Apache::lonnet::appenv({'course.'.$cdom.'_'.$cnum.'.internal.releaserequired' =>
19232: $curr_reqd_hash{'internal.releaserequired'}});
19233: my ($switchserver,$switchwarning) =
19234: &check_release_required($loncaparev,$cdom.'_'.$cnum,$env{'request.role'},
19235: $curr_reqd_hash{'internal.releaserequired'});
19236: if ($switchwarning ne '' || $switchserver ne '') {
19237: return ('switch',$switchwarning,$switchserver);
19238: }
1.1207 raeburn 19239: }
19240: }
19241: return ();
19242: }
1.1181 raeburn 19243:
1.1083 raeburn 19244: sub update_content_constraints {
1.1395 raeburn 19245: my ($cdom,$cnum,$chome,$cid) = @_;
1.1083 raeburn 19246: my %curr_reqd_hash = &Apache::lonnet::userenvironment($cdom,$cnum,'internal.releaserequired');
19247: my ($reqdmajor,$reqdminor) = split(/\./,$curr_reqd_hash{'internal.releaserequired'});
1.1307 raeburn 19248: my (%checkresponsetypes,%checkcrsrestypes);
1.1083 raeburn 19249: foreach my $key (keys(%Apache::lonnet::needsrelease)) {
1.1236 raeburn 19250: my ($item,$name,$value) = split(/:/,$key);
1.1083 raeburn 19251: if ($item eq 'resourcetag') {
19252: if ($name eq 'responsetype') {
19253: $checkresponsetypes{$value} = $Apache::lonnet::needsrelease{$key}
19254: }
1.1307 raeburn 19255: } elsif ($item eq 'course') {
19256: if ($name eq 'courserestype') {
19257: $checkcrsrestypes{$value} = $Apache::lonnet::needsrelease{$key};
19258: }
1.1083 raeburn 19259: }
19260: }
19261: my $navmap = Apache::lonnavmaps::navmap->new();
19262: if (defined($navmap)) {
1.1307 raeburn 19263: my (%allresponses,%allcrsrestypes);
19264: foreach my $res ($navmap->retrieveResources(undef,sub { $_[0]->is_problem() || $_[0]->is_tool() },1,0)) {
19265: if ($res->is_tool()) {
19266: if ($allcrsrestypes{'exttool'}) {
19267: $allcrsrestypes{'exttool'} ++;
19268: } else {
19269: $allcrsrestypes{'exttool'} = 1;
19270: }
19271: next;
19272: }
1.1083 raeburn 19273: my %responses = $res->responseTypes();
19274: foreach my $key (keys(%responses)) {
19275: next unless(exists($checkresponsetypes{$key}));
19276: $allresponses{$key} += $responses{$key};
19277: }
19278: }
19279: foreach my $key (keys(%allresponses)) {
19280: my ($major,$minor) = split(/\./,$checkresponsetypes{$key});
19281: if (($major > $reqdmajor) || ($major == $reqdmajor && $minor > $reqdminor)) {
19282: ($reqdmajor,$reqdminor) = ($major,$minor);
19283: }
19284: }
1.1307 raeburn 19285: foreach my $key (keys(%allcrsrestypes)) {
1.1308 raeburn 19286: my ($major,$minor) = split(/\./,$checkcrsrestypes{$key});
1.1307 raeburn 19287: if (($major > $reqdmajor) || ($major == $reqdmajor && $minor > $reqdminor)) {
19288: ($reqdmajor,$reqdminor) = ($major,$minor);
19289: }
19290: }
1.1083 raeburn 19291: undef($navmap);
19292: }
1.1391 raeburn 19293: if (&Apache::lonnet::count_supptools($cnum,$cdom,1)) {
1.1308 raeburn 19294: my ($major,$minor) = split(/\./,$checkcrsrestypes{'exttool'});
19295: if (($major > $reqdmajor) || ($major == $reqdmajor && $minor > $reqdminor)) {
19296: ($reqdmajor,$reqdminor) = ($major,$minor);
19297: }
19298: }
1.1083 raeburn 19299: unless (($reqdmajor eq '') && ($reqdminor eq '')) {
19300: &Apache::lonnet::update_released_required($reqdmajor.'.'.$reqdminor,$cdom,$cnum,$chome,$cid);
19301: }
19302: return;
19303: }
19304:
1.1110 raeburn 19305: sub allmaps_incourse {
19306: my ($cdom,$cnum,$chome,$cid) = @_;
19307: if ($cdom eq '' || $cnum eq '' || $chome eq '' || $cid eq '') {
19308: $cid = $env{'request.course.id'};
19309: $cdom = $env{'course.'.$cid.'.domain'};
19310: $cnum = $env{'course.'.$cid.'.num'};
19311: $chome = $env{'course.'.$cid.'.home'};
19312: }
19313: my %allmaps = ();
19314: my $lastchange =
19315: &Apache::lonnet::get_coursechange($cdom,$cnum);
19316: if ($lastchange > $env{'request.course.tied'}) {
19317: my ($furl,$ferr) = &Apache::lonuserstate::readmap("$cdom/$cnum");
19318: unless ($ferr) {
1.1395 raeburn 19319: &update_content_constraints($cdom,$cnum,$chome,$cid);
1.1110 raeburn 19320: }
19321: }
19322: my $navmap = Apache::lonnavmaps::navmap->new();
19323: if (defined($navmap)) {
19324: foreach my $res ($navmap->retrieveResources(undef,sub { $_[0]->is_map() },1,0,1)) {
19325: $allmaps{$res->src()} = 1;
19326: }
19327: }
19328: return \%allmaps;
19329: }
19330:
1.1083 raeburn 19331: sub parse_supplemental_title {
19332: my ($title) = @_;
19333:
19334: my ($foldertitle,$renametitle);
19335: if ($title =~ /&&&/) {
19336: $title = &HTML::Entites::decode($title);
19337: }
19338: if ($title =~ m/^(\d+)___&&&___($match_username)___&&&___($match_domain)___&&&___(.*)$/) {
19339: $renametitle=$4;
19340: my ($time,$uname,$udom) = ($1,$2,$3);
19341: $foldertitle=&Apache::lontexconvert::msgtexconverted($4);
19342: my $name = &plainname($uname,$udom);
19343: $name = &HTML::Entities::encode($name,'"<>&\'');
19344: $renametitle = &HTML::Entities::encode($renametitle,'"<>&\'');
1.1401 raeburn 19345: $title='<i>'.&Apache::lonlocal::locallocaltime($time).'</i> '.$name;
1.1402 raeburn 19346: if ($foldertitle ne '') {
1.1401 raeburn 19347: $title .= ': <br />'.$foldertitle;
19348: }
1.1083 raeburn 19349: }
19350: if (wantarray) {
19351: return ($title,$foldertitle,$renametitle);
19352: }
19353: return $title;
19354: }
19355:
1.1395 raeburn 19356: sub get_supplemental {
19357: my ($cnum,$cdom,$ignorecache,$possdel)=@_;
19358: my $hashid=$cnum.':'.$cdom;
19359: my ($supplemental,$cached,$set_httprefs);
19360: unless ($ignorecache) {
19361: ($supplemental,$cached) = &Apache::lonnet::is_cached_new('supplemental',$hashid);
19362: }
19363: unless (defined($cached)) {
19364: my $chome=&Apache::lonnet::homeserver($cnum,$cdom);
19365: unless ($chome eq 'no_host') {
19366: my @order = @LONCAPA::map::order;
19367: my @resources = @LONCAPA::map::resources;
19368: my @resparms = @LONCAPA::map::resparms;
19369: my @zombies = @LONCAPA::map::zombies;
19370: my ($errors,%ids,%hidden);
19371: $errors =
19372: &recurse_supplemental($cnum,$cdom,'supplemental.sequence',
19373: $errors,$possdel,\%ids,\%hidden);
19374: @LONCAPA::map::order = @order;
19375: @LONCAPA::map::resources = @resources;
19376: @LONCAPA::map::resparms = @resparms;
19377: @LONCAPA::map::zombies = @zombies;
19378: $set_httprefs = 1;
19379: if ($env{'request.course.id'} eq $cdom.'_'.$cnum) {
19380: &Apache::lonnet::appenv({'request.course.suppupdated' => time});
19381: }
19382: $supplemental = {
19383: ids => \%ids,
19384: hidden => \%hidden,
19385: };
19386: &Apache::lonnet::do_cache_new('supplemental',$hashid,$supplemental,600);
19387: }
19388: }
19389: return ($supplemental,$set_httprefs);
19390: }
19391:
1.1143 raeburn 19392: sub recurse_supplemental {
1.1391 raeburn 19393: my ($cnum,$cdom,$suppmap,$errors,$possdel,$suppids,$hiddensupp,$hidden) = @_;
19394: if (($suppmap) && (ref($suppids) eq 'HASH') && (ref($hiddensupp) eq 'HASH')) {
19395: my $mapnum;
19396: if ($suppmap eq 'supplemental.sequence') {
19397: $mapnum = 0;
19398: } else {
19399: ($mapnum) = ($suppmap =~ /^supplemental_(\d+)\.sequence$/);
19400: }
1.1143 raeburn 19401: my ($errtext,$fatal) = &LONCAPA::map::mapread('/uploaded/'.$cdom.'/'.$cnum.'/'.$suppmap);
19402: if ($fatal) {
19403: $errors ++;
19404: } else {
1.1389 raeburn 19405: my @order = @LONCAPA::map::order;
19406: if (@order > 0) {
19407: my @resources = @LONCAPA::map::resources;
1.1391 raeburn 19408: my @resparms = @LONCAPA::map::resparms;
1.1389 raeburn 19409: foreach my $idx (@order) {
19410: my ($title,$src,$ext,$type,$status)=split(/\:/,$resources[$idx]);
1.1143 raeburn 19411: if (($src ne '') && ($status eq 'res')) {
1.1391 raeburn 19412: my $id = $mapnum.':'.$idx;
19413: push(@{$suppids->{$src}},$id);
19414: if (($hidden) || (&get_supp_parameter($resparms[$idx],'parameter_hiddenresource') =~ /^yes/i)) {
19415: $hiddensupp->{$id} = 1;
19416: }
1.1146 raeburn 19417: if ($src =~ m{^\Q/uploaded/$cdom/$cnum/\E(supplemental_\d+\.sequence)$}) {
1.1391 raeburn 19418: $errors = &recurse_supplemental($cnum,$cdom,$1,$errors,$possdel,$suppids,
19419: $hiddensupp,$hiddensupp->{$id});
1.1143 raeburn 19420: } else {
1.1391 raeburn 19421: my $allowed;
19422: if (($env{'request.role.adv'}) || (!$hiddensupp->{$id})) {
19423: $allowed = 1;
19424: } elsif ($possdel) {
19425: foreach my $item (@{$suppids->{$src}}) {
19426: next if ($item eq $id);
19427: unless ($hiddensupp->{$item}) {
19428: $allowed = 1;
19429: last;
19430: }
19431: }
19432: if ((!$allowed) && (exists($env{'httpref.'.$src}))) {
19433: &Apache::lonnet::delenv('httpref.'.$src);
19434: }
19435: }
19436: if ($allowed && (!exists($env{'httpref.'.$src}))) {
19437: &Apache::lonnet::allowuploaded('/adm/coursedoc',$src);
1.1308 raeburn 19438: }
1.1143 raeburn 19439: }
19440: }
19441: }
19442: }
19443: }
19444: }
1.1391 raeburn 19445: return $errors;
19446: }
19447:
19448: sub set_supp_httprefs {
19449: my ($cnum,$cdom,$supplemental,$possdel) = @_;
19450: if (ref($supplemental) eq 'HASH') {
19451: if ((ref($supplemental->{'ids'}) eq 'HASH') && (ref($supplemental->{'hidden'}) eq 'HASH')) {
19452: foreach my $src (keys(%{$supplemental->{'ids'}})) {
19453: next if ($src =~ /\.sequence$/);
19454: if (ref($supplemental->{'ids'}->{$src}) eq 'ARRAY') {
19455: my $allowed;
19456: if ($env{'request.role.adv'}) {
19457: $allowed = 1;
19458: } else {
19459: foreach my $id (@{$supplemental->{'ids'}->{$src}}) {
19460: unless ($supplemental->{'hidden'}->{$id}) {
19461: $allowed = 1;
19462: last;
19463: }
19464: }
19465: }
19466: if (exists($env{'httpref.'.$src})) {
19467: if ($possdel) {
19468: unless ($allowed) {
19469: &Apache::lonnet::delenv('httpref.'.$src);
19470: }
19471: }
19472: } elsif ($allowed) {
19473: &Apache::lonnet::allowuploaded('/adm/coursedoc',$src);
19474: }
19475: }
19476: }
19477: if ($env{'request.course.id'} eq $cdom.'_'.$cnum) {
19478: &Apache::lonnet::appenv({'request.course.suppupdated' => time});
19479: }
19480: }
19481: }
19482: }
19483:
19484: sub get_supp_parameter {
19485: my ($resparm,$name)=@_;
19486: return if ($resparm eq '');
19487: my $value=undef;
19488: my $ptype=undef;
19489: foreach (split('&&&',$resparm)) {
19490: my ($thistype,$thisname,$thisvalue)=split('___',$_);
19491: if ($thisname eq $name) {
19492: $value=$thisvalue;
19493: $ptype=$thistype;
19494: }
19495: }
19496: return $value;
1.1143 raeburn 19497: }
19498:
1.1101 raeburn 19499: sub symb_to_docspath {
1.1267 raeburn 19500: my ($symb,$navmapref) = @_;
19501: return unless ($symb && ref($navmapref));
1.1101 raeburn 19502: my ($mapurl,$id,$resurl) = &Apache::lonnet::decode_symb($symb);
19503: if ($resurl=~/\.(sequence|page)$/) {
19504: $mapurl=$resurl;
19505: } elsif ($resurl eq 'adm/navmaps') {
19506: $mapurl=$env{'course.'.$env{'request.course.id'}.'.url'};
19507: }
19508: my $mapresobj;
1.1267 raeburn 19509: unless (ref($$navmapref)) {
19510: $$navmapref = Apache::lonnavmaps::navmap->new();
19511: }
19512: if (ref($$navmapref)) {
19513: $mapresobj = $$navmapref->getResourceByUrl($mapurl);
1.1101 raeburn 19514: }
19515: $mapurl=~s{^.*/([^/]+)\.(\w+)$}{$1};
19516: my $type=$2;
19517: my $path;
19518: if (ref($mapresobj)) {
19519: my $pcslist = $mapresobj->map_hierarchy();
19520: if ($pcslist ne '') {
19521: foreach my $pc (split(/,/,$pcslist)) {
19522: next if ($pc <= 1);
1.1267 raeburn 19523: my $res = $$navmapref->getByMapPc($pc);
1.1101 raeburn 19524: if (ref($res)) {
19525: my $thisurl = $res->src();
19526: $thisurl=~s{^.*/([^/]+)\.\w+$}{$1};
19527: my $thistitle = $res->title();
19528: $path .= '&'.
19529: &Apache::lonhtmlcommon::entity_encode($thisurl).'&'.
1.1146 raeburn 19530: &escape($thistitle).
1.1101 raeburn 19531: ':'.$res->randompick().
19532: ':'.$res->randomout().
19533: ':'.$res->encrypted().
19534: ':'.$res->randomorder().
19535: ':'.$res->is_page();
19536: }
19537: }
19538: }
19539: $path =~ s/^\&//;
19540: my $maptitle = $mapresobj->title();
19541: if ($mapurl eq 'default') {
1.1129 raeburn 19542: $maptitle = 'Main Content';
1.1101 raeburn 19543: }
19544: $path .= (($path ne '')? '&' : '').
19545: &Apache::lonhtmlcommon::entity_encode($mapurl).'&'.
1.1146 raeburn 19546: &escape($maptitle).
1.1101 raeburn 19547: ':'.$mapresobj->randompick().
19548: ':'.$mapresobj->randomout().
19549: ':'.$mapresobj->encrypted().
19550: ':'.$mapresobj->randomorder().
19551: ':'.$mapresobj->is_page();
19552: } else {
19553: my $maptitle = &Apache::lonnet::gettitle($mapurl);
19554: my $ispage = (($type eq 'page')? 1 : '');
19555: if ($mapurl eq 'default') {
1.1129 raeburn 19556: $maptitle = 'Main Content';
1.1101 raeburn 19557: }
19558: $path = &Apache::lonhtmlcommon::entity_encode($mapurl).'&'.
1.1146 raeburn 19559: &escape($maptitle).':::::'.$ispage;
1.1101 raeburn 19560: }
19561: unless ($mapurl eq 'default') {
19562: $path = 'default&'.
1.1146 raeburn 19563: &escape('Main Content').
1.1101 raeburn 19564: ':::::&'.$path;
19565: }
19566: return $path;
19567: }
19568:
1.1393 raeburn 19569: sub validate_folderpath {
19570: my ($supplementalflag,$allowed,$coursenum,$coursedom) = @_;
19571: if ($env{'form.folderpath'} ne '') {
19572: my @items = split(/\&/,$env{'form.folderpath'});
1.1394 raeburn 19573: my ($badpath,$changed,$got_supp,$supppath,%supphidden,%suppids);
1.1393 raeburn 19574: for (my $i=0; $i<@items; $i++) {
19575: my $odd = $i%2;
19576: if (($odd) && (!$supplementalflag) && ($items[$i] !~ /^[^:]*:(|\d+):(|1):(|1):(|1):(|1)$/)) {
19577: $badpath = 1;
1.1394 raeburn 19578: } elsif ($odd && $supplementalflag) {
1.1393 raeburn 19579: my $idx = $i-1;
1.1394 raeburn 19580: if ($items[$i] =~ /^([^:]*)::(|1):::$/) {
19581: my $esc_name = $1;
19582: if ((!$allowed) || ($items[$idx] eq 'supplemental')) {
19583: $supppath .= '&'.$esc_name;
19584: $changed = 1;
19585: } else {
19586: $supppath .= '&'.$items[$i];
19587: }
19588: } elsif (($allowed) && ($items[$idx] ne 'supplemental')) {
19589: $changed = 1;
1.1393 raeburn 19590: my $is_hidden;
19591: unless ($got_supp) {
1.1395 raeburn 19592: my ($supplemental) = &get_supplemental($coursenum,$coursedom);
1.1393 raeburn 19593: if (ref($supplemental) eq 'HASH') {
19594: if (ref($supplemental->{'hidden'}) eq 'HASH') {
19595: %supphidden = %{$supplemental->{'hidden'}};
19596: }
19597: if (ref($supplemental->{'ids'}) eq 'HASH') {
19598: %suppids = %{$supplemental->{'ids'}};
19599: }
19600: }
19601: $got_supp = 1;
19602: }
19603: if (ref($suppids{"/uploaded/$coursedom/$coursenum/$items[$idx].sequence"}) eq 'ARRAY') {
19604: my $mapid = $suppids{"/uploaded/$coursedom/$coursenum/$items[$idx].sequence"}->[0];
19605: if ($supphidden{$mapid}) {
19606: $is_hidden = 1;
19607: }
19608: }
1.1394 raeburn 19609: $supppath .= '&'.$items[$i].'::'.$is_hidden.':::';
19610: } else {
19611: $supppath .= '&'.$items[$i];
1.1393 raeburn 19612: }
19613: } elsif ((!$odd) && ($items[$i] !~ /^(default|supplemental)(|_\d+)$/)) {
19614: $badpath = 1;
1.1394 raeburn 19615: } elsif ($supplementalflag) {
1.1393 raeburn 19616: $supppath .= '&'.$items[$i];
19617: }
19618: last if ($badpath);
19619: }
19620: if ($badpath) {
19621: delete($env{'form.folderpath'});
1.1394 raeburn 19622: } elsif ($changed && $supplementalflag) {
1.1393 raeburn 19623: $supppath =~ s/^\&//;
19624: $env{'form.folderpath'} = $supppath;
19625: }
19626: }
19627: return;
19628: }
19629:
1.1094 raeburn 19630: sub captcha_display {
1.1327 raeburn 19631: my ($context,$lonhost,$defdom) = @_;
1.1094 raeburn 19632: my ($output,$error);
1.1234 raeburn 19633: my ($captcha,$pubkey,$privkey,$version) =
1.1327 raeburn 19634: &get_captcha_config($context,$lonhost,$defdom);
1.1095 raeburn 19635: if ($captcha eq 'original') {
1.1094 raeburn 19636: $output = &create_captcha();
19637: unless ($output) {
1.1172 raeburn 19638: $error = 'captcha';
1.1094 raeburn 19639: }
19640: } elsif ($captcha eq 'recaptcha') {
1.1234 raeburn 19641: $output = &create_recaptcha($pubkey,$version);
1.1094 raeburn 19642: unless ($output) {
1.1172 raeburn 19643: $error = 'recaptcha';
1.1094 raeburn 19644: }
19645: }
1.1234 raeburn 19646: return ($output,$error,$captcha,$version);
1.1094 raeburn 19647: }
19648:
19649: sub captcha_response {
1.1327 raeburn 19650: my ($context,$lonhost,$defdom) = @_;
1.1094 raeburn 19651: my ($captcha_chk,$captcha_error);
1.1327 raeburn 19652: my ($captcha,$pubkey,$privkey,$version) = &get_captcha_config($context,$lonhost,$defdom);
1.1095 raeburn 19653: if ($captcha eq 'original') {
1.1094 raeburn 19654: ($captcha_chk,$captcha_error) = &check_captcha();
19655: } elsif ($captcha eq 'recaptcha') {
1.1234 raeburn 19656: $captcha_chk = &check_recaptcha($privkey,$version);
1.1094 raeburn 19657: } else {
19658: $captcha_chk = 1;
19659: }
19660: return ($captcha_chk,$captcha_error);
19661: }
19662:
19663: sub get_captcha_config {
1.1327 raeburn 19664: my ($context,$lonhost,$dom_in_effect) = @_;
1.1234 raeburn 19665: my ($captcha,$pubkey,$privkey,$version,$hashtocheck);
1.1094 raeburn 19666: my $hostname = &Apache::lonnet::hostname($lonhost);
19667: my $serverhomeID = &Apache::lonnet::get_server_homeID($hostname);
19668: my $serverhomedom = &Apache::lonnet::host_domain($serverhomeID);
1.1095 raeburn 19669: if ($context eq 'usercreation') {
19670: my %domconfig = &Apache::lonnet::get_dom('configuration',[$context],$serverhomedom);
19671: if (ref($domconfig{$context}) eq 'HASH') {
19672: $hashtocheck = $domconfig{$context}{'cancreate'};
19673: if (ref($hashtocheck) eq 'HASH') {
19674: if ($hashtocheck->{'captcha'} eq 'recaptcha') {
19675: if (ref($hashtocheck->{'recaptchakeys'}) eq 'HASH') {
19676: $pubkey = $hashtocheck->{'recaptchakeys'}{'public'};
19677: $privkey = $hashtocheck->{'recaptchakeys'}{'private'};
19678: }
19679: if ($privkey && $pubkey) {
19680: $captcha = 'recaptcha';
1.1234 raeburn 19681: $version = $hashtocheck->{'recaptchaversion'};
19682: if ($version ne '2') {
19683: $version = 1;
19684: }
1.1095 raeburn 19685: } else {
19686: $captcha = 'original';
19687: }
19688: } elsif ($hashtocheck->{'captcha'} ne 'notused') {
19689: $captcha = 'original';
19690: }
1.1094 raeburn 19691: }
1.1095 raeburn 19692: } else {
19693: $captcha = 'captcha';
19694: }
19695: } elsif ($context eq 'login') {
19696: my %domconfhash = &Apache::loncommon::get_domainconf($serverhomedom);
19697: if ($domconfhash{$serverhomedom.'.login.captcha'} eq 'recaptcha') {
19698: $pubkey = $domconfhash{$serverhomedom.'.login.recaptchakeys_public'};
19699: $privkey = $domconfhash{$serverhomedom.'.login.recaptchakeys_private'};
1.1094 raeburn 19700: if ($privkey && $pubkey) {
19701: $captcha = 'recaptcha';
1.1234 raeburn 19702: $version = $domconfhash{$serverhomedom.'.login.recaptchaversion'};
19703: if ($version ne '2') {
19704: $version = 1;
19705: }
1.1095 raeburn 19706: } else {
19707: $captcha = 'original';
1.1094 raeburn 19708: }
1.1095 raeburn 19709: } elsif ($domconfhash{$serverhomedom.'.login.captcha'} eq 'original') {
19710: $captcha = 'original';
1.1094 raeburn 19711: }
1.1327 raeburn 19712: } elsif ($context eq 'passwords') {
19713: if ($dom_in_effect) {
19714: my %passwdconf = &Apache::lonnet::get_passwdconf($dom_in_effect);
19715: if ($passwdconf{'captcha'} eq 'recaptcha') {
19716: if (ref($passwdconf{'recaptchakeys'}) eq 'HASH') {
19717: $pubkey = $passwdconf{'recaptchakeys'}{'public'};
19718: $privkey = $passwdconf{'recaptchakeys'}{'private'};
19719: }
19720: if ($privkey && $pubkey) {
19721: $captcha = 'recaptcha';
19722: $version = $passwdconf{'recaptchaversion'};
19723: if ($version ne '2') {
19724: $version = 1;
19725: }
19726: } else {
19727: $captcha = 'original';
19728: }
19729: } elsif ($passwdconf{'captcha'} ne 'notused') {
19730: $captcha = 'original';
19731: }
19732: }
19733: }
1.1234 raeburn 19734: return ($captcha,$pubkey,$privkey,$version);
1.1094 raeburn 19735: }
19736:
19737: sub create_captcha {
19738: my %captcha_params = &captcha_settings();
19739: my ($output,$maxtries,$tries) = ('',10,0);
19740: while ($tries < $maxtries) {
19741: $tries ++;
19742: my $captcha = Authen::Captcha->new (
19743: output_folder => $captcha_params{'output_dir'},
19744: data_folder => $captcha_params{'db_dir'},
19745: );
19746: my $md5sum = $captcha->generate_code($captcha_params{'numchars'});
19747:
19748: if (-e $Apache::lonnet::perlvar{'lonCaptchaDir'}.'/'.$md5sum.'.png') {
19749: $output = '<input type="hidden" name="crypt" value="'.$md5sum.'" />'."\n".
1.1367 raeburn 19750: '<span class="LC_nobreak">'.
1.1453 raeburn 19751: '<label>'.&mt('Type in the letters/numbers shown below').' '.
1.1463 raeburn 19752: '<input type="text" size="5" name="code" value="" autocomplete="new-password" aria-required="true" />'.
1.1453 raeburn 19753: '</label></span><br />'.
1.1176 raeburn 19754: '<img src="'.$captcha_params{'www_output_dir'}.'/'.$md5sum.'.png" alt="captcha" />';
1.1094 raeburn 19755: last;
19756: }
19757: }
1.1323 raeburn 19758: if ($output eq '') {
19759: &Apache::lonnet::logthis("Failed to create Captcha code after $tries attempts.");
19760: }
1.1094 raeburn 19761: return $output;
19762: }
19763:
19764: sub captcha_settings {
19765: my %captcha_params = (
19766: output_dir => $Apache::lonnet::perlvar{'lonCaptchaDir'},
19767: www_output_dir => "/captchaspool",
19768: db_dir => $Apache::lonnet::perlvar{'lonCaptchaDb'},
19769: numchars => '5',
19770: );
19771: return %captcha_params;
19772: }
19773:
19774: sub check_captcha {
19775: my ($captcha_chk,$captcha_error);
19776: my $code = $env{'form.code'};
19777: my $md5sum = $env{'form.crypt'};
19778: my %captcha_params = &captcha_settings();
19779: my $captcha = Authen::Captcha->new(
19780: output_folder => $captcha_params{'output_dir'},
19781: data_folder => $captcha_params{'db_dir'},
19782: );
1.1109 raeburn 19783: $captcha_chk = $captcha->check_code($code,$md5sum);
1.1094 raeburn 19784: my %captcha_hash = (
19785: 0 => 'Code not checked (file error)',
19786: -1 => 'Failed: code expired',
19787: -2 => 'Failed: invalid code (not in database)',
19788: -3 => 'Failed: invalid code (code does not match crypt)',
19789: );
19790: if ($captcha_chk != 1) {
19791: $captcha_error = $captcha_hash{$captcha_chk}
19792: }
19793: return ($captcha_chk,$captcha_error);
19794: }
19795:
19796: sub create_recaptcha {
1.1234 raeburn 19797: my ($pubkey,$version) = @_;
19798: if ($version >= 2) {
1.1367 raeburn 19799: return '<div class="g-recaptcha" data-sitekey="'.$pubkey.'"></div>'.
19800: '<div style="padding:0;clear:both;margin:0;border:0"></div>';
1.1234 raeburn 19801: } else {
19802: my $use_ssl;
19803: if ($ENV{'SERVER_PORT'} == 443) {
19804: $use_ssl = 1;
19805: }
19806: my $captcha = Captcha::reCAPTCHA->new;
19807: return $captcha->get_options_setter({theme => 'white'})."\n".
19808: $captcha->get_html($pubkey,undef,$use_ssl).
19809: &mt('If the text is hard to read, [_1] will replace them.',
19810: '<img src="/res/adm/pages/refresh.gif" alt="reCAPTCHA refresh" />').
19811: '<br /><br />';
19812: }
1.1094 raeburn 19813: }
19814:
19815: sub check_recaptcha {
1.1234 raeburn 19816: my ($privkey,$version) = @_;
1.1094 raeburn 19817: my $captcha_chk;
1.1350 raeburn 19818: my $ip = &Apache::lonnet::get_requestor_ip();
1.1234 raeburn 19819: if ($version >= 2) {
19820: my %info = (
19821: secret => $privkey,
19822: response => $env{'form.g-recaptcha-response'},
1.1350 raeburn 19823: remoteip => $ip,
1.1234 raeburn 19824: );
1.1280 raeburn 19825: my $request=new HTTP::Request('POST','https://www.google.com/recaptcha/api/siteverify');
19826: $request->content(join('&',map {
19827: my $name = escape($_);
19828: "$name=" . ( ref($info{$_}) eq 'ARRAY'
19829: ? join("&$name=", map {escape($_) } @{$info{$_}})
19830: : &escape($info{$_}) );
19831: } keys(%info)));
19832: my $response = &LONCAPA::LWPReq::makerequest('',$request,'','',10,1);
1.1234 raeburn 19833: if ($response->is_success) {
19834: my $data = JSON::DWIW->from_json($response->decoded_content);
19835: if (ref($data) eq 'HASH') {
19836: if ($data->{'success'}) {
19837: $captcha_chk = 1;
19838: }
19839: }
19840: }
19841: } else {
19842: my $captcha = Captcha::reCAPTCHA->new;
19843: my $captcha_result =
19844: $captcha->check_answer(
19845: $privkey,
1.1350 raeburn 19846: $ip,
1.1234 raeburn 19847: $env{'form.recaptcha_challenge_field'},
19848: $env{'form.recaptcha_response_field'},
19849: );
19850: if ($captcha_result->{is_valid}) {
19851: $captcha_chk = 1;
19852: }
1.1094 raeburn 19853: }
19854: return $captcha_chk;
19855: }
19856:
1.1174 raeburn 19857: sub emailusername_info {
1.1244 raeburn 19858: my @fields = ('firstname','lastname','institution','web','location','officialemail','id');
1.1174 raeburn 19859: my %titles = &Apache::lonlocal::texthash (
19860: lastname => 'Last Name',
19861: firstname => 'First Name',
19862: institution => 'School/college/university',
19863: location => "School's city, state/province, country",
19864: web => "School's web address",
19865: officialemail => 'E-mail address at institution (if different)',
1.1244 raeburn 19866: id => 'Student/Employee ID',
1.1174 raeburn 19867: );
19868: return (\@fields,\%titles);
19869: }
19870:
1.1161 raeburn 19871: sub cleanup_html {
19872: my ($incoming) = @_;
19873: my $outgoing;
19874: if ($incoming ne '') {
19875: $outgoing = $incoming;
19876: $outgoing =~ s/;/;/g;
19877: $outgoing =~ s/\#/#/g;
19878: $outgoing =~ s/\&/&/g;
19879: $outgoing =~ s/</</g;
19880: $outgoing =~ s/>/>/g;
19881: $outgoing =~ s/\(/(/g;
19882: $outgoing =~ s/\)/)/g;
19883: $outgoing =~ s/"/"/g;
19884: $outgoing =~ s/'/'/g;
19885: $outgoing =~ s/\$/$/g;
19886: $outgoing =~ s{/}{/}g;
19887: $outgoing =~ s/=/=/g;
19888: $outgoing =~ s/\\/\/g
19889: }
19890: return $outgoing;
19891: }
19892:
1.1190 musolffc 19893: # Checks for critical messages and returns a redirect url if one exists.
19894: # $interval indicates how often to check for messages.
1.1282 raeburn 19895: # $context is the calling context -- roles, grades, contents, menu or flip.
1.1190 musolffc 19896: sub critical_redirect {
1.1282 raeburn 19897: my ($interval,$context) = @_;
1.1356 raeburn 19898: unless (($env{'user.domain'} ne '') && ($env{'user.name'} ne '')) {
19899: return ();
19900: }
1.1190 musolffc 19901: if ((time-$env{'user.criticalcheck.time'})>$interval) {
1.1282 raeburn 19902: if (($env{'request.course.id'}) && (($context eq 'flip') || ($context eq 'contents'))) {
19903: my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
19904: my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
1.1372 raeburn 19905: my $blocked = &blocking_status('alert',undef,$cnum,$cdom,undef,1);
1.1282 raeburn 19906: if ($blocked) {
19907: my $checkrole = "cm./$cdom/$cnum";
19908: if ($env{'request.course.sec'} ne '') {
19909: $checkrole .= "/$env{'request.course.sec'}";
19910: }
19911: unless ((&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) &&
19912: ($env{'request.role'} !~ m{^st\./$cdom/$cnum})) {
19913: return;
19914: }
19915: }
19916: }
1.1190 musolffc 19917: my @what=&Apache::lonnet::dump('critical', $env{'user.domain'},
19918: $env{'user.name'});
19919: &Apache::lonnet::appenv({'user.criticalcheck.time'=>time});
1.1191 raeburn 19920: my $redirecturl;
1.1190 musolffc 19921: if ($what[0]) {
1.1356 raeburn 19922: if (($what[0] ne 'con_lost') && ($what[0] ne 'no_such_host') && ($what[0]!~/^error\:/)) {
1.1190 musolffc 19923: $redirecturl='/adm/email?critical=display';
1.1191 raeburn 19924: my $url=&Apache::lonnet::absolute_url().$redirecturl;
19925: return (1, $url);
1.1190 musolffc 19926: }
1.1191 raeburn 19927: }
19928: }
19929: return ();
1.1190 musolffc 19930: }
19931:
1.1174 raeburn 19932: # Use:
19933: # my $answer=reply("encrypt:passwd:$udom:$uname:$upass",$tryserver);
19934: #
19935: ##################################################
19936: # password associated functions #
19937: ##################################################
19938: sub des_keys {
19939: # Make a new key for DES encryption.
19940: # Each key has two parts which are returned separately.
19941: # Please note: Each key must be passed through the &hex function
19942: # before it is output to the web browser. The hex versions cannot
19943: # be used to decrypt.
19944: my @hexstr=('0','1','2','3','4','5','6','7',
19945: '8','9','a','b','c','d','e','f');
19946: my $lkey='';
19947: for (0..7) {
19948: $lkey.=$hexstr[rand(15)];
19949: }
19950: my $ukey='';
19951: for (0..7) {
19952: $ukey.=$hexstr[rand(15)];
19953: }
19954: return ($lkey,$ukey);
19955: }
19956:
19957: sub des_decrypt {
19958: my ($key,$cyphertext) = @_;
19959: my $keybin=pack("H16",$key);
19960: my $cypher;
19961: if ($Crypt::DES::VERSION>=2.03) {
19962: $cypher=new Crypt::DES $keybin;
19963: } else {
19964: $cypher=new DES $keybin;
19965: }
1.1233 raeburn 19966: my $plaintext='';
19967: my $cypherlength = length($cyphertext);
19968: my $numchunks = int($cypherlength/32);
19969: for (my $j=0; $j<$numchunks; $j++) {
19970: my $start = $j*32;
19971: my $cypherblock = substr($cyphertext,$start,32);
19972: my $chunk =
19973: $cypher->decrypt(unpack("a8",pack("H16",substr($cypherblock,0,16))));
19974: $chunk .=
19975: $cypher->decrypt(unpack("a8",pack("H16",substr($cypherblock,16,16))));
19976: $chunk=substr($chunk,1,ord(substr($chunk,0,1)) );
19977: $plaintext .= $chunk;
19978: }
1.1174 raeburn 19979: return $plaintext;
19980: }
19981:
1.1344 raeburn 19982: sub get_requested_shorturls {
1.1309 raeburn 19983: my ($cdom,$cnum,$navmap) = @_;
19984: return unless (ref($navmap));
1.1344 raeburn 19985: my ($numnew,$errors);
1.1309 raeburn 19986: my @toshorten = &Apache::loncommon::get_env_multiple('form.addtiny');
19987: if (@toshorten) {
19988: my (%maps,%resources,%titles);
19989: &Apache::loncourserespicker::enumerate_course_contents($navmap,\%maps,\%resources,\%titles,
19990: 'shorturls',$cdom,$cnum);
19991: if (keys(%resources)) {
1.1344 raeburn 19992: my %tocreate;
1.1309 raeburn 19993: foreach my $item (sort {$a <=> $b} (@toshorten)) {
19994: my $symb = $resources{$item};
19995: if ($symb) {
19996: $tocreate{$cnum.'&'.$symb} = 1;
19997: }
19998: }
1.1344 raeburn 19999: if (keys(%tocreate)) {
20000: ($numnew,$errors) = &make_short_symbs($cdom,$cnum,
20001: \%tocreate);
20002: }
1.1309 raeburn 20003: }
1.1344 raeburn 20004: }
20005: return ($numnew,$errors);
20006: }
20007:
20008: sub make_short_symbs {
20009: my ($cdom,$cnum,$tocreateref,$lockuser) = @_;
20010: my ($numnew,@errors);
20011: if (ref($tocreateref) eq 'HASH') {
20012: my %tocreate = %{$tocreateref};
1.1309 raeburn 20013: if (keys(%tocreate)) {
20014: my %coursetiny = &Apache::lonnet::dump('tiny',$cdom,$cnum);
20015: my $su = Short::URL->new(no_vowels => 1);
20016: my $init = '';
20017: my (%newunique,%addcourse,%courseonly,%failed);
20018: # get lock on tiny db
20019: my $now = time;
1.1344 raeburn 20020: if ($lockuser eq '') {
20021: $lockuser = $env{'user.name'}.':'.$env{'user.domain'};
20022: }
1.1309 raeburn 20023: my $lockhash = {
1.1344 raeburn 20024: "lock\0$now" => $lockuser,
1.1309 raeburn 20025: };
20026: my $tries = 0;
20027: my $gotlock = &Apache::lonnet::newput_dom('tiny',$lockhash,$cdom);
20028: my ($code,$error);
20029: while (($gotlock ne 'ok') && ($tries<3)) {
20030: $tries ++;
20031: sleep 1;
1.1319 raeburn 20032: $gotlock = &Apache::lonnet::newput_dom('tiny',$lockhash,$cdom);
1.1309 raeburn 20033: }
20034: if ($gotlock eq 'ok') {
20035: $init = &shorten_symbs($cdom,$init,$su,\%coursetiny,\%tocreate,\%newunique,
20036: \%addcourse,\%courseonly,\%failed);
20037: if (keys(%failed)) {
20038: my $numfailed = scalar(keys(%failed));
20039: push(@errors,&mt('error: could not obtain unique six character URL for [quant,_1,resource]',$numfailed));
20040: }
20041: if (keys(%newunique)) {
20042: my $putres = &Apache::lonnet::newput_dom('tiny',\%newunique,$cdom);
20043: if ($putres eq 'ok') {
20044: $numnew = scalar(keys(%newunique));
20045: my $newputres = &Apache::lonnet::newput('tiny',\%addcourse,$cdom,$cnum);
20046: unless ($newputres eq 'ok') {
20047: push(@errors,&mt('error: could not store course look-up of short URLs'));
20048: }
20049: } else {
20050: push(@errors,&mt('error: could not store unique six character URLs'));
20051: }
20052: }
20053: my $dellockres = &Apache::lonnet::del_dom('tiny',["lock\0$now"],$cdom);
20054: unless ($dellockres eq 'ok') {
20055: push(@errors,&mt('error: could not release lockfile'));
20056: }
20057: } else {
20058: push(@errors,&mt('error: could not obtain lockfile'));
20059: }
20060: if (keys(%courseonly)) {
20061: my $result = &Apache::lonnet::newput('tiny',\%courseonly,$cdom,$cnum);
20062: if ($result ne 'ok') {
20063: push(@errors,&mt('error: could not update course look-up of short URLs'));
20064: }
20065: }
20066: }
20067: }
20068: return ($numnew,\@errors);
20069: }
20070:
20071: sub shorten_symbs {
20072: my ($cdom,$init,$su,$coursetiny,$tocreate,$newunique,$addcourse,$courseonly,$failed) = @_;
20073: return unless ((ref($su)) && (ref($coursetiny) eq 'HASH') && (ref($tocreate) eq 'HASH') &&
20074: (ref($newunique) eq 'HASH') && (ref($addcourse) eq 'HASH') &&
20075: (ref($courseonly) eq 'HASH') && (ref($failed) eq 'HASH'));
20076: my (%possibles,%collisions);
20077: foreach my $key (keys(%{$tocreate})) {
20078: my $num = String::CRC32::crc32($key);
20079: my $tiny = $su->encode($num,$init);
20080: if ($tiny) {
20081: $possibles{$tiny} = $key;
20082: }
20083: }
20084: if (!$init) {
20085: $init = 1;
20086: } else {
20087: $init ++;
20088: }
20089: if (keys(%possibles)) {
20090: my @posstiny = keys(%possibles);
20091: my $configuname = &Apache::lonnet::get_domainconfiguser($cdom);
20092: my %currtiny = &Apache::lonnet::get('tiny',\@posstiny,$cdom,$configuname);
20093: if (keys(%currtiny)) {
20094: foreach my $key (keys(%currtiny)) {
20095: next if ($currtiny{$key} eq '');
20096: if ($currtiny{$key} eq $possibles{$key}) {
20097: my ($tcnum,$tsymb) = split(/\&/,$currtiny{$key});
20098: unless (($coursetiny->{$tsymb} eq $key) || ($addcourse->{$tsymb} eq $key) || ($courseonly->{$tsymb} eq $key)) {
20099: $courseonly->{$tsymb} = $key;
20100: }
20101: } else {
20102: $collisions{$possibles{$key}} = 1;
20103: }
20104: delete($possibles{$key});
20105: }
20106: }
20107: foreach my $key (keys(%possibles)) {
20108: $newunique->{$key} = $possibles{$key};
20109: my ($tcnum,$tsymb) = split(/\&/,$possibles{$key});
20110: unless (($coursetiny->{$tsymb} eq $key) || ($addcourse->{$tsymb} eq $key) || ($courseonly->{$tsymb} eq $key)) {
20111: $addcourse->{$tsymb} = $key;
20112: }
20113: }
20114: }
20115: if (keys(%collisions)) {
20116: if ($init <5) {
20117: if (!$init) {
20118: $init = 1;
20119: } else {
20120: $init ++;
20121: }
20122: $init = &shorten_symbs($cdom,$init,$su,$coursetiny,\%collisions,
20123: $newunique,$addcourse,$courseonly,$failed);
20124: } else {
20125: foreach my $key (keys(%collisions)) {
20126: $failed->{$key} = 1;
20127: }
20128: }
20129: }
20130: return $init;
20131: }
20132:
1.1328 raeburn 20133: sub is_nonframeable {
1.1329 raeburn 20134: my ($url,$absolute,$hostname,$ip,$nocache) = @_;
20135: my ($remprotocol,$remhost) = ($url =~ m{^(https?)\://(([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,})}i);
1.1330 raeburn 20136: return if (($remprotocol eq '') || ($remhost eq ''));
1.1329 raeburn 20137:
20138: $remprotocol = lc($remprotocol);
20139: $remhost = lc($remhost);
20140: my $remport = 80;
20141: if ($remprotocol eq 'https') {
20142: $remport = 443;
20143: }
1.1330 raeburn 20144: my ($result,$cached) = &Apache::lonnet::is_cached_new('noiframe',$remhost.':'.$remport);
1.1329 raeburn 20145: if ($cached) {
20146: unless ($nocache) {
20147: if ($result) {
20148: return 1;
20149: } else {
20150: return 0;
20151: }
20152: }
20153: }
1.1328 raeburn 20154: my $uselink;
20155: my $request = new HTTP::Request('HEAD',$url);
20156: my $response = &LONCAPA::LWPReq::makerequest('',$request,'','',5);
20157: if ($response->is_success()) {
20158: my $secpolicy = lc($response->header('content-security-policy'));
20159: my $xframeop = lc($response->header('x-frame-options'));
20160: $secpolicy =~ s/^\s+|\s+$//g;
20161: $xframeop =~ s/^\s+|\s+$//g;
20162: if (($secpolicy ne '') || ($xframeop ne '')) {
1.1329 raeburn 20163: my $remotehost = $remprotocol.'://'.$remhost;
1.1328 raeburn 20164: my ($origin,$protocol,$port);
20165: if ($ENV{'SERVER_PORT'} =~/^\d+$/) {
20166: $port = $ENV{'SERVER_PORT'};
20167: } else {
20168: $port = 80;
20169: }
20170: if ($absolute eq '') {
20171: $protocol = 'http:';
20172: if ($port == 443) {
20173: $protocol = 'https:';
20174: }
20175: $origin = $protocol.'//'.lc($hostname);
20176: } else {
20177: $origin = lc($absolute);
20178: ($protocol,$hostname) = ($absolute =~ m{^(https?:)//([^/]+)$});
20179: }
20180: if (($secpolicy) && ($secpolicy =~ /\Qframe-ancestors\E([^;]*)(;|$)/)) {
20181: my $framepolicy = $1;
20182: $framepolicy =~ s/^\s+|\s+$//g;
20183: my @policies = split(/\s+/,$framepolicy);
20184: if (@policies) {
20185: if (grep(/^\Q'none'\E$/,@policies)) {
20186: $uselink = 1;
20187: } else {
20188: $uselink = 1;
20189: if ((grep(/^\Q*\E$/,@policies)) || (grep(/^\Q$protocol\E$/,@policies)) ||
20190: (($origin ne '') && (grep(/^\Q$origin\E$/,@policies))) ||
20191: (($ip ne '') && (grep(/^\Q$ip\E$/,@policies)))) {
20192: undef($uselink);
20193: }
20194: if ($uselink) {
20195: if (grep(/^\Q'self'\E$/,@policies)) {
20196: if (($origin ne '') && ($remotehost eq $origin)) {
20197: undef($uselink);
20198: }
20199: }
20200: }
20201: if ($uselink) {
20202: my @possok;
20203: if ($ip ne '') {
20204: push(@possok,$ip);
20205: }
20206: my $hoststr = '';
20207: foreach my $part (reverse(split(/\./,$hostname))) {
20208: if ($hoststr eq '') {
20209: $hoststr = $part;
20210: } else {
20211: $hoststr = "$part.$hoststr";
20212: }
20213: if ($hoststr eq $hostname) {
20214: push(@possok,$hostname);
20215: } else {
20216: push(@possok,"*.$hoststr");
20217: }
20218: }
20219: if (@possok) {
20220: foreach my $poss (@possok) {
20221: last if (!$uselink);
20222: foreach my $policy (@policies) {
20223: if ($policy =~ m{^(\Q$protocol\E//|)\Q$poss\E(\Q:$port\E|)$}) {
20224: undef($uselink);
20225: last;
20226: }
20227: }
20228: }
20229: }
20230: }
20231: }
20232: }
20233: } elsif ($xframeop ne '') {
20234: $uselink = 1;
20235: my @policies = split(/\s*,\s*/,$xframeop);
20236: if (@policies) {
20237: unless (grep(/^deny$/,@policies)) {
20238: if ($origin ne '') {
20239: if (grep(/^sameorigin$/,@policies)) {
20240: if ($remotehost eq $origin) {
20241: undef($uselink);
20242: }
20243: }
20244: if ($uselink) {
20245: foreach my $policy (@policies) {
20246: if ($policy =~ /^allow-from\s*(.+)$/) {
20247: my $allowfrom = $1;
20248: if (($allowfrom ne '') && ($allowfrom eq $origin)) {
20249: undef($uselink);
20250: last;
20251: }
20252: }
20253: }
20254: }
20255: }
20256: }
20257: }
20258: }
20259: }
20260: }
1.1329 raeburn 20261: if ($nocache) {
20262: if ($cached) {
20263: my $devalidate;
20264: if ($uselink && !$result) {
20265: $devalidate = 1;
20266: } elsif (!$uselink && $result) {
20267: $devalidate = 1;
20268: }
20269: if ($devalidate) {
20270: &Apache::lonnet::devalidate_cache_new('noiframe',$remhost.':'.$remport);
20271: }
20272: }
20273: } else {
20274: if ($uselink) {
20275: $result = 1;
20276: } else {
20277: $result = 0;
20278: }
20279: &Apache::lonnet::do_cache_new('noiframe',$remhost.':'.$remport,$result,3600);
20280: }
1.1328 raeburn 20281: return $uselink;
20282: }
20283:
1.1359 raeburn 20284: sub page_menu {
20285: my ($menucolls,$menunum) = @_;
20286: my %menu;
20287: foreach my $item (split(/;/,$menucolls)) {
20288: my ($num,$value) = split(/\%/,$item);
20289: if ($num eq $menunum) {
20290: my @entries = split(/\&/,$value);
20291: foreach my $entry (@entries) {
20292: my ($name,$fields) = split(/=/,$entry);
1.1368 raeburn 20293: if (($name eq 'top') || ($name eq 'inline') || ($name eq 'foot') || ($name eq 'main')) {
1.1359 raeburn 20294: $menu{$name} = $fields;
20295: } else {
20296: my @shown;
20297: if ($fields =~ /,/) {
20298: @shown = split(/,/,$fields);
20299: } else {
20300: @shown = ($fields);
20301: }
20302: if (@shown) {
20303: foreach my $field (@shown) {
20304: next if ($field eq '');
20305: $menu{$field} = 1;
20306: }
20307: }
20308: }
20309: }
20310: }
20311: }
20312: return %menu;
20313: }
20314:
1.112 bowersj2 20315: 1;
20316: __END__;
1.41 ng 20317:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>