Annotation of loncom/interface/loncommon.pm, revision 1.1330
1.10 albertel 1: # The LearningOnline Network with CAPA
1.1 albertel 2: # a pile of common routines
1.10 albertel 3: #
1.1330 ! raeburn 4: # $Id: loncommon.pm,v 1.1329 2019/05/02 23:09:38 raeburn Exp $
1.10 albertel 5: #
6: # Copyright Michigan State University Board of Trustees
7: #
8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
9: #
10: # LON-CAPA is free software; you can redistribute it and/or modify
11: # it under the terms of the GNU General Public License as published by
12: # the Free Software Foundation; either version 2 of the License, or
13: # (at your option) any later version.
14: #
15: # LON-CAPA is distributed in the hope that it will be useful,
16: # but WITHOUT ANY WARRANTY; without even the implied warranty of
17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18: # GNU General Public License for more details.
19: #
20: # You should have received a copy of the GNU General Public License
21: # along with LON-CAPA; if not, write to the Free Software
22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23: #
24: # /home/httpd/html/adm/gpl.txt
25: #
26: # http://www.lon-capa.org/
27: #
1.1 albertel 28:
29: # Makes a table out of the previous attempts
1.2 albertel 30: # Inputs result_from_symbread, user, domain, course_id
1.16 harris41 31: # Reads in non-network-related .tab files
1.1 albertel 32:
1.35 matthew 33: # POD header:
34:
1.45 matthew 35: =pod
36:
1.35 matthew 37: =head1 NAME
38:
39: Apache::loncommon - pile of common routines
40:
41: =head1 SYNOPSIS
42:
1.112 bowersj2 43: Common routines for manipulating connections, student answers,
44: domains, common Javascript fragments, etc.
1.35 matthew 45:
1.112 bowersj2 46: =head1 OVERVIEW
1.35 matthew 47:
1.112 bowersj2 48: A collection of commonly used subroutines that don't have a natural
49: home anywhere else. This collection helps remove
1.35 matthew 50: redundancy from other modules and increase efficiency of memory usage.
51:
52: =cut
53:
54: # End of POD header
1.1 albertel 55: package Apache::loncommon;
56:
57: use strict;
1.258 albertel 58: use Apache::lonnet;
1.46 matthew 59: use GDBM_File;
1.51 www 60: use POSIX qw(strftime mktime);
1.82 www 61: use Apache::lonmenu();
1.498 albertel 62: use Apache::lonenc();
1.117 www 63: use Apache::lonlocal;
1.685 tempelho 64: use Apache::lonnet();
1.139 matthew 65: use HTML::Entities;
1.334 albertel 66: use Apache::lonhtmlcommon();
67: use Apache::loncoursedata();
1.344 albertel 68: use Apache::lontexconvert();
1.444 albertel 69: use Apache::lonclonecourse();
1.1108 raeburn 70: use Apache::lonuserutils();
1.1110 raeburn 71: use Apache::lonuserstate();
1.1182 raeburn 72: use Apache::courseclassifier();
1.479 albertel 73: use LONCAPA qw(:DEFAULT :match);
1.1280 raeburn 74: use LONCAPA::LWPReq;
1.1328 raeburn 75: use HTTP::Request;
1.657 raeburn 76: use DateTime::TimeZone;
1.1241 raeburn 77: use DateTime::Locale;
1.1220 raeburn 78: use Encode();
1.1091 foxr 79: use Text::Aspell;
1.1094 raeburn 80: use Authen::Captcha;
81: use Captcha::reCAPTCHA;
1.1234 raeburn 82: use JSON::DWIW;
83: use LWP::UserAgent;
1.1174 raeburn 84: use Crypt::DES;
85: use DynaLoader; # for Crypt::DES version
1.1223 musolffc 86: use MIME::Lite;
87: use MIME::Types;
1.1292 raeburn 88: use File::Copy();
1.1300 raeburn 89: use File::Path();
1.1309 raeburn 90: use String::CRC32();
91: use Short::URL();
1.117 www 92:
1.517 raeburn 93: # ---------------------------------------------- Designs
94: use vars qw(%defaultdesign);
95:
1.22 www 96: my $readit;
97:
1.517 raeburn 98:
1.157 matthew 99: ##
100: ## Global Variables
101: ##
1.46 matthew 102:
1.643 foxr 103:
104: # ----------------------------------------------- SSI with retries:
105: #
106:
107: =pod
108:
1.648 raeburn 109: =head1 Server Side include with retries:
1.643 foxr 110:
111: =over 4
112:
1.648 raeburn 113: =item * &ssi_with_retries(resource,retries form)
1.643 foxr 114:
115: Performs an ssi with some number of retries. Retries continue either
116: until the result is ok or until the retry count supplied by the
117: caller is exhausted.
118:
119: Inputs:
1.648 raeburn 120:
121: =over 4
122:
1.643 foxr 123: resource - Identifies the resource to insert.
1.648 raeburn 124:
1.643 foxr 125: retries - Count of the number of retries allowed.
1.648 raeburn 126:
1.643 foxr 127: form - Hash that identifies the rendering options.
128:
1.648 raeburn 129: =back
130:
131: Returns:
132:
133: =over 4
134:
1.643 foxr 135: content - The content of the response. If retries were exhausted this is empty.
1.648 raeburn 136:
1.643 foxr 137: response - The response from the last attempt (which may or may not have been successful.
138:
1.648 raeburn 139: =back
140:
141: =back
142:
1.643 foxr 143: =cut
144:
145: sub ssi_with_retries {
146: my ($resource, $retries, %form) = @_;
147:
148:
149: my $ok = 0; # True if we got a good response.
150: my $content;
151: my $response;
152:
153: # Try to get the ssi done. within the retries count:
154:
155: do {
156: ($content, $response) = &Apache::lonnet::ssi($resource, %form);
157: $ok = $response->is_success;
1.650 www 158: if (!$ok) {
159: &Apache::lonnet::logthis("Failed ssi_with_retries on $resource: ".$response->is_success.', '.$response->code.', '.$response->message);
160: }
1.643 foxr 161: $retries--;
162: } while (!$ok && ($retries > 0));
163:
164: if (!$ok) {
165: $content = ''; # On error return an empty content.
166: }
167: return ($content, $response);
168:
169: }
170:
171:
172:
1.20 www 173: # ----------------------------------------------- Filetypes/Languages/Copyright
1.12 harris41 174: my %language;
1.124 www 175: my %supported_language;
1.1088 foxr 176: my %supported_codes;
1.1048 foxr 177: my %latex_language; # For choosing hyphenation in <transl..>
178: my %latex_language_bykey; # for choosing hyphenation from metadata
1.12 harris41 179: my %cprtag;
1.192 taceyjo1 180: my %scprtag;
1.351 www 181: my %fe; my %fd; my %fm;
1.41 ng 182: my %category_extensions;
1.12 harris41 183:
1.46 matthew 184: # ---------------------------------------------- Thesaurus variables
1.144 matthew 185: #
186: # %Keywords:
187: # A hash used by &keyword to determine if a word is considered a keyword.
188: # $thesaurus_db_file
189: # Scalar containing the full path to the thesaurus database.
1.46 matthew 190:
191: my %Keywords;
192: my $thesaurus_db_file;
193:
1.144 matthew 194: #
195: # Initialize values from language.tab, copyright.tab, filetypes.tab,
196: # thesaurus.tab, and filecategories.tab.
197: #
1.18 www 198: BEGIN {
1.46 matthew 199: # Variable initialization
200: $thesaurus_db_file = $Apache::lonnet::perlvar{'lonTabDir'}."/thesaurus.db";
201: #
1.22 www 202: unless ($readit) {
1.12 harris41 203: # ------------------------------------------------------------------- languages
204: {
1.158 raeburn 205: my $langtabfile = $Apache::lonnet::perlvar{'lonTabDir'}.
206: '/language.tab';
1.1317 raeburn 207: if ( open(my $fh,'<',$langtabfile) ) {
1.356 albertel 208: while (my $line = <$fh>) {
209: next if ($line=~/^\#/);
210: chomp($line);
1.1088 foxr 211: my ($key,$code,$country,$three,$enc,$val,$sup,$latex)=(split(/\t/,$line));
1.158 raeburn 212: $language{$key}=$val.' - '.$enc;
213: if ($sup) {
214: $supported_language{$key}=$sup;
1.1088 foxr 215: $supported_codes{$key} = $code;
1.158 raeburn 216: }
1.1048 foxr 217: if ($latex) {
218: $latex_language_bykey{$key} = $latex;
1.1088 foxr 219: $latex_language{$code} = $latex;
1.1048 foxr 220: }
1.158 raeburn 221: }
222: close($fh);
223: }
1.12 harris41 224: }
225: # ------------------------------------------------------------------ copyrights
226: {
1.158 raeburn 227: my $copyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}.
228: '/copyright.tab';
1.1317 raeburn 229: if ( open (my $fh,'<',$copyrightfile) ) {
1.356 albertel 230: while (my $line = <$fh>) {
231: next if ($line=~/^\#/);
232: chomp($line);
233: my ($key,$val)=(split(/\s+/,$line,2));
1.158 raeburn 234: $cprtag{$key}=$val;
235: }
236: close($fh);
237: }
1.12 harris41 238: }
1.351 www 239: # ----------------------------------------------------------- source copyrights
1.192 taceyjo1 240: {
241: my $sourcecopyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}.
242: '/source_copyright.tab';
1.1317 raeburn 243: if ( open (my $fh,'<',$sourcecopyrightfile) ) {
1.356 albertel 244: while (my $line = <$fh>) {
245: next if ($line =~ /^\#/);
246: chomp($line);
247: my ($key,$val)=(split(/\s+/,$line,2));
1.192 taceyjo1 248: $scprtag{$key}=$val;
249: }
250: close($fh);
251: }
252: }
1.63 www 253:
1.517 raeburn 254: # -------------------------------------------------------------- default domain designs
1.63 www 255: my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors';
1.517 raeburn 256: my $designfile = $designdir.'/default.tab';
1.1317 raeburn 257: if ( open (my $fh,'<',$designfile) ) {
1.517 raeburn 258: while (my $line = <$fh>) {
259: next if ($line =~ /^\#/);
260: chomp($line);
261: my ($key,$val)=(split(/\=/,$line));
262: if ($val) { $defaultdesign{$key}=$val; }
263: }
264: close($fh);
1.63 www 265: }
266:
1.15 harris41 267: # ------------------------------------------------------------- file categories
268: {
1.158 raeburn 269: my $categoryfile = $Apache::lonnet::perlvar{'lonTabDir'}.
270: '/filecategories.tab';
1.1317 raeburn 271: if ( open (my $fh,'<',$categoryfile) ) {
1.356 albertel 272: while (my $line = <$fh>) {
273: next if ($line =~ /^\#/);
274: chomp($line);
275: my ($extension,$category)=(split(/\s+/,$line,2));
1.1263 raeburn 276: push(@{$category_extensions{lc($category)}},$extension);
1.158 raeburn 277: }
278: close($fh);
279: }
280:
1.15 harris41 281: }
1.12 harris41 282: # ------------------------------------------------------------------ file types
283: {
1.158 raeburn 284: my $typesfile = $Apache::lonnet::perlvar{'lonTabDir'}.
285: '/filetypes.tab';
1.1317 raeburn 286: if ( open (my $fh,'<',$typesfile) ) {
1.356 albertel 287: while (my $line = <$fh>) {
288: next if ($line =~ /^\#/);
289: chomp($line);
290: my ($ending,$emb,$mime,$descr)=split(/\s+/,$line,4);
1.158 raeburn 291: if ($descr ne '') {
292: $fe{$ending}=lc($emb);
293: $fd{$ending}=$descr;
1.351 www 294: if ($mime ne 'unk') { $fm{$ending}=$mime; }
1.158 raeburn 295: }
296: }
297: close($fh);
298: }
1.12 harris41 299: }
1.22 www 300: &Apache::lonnet::logthis(
1.705 tempelho 301: "<span style='color:yellow;'>INFO: Read file types</span>");
1.22 www 302: $readit=1;
1.46 matthew 303: } # end of unless($readit)
1.32 matthew 304:
305: }
1.112 bowersj2 306:
1.42 matthew 307: ###############################################################
308: ## HTML and Javascript Helper Functions ##
309: ###############################################################
310:
311: =pod
312:
1.112 bowersj2 313: =head1 HTML and Javascript Functions
1.42 matthew 314:
1.112 bowersj2 315: =over 4
316:
1.648 raeburn 317: =item * &browser_and_searcher_javascript()
1.112 bowersj2 318:
319: X<browsing, javascript>X<searching, javascript>Returns a string
320: containing javascript with two functions, C<openbrowser> and
321: C<opensearcher>. Returned string does not contain E<lt>scriptE<gt>
322: tags.
1.42 matthew 323:
1.648 raeburn 324: =item * &openbrowser(formname,elementname,only,omit) [javascript]
1.42 matthew 325:
326: inputs: formname, elementname, only, omit
327:
328: formname and elementname indicate the name of the html form and name of
329: the element that the results of the browsing selection are to be placed in.
330:
331: Specifying 'only' will restrict the browser to displaying only files
1.185 www 332: with the given extension. Can be a comma separated list.
1.42 matthew 333:
334: Specifying 'omit' will restrict the browser to NOT displaying files
1.185 www 335: with the given extension. Can be a comma separated list.
1.42 matthew 336:
1.648 raeburn 337: =item * &opensearcher(formname,elementname) [javascript]
1.42 matthew 338:
339: Inputs: formname, elementname
340:
341: formname and elementname specify the name of the html form and the name
342: of the element the selection from the search results will be placed in.
1.542 raeburn 343:
1.42 matthew 344: =cut
345:
346: sub browser_and_searcher_javascript {
1.199 albertel 347: my ($mode)=@_;
348: if (!defined($mode)) { $mode='edit'; }
1.453 albertel 349: my $resurl=&escape_single(&lastresurl());
1.42 matthew 350: return <<END;
1.219 albertel 351: // <!-- BEGIN LON-CAPA Internal
1.50 matthew 352: var editbrowser = null;
1.135 albertel 353: function openbrowser(formname,elementname,only,omit,titleelement) {
1.170 www 354: var url = '$resurl/?';
1.42 matthew 355: if (editbrowser == null) {
356: url += 'launch=1&';
357: }
358: url += 'catalogmode=interactive&';
1.199 albertel 359: url += 'mode=$mode&';
1.611 albertel 360: url += 'inhibitmenu=yes&';
1.42 matthew 361: url += 'form=' + formname + '&';
362: if (only != null) {
363: url += 'only=' + only + '&';
1.217 albertel 364: } else {
365: url += 'only=&';
366: }
1.42 matthew 367: if (omit != null) {
368: url += 'omit=' + omit + '&';
1.217 albertel 369: } else {
370: url += 'omit=&';
371: }
1.135 albertel 372: if (titleelement != null) {
373: url += 'titleelement=' + titleelement + '&';
1.217 albertel 374: } else {
375: url += 'titleelement=&';
376: }
1.42 matthew 377: url += 'element=' + elementname + '';
378: var title = 'Browser';
1.435 albertel 379: var options = 'scrollbars=1,resizable=1,menubar=0,toolbar=1,location=1';
1.42 matthew 380: options += ',width=700,height=600';
381: editbrowser = open(url,title,options,'1');
382: editbrowser.focus();
383: }
384: var editsearcher;
1.135 albertel 385: function opensearcher(formname,elementname,titleelement) {
1.42 matthew 386: var url = '/adm/searchcat?';
387: if (editsearcher == null) {
388: url += 'launch=1&';
389: }
390: url += 'catalogmode=interactive&';
1.199 albertel 391: url += 'mode=$mode&';
1.42 matthew 392: url += 'form=' + formname + '&';
1.135 albertel 393: if (titleelement != null) {
394: url += 'titleelement=' + titleelement + '&';
1.217 albertel 395: } else {
396: url += 'titleelement=&';
397: }
1.42 matthew 398: url += 'element=' + elementname + '';
399: var title = 'Search';
1.435 albertel 400: var options = 'scrollbars=1,resizable=1,menubar=0,toolbar=1,location=1';
1.42 matthew 401: options += ',width=700,height=600';
402: editsearcher = open(url,title,options,'1');
403: editsearcher.focus();
404: }
1.219 albertel 405: // END LON-CAPA Internal -->
1.42 matthew 406: END
1.170 www 407: }
408:
409: sub lastresurl {
1.258 albertel 410: if ($env{'environment.lastresurl'}) {
411: return $env{'environment.lastresurl'}
1.170 www 412: } else {
413: return '/res';
414: }
415: }
416:
417: sub storeresurl {
418: my $resurl=&Apache::lonnet::clutter(shift);
419: unless ($resurl=~/^\/res/) { return 0; }
420: $resurl=~s/\/$//;
421: &Apache::lonnet::put('environment',{'lastresurl' => $resurl});
1.646 raeburn 422: &Apache::lonnet::appenv({'environment.lastresurl' => $resurl});
1.170 www 423: return 1;
1.42 matthew 424: }
425:
1.74 www 426: sub studentbrowser_javascript {
1.111 www 427: unless (
1.258 albertel 428: (($env{'request.course.id'}) &&
1.302 albertel 429: (&Apache::lonnet::allowed('srm',$env{'request.course.id'})
430: || &Apache::lonnet::allowed('srm',$env{'request.course.id'}.
431: '/'.$env{'request.course.sec'})
432: ))
1.258 albertel 433: || ($env{'request.role'}=~/^(au|dc|su)/)
1.111 www 434: ) { return ''; }
1.74 www 435: return (<<'ENDSTDBRW');
1.776 bisitz 436: <script type="text/javascript" language="Javascript">
1.824 bisitz 437: // <![CDATA[
1.74 www 438: var stdeditbrowser;
1.999 www 439: function openstdbrowser(formname,uname,udom,clicker,roleflag,ignorefilter,courseadvonly) {
1.74 www 440: var url = '/adm/pickstudent?';
441: var filter;
1.558 albertel 442: if (!ignorefilter) {
443: eval('filter=document.'+formname+'.'+uname+'.value;');
444: }
1.74 www 445: if (filter != null) {
446: if (filter != '') {
447: url += 'filter='+filter+'&';
448: }
449: }
450: url += 'form=' + formname + '&unameelement='+uname+
1.999 www 451: '&udomelement='+udom+
452: '&clicker='+clicker;
1.111 www 453: if (roleflag) { url+="&roles=1"; }
1.793 raeburn 454: if (courseadvonly) { url+="&courseadvonly=1"; }
1.102 www 455: var title = 'Student_Browser';
1.74 www 456: var options = 'scrollbars=1,resizable=1,menubar=0';
457: options += ',width=700,height=600';
458: stdeditbrowser = open(url,title,options,'1');
459: stdeditbrowser.focus();
460: }
1.824 bisitz 461: // ]]>
1.74 www 462: </script>
463: ENDSTDBRW
464: }
1.42 matthew 465:
1.1003 www 466: sub resourcebrowser_javascript {
467: unless ($env{'request.course.id'}) { return ''; }
1.1004 www 468: return (<<'ENDRESBRW');
1.1003 www 469: <script type="text/javascript" language="Javascript">
470: // <![CDATA[
471: var reseditbrowser;
1.1004 www 472: function openresbrowser(formname,reslink) {
1.1005 www 473: var url = '/adm/pickresource?form='+formname+'&reslink='+reslink;
1.1003 www 474: var title = 'Resource_Browser';
475: var options = 'scrollbars=1,resizable=1,menubar=0';
1.1005 www 476: options += ',width=700,height=500';
1.1004 www 477: reseditbrowser = open(url,title,options,'1');
478: reseditbrowser.focus();
1.1003 www 479: }
480: // ]]>
481: </script>
1.1004 www 482: ENDRESBRW
1.1003 www 483: }
484:
1.74 www 485: sub selectstudent_link {
1.999 www 486: my ($form,$unameele,$udomele,$courseadvonly,$clickerid)=@_;
487: my $callargs = "'".&Apache::lonhtmlcommon::entity_encode($form)."','".
488: &Apache::lonhtmlcommon::entity_encode($unameele)."','".
489: &Apache::lonhtmlcommon::entity_encode($udomele)."'";
1.258 albertel 490: if ($env{'request.course.id'}) {
1.302 albertel 491: if (!&Apache::lonnet::allowed('srm',$env{'request.course.id'})
492: && !&Apache::lonnet::allowed('srm',$env{'request.course.id'}.
493: '/'.$env{'request.course.sec'})) {
1.111 www 494: return '';
495: }
1.999 www 496: $callargs.=",'".&Apache::lonhtmlcommon::entity_encode($clickerid)."'";
1.793 raeburn 497: if ($courseadvonly) {
498: $callargs .= ",'',1,1";
499: }
500: return '<span class="LC_nobreak">'.
501: '<a href="javascript:openstdbrowser('.$callargs.');">'.
502: &mt('Select User').'</a></span>';
1.74 www 503: }
1.258 albertel 504: if ($env{'request.role'}=~/^(au|dc|su)/) {
1.1012 www 505: $callargs .= ",'',1";
1.793 raeburn 506: return '<span class="LC_nobreak">'.
507: '<a href="javascript:openstdbrowser('.$callargs.');">'.
508: &mt('Select User').'</a></span>';
1.111 www 509: }
510: return '';
1.91 www 511: }
512:
1.1004 www 513: sub selectresource_link {
514: my ($form,$reslink,$arg)=@_;
515:
516: my $callargs = "'".&Apache::lonhtmlcommon::entity_encode($form)."','".
517: &Apache::lonhtmlcommon::entity_encode($reslink)."'";
518: unless ($env{'request.course.id'}) { return $arg; }
519: return '<span class="LC_nobreak">'.
520: '<a href="javascript:openresbrowser('.$callargs.');">'.
521: $arg.'</a></span>';
522: }
523:
524:
525:
1.653 raeburn 526: sub authorbrowser_javascript {
527: return <<"ENDAUTHORBRW";
1.776 bisitz 528: <script type="text/javascript" language="JavaScript">
1.824 bisitz 529: // <![CDATA[
1.653 raeburn 530: var stdeditbrowser;
531:
532: function openauthorbrowser(formname,udom) {
533: var url = '/adm/pickauthor?';
534: url += 'form='+formname+'&roledom='+udom;
535: var title = 'Author_Browser';
536: var options = 'scrollbars=1,resizable=1,menubar=0';
537: options += ',width=700,height=600';
538: stdeditbrowser = open(url,title,options,'1');
539: stdeditbrowser.focus();
540: }
541:
1.824 bisitz 542: // ]]>
1.653 raeburn 543: </script>
544: ENDAUTHORBRW
545: }
546:
1.91 www 547: sub coursebrowser_javascript {
1.1116 raeburn 548: my ($domainfilter,$sec_element,$formname,$role_element,$crstype,
1.1221 raeburn 549: $credits_element,$instcode) = @_;
1.932 raeburn 550: my $wintitle = 'Course_Browser';
1.931 raeburn 551: if ($crstype eq 'Community') {
1.932 raeburn 552: $wintitle = 'Community_Browser';
1.909 raeburn 553: }
1.876 raeburn 554: my $id_functions = &javascript_index_functions();
555: my $output = '
1.776 bisitz 556: <script type="text/javascript" language="JavaScript">
1.824 bisitz 557: // <![CDATA[
1.468 raeburn 558: var stdeditbrowser;'."\n";
1.876 raeburn 559:
560: $output .= <<"ENDSTDBRW";
1.909 raeburn 561: function opencrsbrowser(formname,uname,udom,desc,extra_element,multflag,type,type_elem) {
1.91 www 562: var url = '/adm/pickcourse?';
1.895 raeburn 563: var formid = getFormIdByName(formname);
1.876 raeburn 564: var domainfilter = getDomainFromSelectbox(formname,udom);
1.128 albertel 565: if (domainfilter != null) {
566: if (domainfilter != '') {
567: url += 'domainfilter='+domainfilter+'&';
568: }
569: }
1.91 www 570: url += 'form=' + formname + '&cnumelement='+uname+
1.187 albertel 571: '&cdomelement='+udom+
572: '&cnameelement='+desc;
1.468 raeburn 573: if (extra_element !=null && extra_element != '') {
1.594 raeburn 574: if (formname == 'rolechoice' || formname == 'studentform') {
1.468 raeburn 575: url += '&roleelement='+extra_element;
576: if (domainfilter == null || domainfilter == '') {
577: url += '&domainfilter='+extra_element;
578: }
1.234 raeburn 579: }
1.468 raeburn 580: else {
581: if (formname == 'portform') {
582: url += '&setroles='+extra_element;
1.800 raeburn 583: } else {
584: if (formname == 'rules') {
585: url += '&fixeddom='+extra_element;
586: }
1.468 raeburn 587: }
588: }
1.230 raeburn 589: }
1.909 raeburn 590: if (type != null && type != '') {
591: url += '&type='+type;
592: }
593: if (type_elem != null && type_elem != '') {
594: url += '&typeelement='+type_elem;
595: }
1.872 raeburn 596: if (formname == 'ccrs') {
597: var ownername = document.forms[formid].ccuname.value;
598: var ownerdom = document.forms[formid].ccdomain.options[document.forms[formid].ccdomain.selectedIndex].value;
1.1238 raeburn 599: url += '&cloner='+ownername+':'+ownerdom;
600: if (type == 'Course') {
601: url += '&crscode='+document.forms[formid].crscode.value;
602: }
1.1221 raeburn 603: }
604: if (formname == 'requestcrs') {
605: url += '&crsdom=$domainfilter&crscode=$instcode';
1.872 raeburn 606: }
1.293 raeburn 607: if (multflag !=null && multflag != '') {
608: url += '&multiple='+multflag;
609: }
1.909 raeburn 610: var title = '$wintitle';
1.91 www 611: var options = 'scrollbars=1,resizable=1,menubar=0';
612: options += ',width=700,height=600';
613: stdeditbrowser = open(url,title,options,'1');
614: stdeditbrowser.focus();
615: }
1.876 raeburn 616: $id_functions
617: ENDSTDBRW
1.1116 raeburn 618: if (($sec_element ne '') || ($role_element ne '') || ($credits_element ne '')) {
619: $output .= &setsec_javascript($sec_element,$formname,$role_element,
620: $credits_element);
1.876 raeburn 621: }
622: $output .= '
623: // ]]>
624: </script>';
625: return $output;
626: }
627:
628: sub javascript_index_functions {
629: return <<"ENDJS";
630:
631: function getFormIdByName(formname) {
632: for (var i=0;i<document.forms.length;i++) {
633: if (document.forms[i].name == formname) {
634: return i;
635: }
636: }
637: return -1;
638: }
639:
640: function getIndexByName(formid,item) {
641: for (var i=0;i<document.forms[formid].elements.length;i++) {
642: if (document.forms[formid].elements[i].name == item) {
643: return i;
644: }
645: }
646: return -1;
647: }
1.468 raeburn 648:
1.876 raeburn 649: function getDomainFromSelectbox(formname,udom) {
650: var userdom;
651: var formid = getFormIdByName(formname);
652: if (formid > -1) {
653: var domid = getIndexByName(formid,udom);
654: if (domid > -1) {
655: if (document.forms[formid].elements[domid].type == 'select-one') {
656: userdom=document.forms[formid].elements[domid].options[document.forms[formid].elements[domid].selectedIndex].value;
657: }
658: if (document.forms[formid].elements[domid].type == 'hidden') {
659: userdom=document.forms[formid].elements[domid].value;
1.468 raeburn 660: }
661: }
662: }
1.876 raeburn 663: return userdom;
664: }
665:
666: ENDJS
1.468 raeburn 667:
1.876 raeburn 668: }
669:
1.1017 raeburn 670: sub javascript_array_indexof {
1.1018 raeburn 671: return <<ENDJS;
1.1017 raeburn 672: <script type="text/javascript" language="JavaScript">
673: // <![CDATA[
674:
675: if (!Array.prototype.indexOf) {
676: Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
677: "use strict";
678: if (this === void 0 || this === null) {
679: throw new TypeError();
680: }
681: var t = Object(this);
682: var len = t.length >>> 0;
683: if (len === 0) {
684: return -1;
685: }
686: var n = 0;
687: if (arguments.length > 0) {
688: n = Number(arguments[1]);
1.1088 foxr 689: if (n !== n) { // shortcut for verifying if it is NaN
1.1017 raeburn 690: n = 0;
691: } else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
692: n = (n > 0 || -1) * Math.floor(Math.abs(n));
693: }
694: }
695: if (n >= len) {
696: return -1;
697: }
698: var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
699: for (; k < len; k++) {
700: if (k in t && t[k] === searchElement) {
701: return k;
702: }
703: }
704: return -1;
705: }
706: }
707:
708: // ]]>
709: </script>
710:
711: ENDJS
712:
713: }
714:
1.876 raeburn 715: sub userbrowser_javascript {
716: my $id_functions = &javascript_index_functions();
717: return <<"ENDUSERBRW";
718:
1.888 raeburn 719: function openuserbrowser(formname,uname,udom,ulast,ufirst,uemail,hideudom,crsdom,caller) {
1.876 raeburn 720: var url = '/adm/pickuser?';
721: var userdom = getDomainFromSelectbox(formname,udom);
722: if (userdom != null) {
723: if (userdom != '') {
724: url += 'srchdom='+userdom+'&';
725: }
726: }
727: url += 'form=' + formname + '&unameelement='+uname+
728: '&udomelement='+udom+
729: '&ulastelement='+ulast+
730: '&ufirstelement='+ufirst+
731: '&uemailelement='+uemail+
1.881 raeburn 732: '&hideudomelement='+hideudom+
733: '&coursedom='+crsdom;
1.888 raeburn 734: if ((caller != null) && (caller != undefined)) {
735: url += '&caller='+caller;
736: }
1.876 raeburn 737: var title = 'User_Browser';
738: var options = 'scrollbars=1,resizable=1,menubar=0';
739: options += ',width=700,height=600';
740: var stdeditbrowser = open(url,title,options,'1');
741: stdeditbrowser.focus();
742: }
743:
1.888 raeburn 744: function fix_domain (formname,udom,origdom,uname) {
1.876 raeburn 745: var formid = getFormIdByName(formname);
746: if (formid > -1) {
1.888 raeburn 747: var unameid = getIndexByName(formid,uname);
1.876 raeburn 748: var domid = getIndexByName(formid,udom);
749: var hidedomid = getIndexByName(formid,origdom);
750: if (hidedomid > -1) {
751: var fixeddom = document.forms[formid].elements[hidedomid].value;
1.888 raeburn 752: var unameval = document.forms[formid].elements[unameid].value;
753: if ((fixeddom != '') && (fixeddom != undefined) && (fixeddom != null) && (unameval != '') && (unameval != undefined) && (unameval != null)) {
754: if (domid > -1) {
755: var slct = document.forms[formid].elements[domid];
756: if (slct.type == 'select-one') {
757: var i;
758: for (i=0;i<slct.length;i++) {
759: if (slct.options[i].value==fixeddom) { slct.selectedIndex=i; }
760: }
761: }
762: if (slct.type == 'hidden') {
763: slct.value = fixeddom;
1.876 raeburn 764: }
765: }
1.468 raeburn 766: }
767: }
768: }
1.876 raeburn 769: return;
770: }
771:
772: $id_functions
773: ENDUSERBRW
1.468 raeburn 774: }
775:
776: sub setsec_javascript {
1.1116 raeburn 777: my ($sec_element,$formname,$role_element,$credits_element) = @_;
1.905 raeburn 778: my (@courserolenames,@communityrolenames,$rolestr,$courserolestr,
779: $communityrolestr);
780: if ($role_element ne '') {
781: my @allroles = ('st','ta','ep','in','ad');
782: foreach my $crstype ('Course','Community') {
783: if ($crstype eq 'Community') {
784: foreach my $role (@allroles) {
785: push(@communityrolenames,&Apache::lonnet::plaintext($role,$crstype));
786: }
787: push(@communityrolenames,&Apache::lonnet::plaintext('co'));
788: } else {
789: foreach my $role (@allroles) {
790: push(@courserolenames,&Apache::lonnet::plaintext($role,$crstype));
791: }
792: push(@courserolenames,&Apache::lonnet::plaintext('cc'));
793: }
794: }
795: $rolestr = '"'.join('","',@allroles).'"';
796: $courserolestr = '"'.join('","',@courserolenames).'"';
797: $communityrolestr = '"'.join('","',@communityrolenames).'"';
798: }
1.468 raeburn 799: my $setsections = qq|
800: function setSect(sectionlist) {
1.629 raeburn 801: var sectionsArray = new Array();
802: if ((sectionlist != '') && (typeof sectionlist != "undefined")) {
803: sectionsArray = sectionlist.split(",");
804: }
1.468 raeburn 805: var numSections = sectionsArray.length;
806: document.$formname.$sec_element.length = 0;
807: if (numSections == 0) {
808: document.$formname.$sec_element.multiple=false;
809: document.$formname.$sec_element.size=1;
810: document.$formname.$sec_element.options[0] = new Option('No existing sections','',false,false)
811: } else {
812: if (numSections == 1) {
813: document.$formname.$sec_element.multiple=false;
814: document.$formname.$sec_element.size=1;
815: document.$formname.$sec_element.options[0] = new Option('Select','',true,true);
816: document.$formname.$sec_element.options[1] = new Option('No section','',false,false)
817: document.$formname.$sec_element.options[2] = new Option(sectionsArray[0],sectionsArray[0],false,false);
818: } else {
819: for (var i=0; i<numSections; i++) {
820: document.$formname.$sec_element.options[i] = new Option(sectionsArray[i],sectionsArray[i],false,false)
821: }
822: document.$formname.$sec_element.multiple=true
823: if (numSections < 3) {
824: document.$formname.$sec_element.size=numSections;
825: } else {
826: document.$formname.$sec_element.size=3;
827: }
828: document.$formname.$sec_element.options[0].selected = false
829: }
830: }
1.91 www 831: }
1.905 raeburn 832:
833: function setRole(crstype) {
1.468 raeburn 834: |;
1.905 raeburn 835: if ($role_element eq '') {
836: $setsections .= ' return;
837: }
838: ';
839: } else {
840: $setsections .= qq|
841: var elementLength = document.$formname.$role_element.length;
842: var allroles = Array($rolestr);
843: var courserolenames = Array($courserolestr);
844: var communityrolenames = Array($communityrolestr);
845: if (elementLength != undefined) {
846: if (document.$formname.$role_element.options[5].value == 'cc') {
847: if (crstype == 'Course') {
848: return;
849: } else {
850: allroles[5] = 'co';
851: for (var i=0; i<6; i++) {
852: document.$formname.$role_element.options[i].value = allroles[i];
853: document.$formname.$role_element.options[i].text = communityrolenames[i];
854: }
855: }
856: } else {
857: if (crstype == 'Community') {
858: return;
859: } else {
860: allroles[5] = 'cc';
861: for (var i=0; i<6; i++) {
862: document.$formname.$role_element.options[i].value = allroles[i];
863: document.$formname.$role_element.options[i].text = courserolenames[i];
864: }
865: }
866: }
867: }
868: return;
869: }
870: |;
871: }
1.1116 raeburn 872: if ($credits_element) {
873: $setsections .= qq|
874: function setCredits(defaultcredits) {
875: document.$formname.$credits_element.value = defaultcredits;
876: return;
877: }
878: |;
879: }
1.468 raeburn 880: return $setsections;
881: }
882:
1.91 www 883: sub selectcourse_link {
1.909 raeburn 884: my ($form,$unameele,$udomele,$desc,$extra_element,$multflag,$selecttype,
885: $typeelement) = @_;
886: my $type = $selecttype;
1.871 raeburn 887: my $linktext = &mt('Select Course');
888: if ($selecttype eq 'Community') {
1.909 raeburn 889: $linktext = &mt('Select Community');
1.1239 raeburn 890: } elsif ($selecttype eq 'Placement') {
891: $linktext = &mt('Select Placement Test');
1.906 raeburn 892: } elsif ($selecttype eq 'Course/Community') {
893: $linktext = &mt('Select Course/Community');
1.909 raeburn 894: $type = '';
1.1019 raeburn 895: } elsif ($selecttype eq 'Select') {
896: $linktext = &mt('Select');
897: $type = '';
1.871 raeburn 898: }
1.787 bisitz 899: return '<span class="LC_nobreak">'
900: ."<a href='"
901: .'javascript:opencrsbrowser("'.$form.'","'.$unameele
902: .'","'.$udomele.'","'.$desc.'","'.$extra_element
1.909 raeburn 903: .'","'.$multflag.'","'.$type.'","'.$typeelement.'");'
1.871 raeburn 904: ."'>".$linktext.'</a>'
1.787 bisitz 905: .'</span>';
1.74 www 906: }
1.42 matthew 907:
1.653 raeburn 908: sub selectauthor_link {
909: my ($form,$udom)=@_;
910: return '<a href="javascript:openauthorbrowser('."'$form','$udom'".');">'.
911: &mt('Select Author').'</a>';
912: }
913:
1.876 raeburn 914: sub selectuser_link {
1.881 raeburn 915: my ($form,$unameelem,$domelem,$lastelem,$firstelem,$emailelem,$hdomelem,
1.888 raeburn 916: $coursedom,$linktext,$caller) = @_;
1.876 raeburn 917: return '<a href="javascript:openuserbrowser('."'$form','$unameelem','$domelem',".
1.888 raeburn 918: "'$lastelem','$firstelem','$emailelem','$hdomelem','$coursedom','$caller'".
1.881 raeburn 919: ');">'.$linktext.'</a>';
1.876 raeburn 920: }
921:
1.273 raeburn 922: sub check_uncheck_jscript {
923: my $jscript = <<"ENDSCRT";
924: function checkAll(field) {
925: if (field.length > 0) {
926: for (i = 0; i < field.length; i++) {
1.1093 raeburn 927: if (!field[i].disabled) {
928: field[i].checked = true;
929: }
1.273 raeburn 930: }
931: } else {
1.1093 raeburn 932: if (!field.disabled) {
933: field.checked = true;
934: }
1.273 raeburn 935: }
936: }
937:
938: function uncheckAll(field) {
939: if (field.length > 0) {
940: for (i = 0; i < field.length; i++) {
941: field[i].checked = false ;
1.543 albertel 942: }
943: } else {
1.273 raeburn 944: field.checked = false ;
945: }
946: }
947: ENDSCRT
948: return $jscript;
949: }
950:
1.656 www 951: sub select_timezone {
1.1256 raeburn 952: my ($name,$selected,$onchange,$includeempty,$disabled)=@_;
953: my $output='<select name="'.$name.'" '.$onchange.$disabled.'>'."\n";
1.659 raeburn 954: if ($includeempty) {
955: $output .= '<option value=""';
956: if (($selected eq '') || ($selected eq 'local')) {
957: $output .= ' selected="selected" ';
958: }
959: $output .= '> </option>';
960: }
1.657 raeburn 961: my @timezones = DateTime::TimeZone->all_names;
962: foreach my $tzone (@timezones) {
963: $output.= '<option value="'.$tzone.'"';
964: if ($tzone eq $selected) {
965: $output.=' selected="selected"';
966: }
967: $output.=">$tzone</option>\n";
1.656 www 968: }
969: $output.="</select>";
970: return $output;
971: }
1.273 raeburn 972:
1.687 raeburn 973: sub select_datelocale {
1.1256 raeburn 974: my ($name,$selected,$onchange,$includeempty,$disabled)=@_;
975: my $output='<select name="'.$name.'" '.$onchange.$disabled.'>'."\n";
1.687 raeburn 976: if ($includeempty) {
977: $output .= '<option value=""';
978: if ($selected eq '') {
979: $output .= ' selected="selected" ';
980: }
981: $output .= '> </option>';
982: }
1.1241 raeburn 983: my @languages = &Apache::lonlocal::preferred_languages();
1.687 raeburn 984: my (@possibles,%locale_names);
1.1241 raeburn 985: my @locales = DateTime::Locale->ids();
986: foreach my $id (@locales) {
987: if ($id ne '') {
988: my ($en_terr,$native_terr);
989: my $loc = DateTime::Locale->load($id);
990: if (ref($loc)) {
991: $en_terr = $loc->name();
992: $native_terr = $loc->native_name();
1.687 raeburn 993: if (grep(/^en$/,@languages) || !@languages) {
994: if ($en_terr ne '') {
995: $locale_names{$id} = '('.$en_terr.')';
996: } elsif ($native_terr ne '') {
997: $locale_names{$id} = $native_terr;
998: }
999: } else {
1000: if ($native_terr ne '') {
1001: $locale_names{$id} = $native_terr.' ';
1002: } elsif ($en_terr ne '') {
1003: $locale_names{$id} = '('.$en_terr.')';
1004: }
1005: }
1.1220 raeburn 1006: $locale_names{$id} = Encode::encode('UTF-8',$locale_names{$id});
1.1241 raeburn 1007: push(@possibles,$id);
1008: }
1.687 raeburn 1009: }
1010: }
1011: foreach my $item (sort(@possibles)) {
1012: $output.= '<option value="'.$item.'"';
1013: if ($item eq $selected) {
1014: $output.=' selected="selected"';
1015: }
1016: $output.=">$item";
1017: if ($locale_names{$item} ne '') {
1.1220 raeburn 1018: $output.=' '.$locale_names{$item};
1.687 raeburn 1019: }
1020: $output.="</option>\n";
1021: }
1022: $output.="</select>";
1023: return $output;
1024: }
1025:
1.792 raeburn 1026: sub select_language {
1.1256 raeburn 1027: my ($name,$selected,$includeempty,$noedit) = @_;
1.792 raeburn 1028: my %langchoices;
1029: if ($includeempty) {
1.1117 raeburn 1030: %langchoices = ('' => 'No language preference');
1.792 raeburn 1031: }
1032: foreach my $id (&languageids()) {
1033: my $code = &supportedlanguagecode($id);
1034: if ($code) {
1035: $langchoices{$code} = &plainlanguagedescription($id);
1036: }
1037: }
1.1117 raeburn 1038: %langchoices = &Apache::lonlocal::texthash(%langchoices);
1.1256 raeburn 1039: return &select_form($selected,$name,\%langchoices,undef,$noedit);
1.792 raeburn 1040: }
1041:
1.42 matthew 1042: =pod
1.36 matthew 1043:
1.1088 foxr 1044:
1045: =item * &list_languages()
1046:
1047: Returns an array reference that is suitable for use in language prompters.
1048: Each array element is itself a two element array. The first element
1049: is the language code. The second element a descsriptiuon of the
1050: language itself. This is suitable for use in e.g.
1051: &Apache::edit::select_arg (once dereferenced that is).
1052:
1053: =cut
1054:
1055: sub list_languages {
1056: my @lang_choices;
1057:
1058: foreach my $id (&languageids()) {
1059: my $code = &supportedlanguagecode($id);
1060: if ($code) {
1061: my $selector = $supported_codes{$id};
1062: my $description = &plainlanguagedescription($id);
1.1263 raeburn 1063: push(@lang_choices, [$selector, $description]);
1.1088 foxr 1064: }
1065: }
1066: return \@lang_choices;
1067: }
1068:
1069: =pod
1070:
1.648 raeburn 1071: =item * &linked_select_forms(...)
1.36 matthew 1072:
1073: linked_select_forms returns a string containing a <script></script> block
1074: and html for two <select> menus. The select menus will be linked in that
1075: changing the value of the first menu will result in new values being placed
1076: in the second menu. The values in the select menu will appear in alphabetical
1.609 raeburn 1077: order unless a defined order is provided.
1.36 matthew 1078:
1079: linked_select_forms takes the following ordered inputs:
1080:
1081: =over 4
1082:
1.112 bowersj2 1083: =item * $formname, the name of the <form> tag
1.36 matthew 1084:
1.112 bowersj2 1085: =item * $middletext, the text which appears between the <select> tags
1.36 matthew 1086:
1.112 bowersj2 1087: =item * $firstdefault, the default value for the first menu
1.36 matthew 1088:
1.112 bowersj2 1089: =item * $firstselectname, the name of the first <select> tag
1.36 matthew 1090:
1.112 bowersj2 1091: =item * $secondselectname, the name of the second <select> tag
1.36 matthew 1092:
1.112 bowersj2 1093: =item * $hashref, a reference to a hash containing the data for the menus.
1.36 matthew 1094:
1.609 raeburn 1095: =item * $menuorder, the order of values in the first menu
1096:
1.1115 raeburn 1097: =item * $onchangefirst, additional javascript call to execute for an onchange
1098: event for the first <select> tag
1099:
1100: =item * $onchangesecond, additional javascript call to execute for an onchange
1101: event for the second <select> tag
1102:
1.1245 raeburn 1103: =item * $suffix, to differentiate separate uses of select2data javascript
1104: objects in a page.
1105:
1.41 ng 1106: =back
1107:
1.36 matthew 1108: Below is an example of such a hash. Only the 'text', 'default', and
1109: 'select2' keys must appear as stated. keys(%menu) are the possible
1110: values for the first select menu. The text that coincides with the
1.41 ng 1111: first menu value is given in $menu{$choice1}->{'text'}. The values
1.36 matthew 1112: and text for the second menu are given in the hash pointed to by
1113: $menu{$choice1}->{'select2'}.
1114:
1.112 bowersj2 1115: my %menu = ( A1 => { text =>"Choice A1" ,
1116: default => "B3",
1117: select2 => {
1118: B1 => "Choice B1",
1119: B2 => "Choice B2",
1120: B3 => "Choice B3",
1121: B4 => "Choice B4"
1.609 raeburn 1122: },
1123: order => ['B4','B3','B1','B2'],
1.112 bowersj2 1124: },
1125: A2 => { text =>"Choice A2" ,
1126: default => "C2",
1127: select2 => {
1128: C1 => "Choice C1",
1129: C2 => "Choice C2",
1130: C3 => "Choice C3"
1.609 raeburn 1131: },
1132: order => ['C2','C1','C3'],
1.112 bowersj2 1133: },
1134: A3 => { text =>"Choice A3" ,
1135: default => "D6",
1136: select2 => {
1137: D1 => "Choice D1",
1138: D2 => "Choice D2",
1139: D3 => "Choice D3",
1140: D4 => "Choice D4",
1141: D5 => "Choice D5",
1142: D6 => "Choice D6",
1143: D7 => "Choice D7"
1.609 raeburn 1144: },
1145: order => ['D4','D3','D2','D1','D7','D6','D5'],
1.112 bowersj2 1146: }
1147: );
1.36 matthew 1148:
1149: =cut
1150:
1151: sub linked_select_forms {
1152: my ($formname,
1153: $middletext,
1154: $firstdefault,
1155: $firstselectname,
1156: $secondselectname,
1.609 raeburn 1157: $hashref,
1158: $menuorder,
1.1115 raeburn 1159: $onchangefirst,
1.1245 raeburn 1160: $onchangesecond,
1161: $suffix
1.36 matthew 1162: ) = @_;
1163: my $second = "document.$formname.$secondselectname";
1164: my $first = "document.$formname.$firstselectname";
1165: # output the javascript to do the changing
1166: my $result = '';
1.776 bisitz 1167: $result.='<script type="text/javascript" language="JavaScript">'."\n";
1.824 bisitz 1168: $result.="// <![CDATA[\n";
1.1245 raeburn 1169: $result.="var select2data${suffix} = new Object();\n";
1.36 matthew 1170: $" = '","';
1171: my $debug = '';
1172: foreach my $s1 (sort(keys(%$hashref))) {
1.1245 raeburn 1173: $result.="select2data${suffix}['d_$s1'] = new Object();\n";
1174: $result.="select2data${suffix}['d_$s1'].def = new String('".
1.36 matthew 1175: $hashref->{$s1}->{'default'}."');\n";
1.1245 raeburn 1176: $result.="select2data${suffix}['d_$s1'].values = new Array(";
1.36 matthew 1177: my @s2values = sort(keys( %{ $hashref->{$s1}->{'select2'} } ));
1.609 raeburn 1178: if (ref($hashref->{$s1}->{'order'}) eq 'ARRAY') {
1179: @s2values = @{$hashref->{$s1}->{'order'}};
1180: }
1.36 matthew 1181: $result.="\"@s2values\");\n";
1.1245 raeburn 1182: $result.="select2data${suffix}['d_$s1'].texts = new Array(";
1.36 matthew 1183: my @s2texts;
1184: foreach my $value (@s2values) {
1.1263 raeburn 1185: push(@s2texts, $hashref->{$s1}->{'select2'}->{$value});
1.36 matthew 1186: }
1187: $result.="\"@s2texts\");\n";
1188: }
1189: $"=' ';
1190: $result.= <<"END";
1191:
1.1245 raeburn 1192: function select1${suffix}_changed() {
1.36 matthew 1193: // Determine new choice
1.1245 raeburn 1194: var newvalue = "d_" + $first.options[$first.selectedIndex].value;
1.36 matthew 1195: // update select2
1.1245 raeburn 1196: var values = select2data${suffix}[newvalue].values;
1197: var texts = select2data${suffix}[newvalue].texts;
1198: var select2def = select2data${suffix}[newvalue].def;
1.36 matthew 1199: var i;
1200: // out with the old
1.1245 raeburn 1201: $second.options.length = 0;
1202: // in with the new
1.36 matthew 1203: for (i=0;i<values.length; i++) {
1204: $second.options[i] = new Option(values[i]);
1.143 matthew 1205: $second.options[i].value = values[i];
1.36 matthew 1206: $second.options[i].text = texts[i];
1207: if (values[i] == select2def) {
1208: $second.options[i].selected = true;
1209: }
1210: }
1211: }
1.824 bisitz 1212: // ]]>
1.36 matthew 1213: </script>
1214: END
1215: # output the initial values for the selection lists
1.1245 raeburn 1216: $result .= "<select size=\"1\" name=\"$firstselectname\" onchange=\"select1${suffix}_changed();$onchangefirst\">\n";
1.609 raeburn 1217: my @order = sort(keys(%{$hashref}));
1218: if (ref($menuorder) eq 'ARRAY') {
1219: @order = @{$menuorder};
1220: }
1221: foreach my $value (@order) {
1.36 matthew 1222: $result.=" <option value=\"$value\" ";
1.253 albertel 1223: $result.=" selected=\"selected\" " if ($value eq $firstdefault);
1.119 www 1224: $result.=">".&mt($hashref->{$value}->{'text'})."</option>\n";
1.36 matthew 1225: }
1226: $result .= "</select>\n";
1227: my %select2 = %{$hashref->{$firstdefault}->{'select2'}};
1228: $result .= $middletext;
1.1115 raeburn 1229: $result .= "<select size=\"1\" name=\"$secondselectname\"";
1230: if ($onchangesecond) {
1231: $result .= ' onchange="'.$onchangesecond.'"';
1232: }
1233: $result .= ">\n";
1.36 matthew 1234: my $seconddefault = $hashref->{$firstdefault}->{'default'};
1.609 raeburn 1235:
1236: my @secondorder = sort(keys(%select2));
1237: if (ref($hashref->{$firstdefault}->{'order'}) eq 'ARRAY') {
1238: @secondorder = @{$hashref->{$firstdefault}->{'order'}};
1239: }
1240: foreach my $value (@secondorder) {
1.36 matthew 1241: $result.=" <option value=\"$value\" ";
1.253 albertel 1242: $result.=" selected=\"selected\" " if ($value eq $seconddefault);
1.119 www 1243: $result.=">".&mt($select2{$value})."</option>\n";
1.36 matthew 1244: }
1245: $result .= "</select>\n";
1246: # return $debug;
1247: return $result;
1248: } # end of sub linked_select_forms {
1249:
1.45 matthew 1250: =pod
1.44 bowersj2 1251:
1.973 raeburn 1252: =item * &help_open_topic($topic,$text,$stayOnPage,$width,$height,$imgid)
1.44 bowersj2 1253:
1.112 bowersj2 1254: Returns a string corresponding to an HTML link to the given help
1255: $topic, where $topic corresponds to the name of a .tex file in
1256: /home/httpd/html/adm/help/tex, with underscores replaced by
1257: spaces.
1258:
1259: $text will optionally be linked to the same topic, allowing you to
1260: link text in addition to the graphic. If you do not want to link
1261: text, but wish to specify one of the later parameters, pass an
1262: empty string.
1263:
1264: $stayOnPage is a value that will be interpreted as a boolean. If true,
1265: the link will not open a new window. If false, the link will open
1266: a new window using Javascript. (Default is false.)
1267:
1268: $width and $height are optional numerical parameters that will
1269: override the width and height of the popped up window, which may
1.973 raeburn 1270: be useful for certain help topics with big pictures included.
1271:
1272: $imgid is the id of the img tag used for the help icon. This may be
1273: used in a javascript call to switch the image src. See
1274: lonhtmlcommon::htmlareaselectactive() for an example.
1.44 bowersj2 1275:
1276: =cut
1277:
1278: sub help_open_topic {
1.973 raeburn 1279: my ($topic, $text, $stayOnPage, $width, $height, $imgid) = @_;
1.48 bowersj2 1280: $text = "" if (not defined $text);
1.44 bowersj2 1281: $stayOnPage = 0 if (not defined $stayOnPage);
1.1033 www 1282: $width = 500 if (not defined $width);
1.44 bowersj2 1283: $height = 400 if (not defined $height);
1284: my $filename = $topic;
1285: $filename =~ s/ /_/g;
1286:
1.48 bowersj2 1287: my $template = "";
1288: my $link;
1.572 banghart 1289:
1.159 www 1290: $topic=~s/\W/\_/g;
1.44 bowersj2 1291:
1.572 banghart 1292: if (!$stayOnPage) {
1.1033 www 1293: $link = "javascript:openMyModal('/adm/help/${filename}.hlp',$width,$height,'yes');";
1.1037 www 1294: } elsif ($stayOnPage eq 'popup') {
1295: $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 1296: } else {
1.48 bowersj2 1297: $link = "/adm/help/${filename}.hlp";
1298: }
1299:
1300: # Add the text
1.1314 raeburn 1301: my $target = ' target="_top"';
1302: if (($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) {
1303: $target = '';
1304: }
1.755 neumanie 1305: if ($text ne "") {
1.763 bisitz 1306: $template.='<span class="LC_help_open_topic">'
1.1314 raeburn 1307: .'<a'.$target.' href="'.$link.'">'
1.763 bisitz 1308: .$text.'</a>';
1.48 bowersj2 1309: }
1310:
1.763 bisitz 1311: # (Always) Add the graphic
1.179 matthew 1312: my $title = &mt('Online Help');
1.667 raeburn 1313: my $helpicon=&lonhttpdurl("/adm/help/help.png");
1.973 raeburn 1314: if ($imgid ne '') {
1315: $imgid = ' id="'.$imgid.'"';
1316: }
1.1314 raeburn 1317: $template.=' <a'.$target.' href="'.$link.'" title="'.$title.'">'
1.763 bisitz 1318: .'<img src="'.$helpicon.'" border="0"'
1319: .' alt="'.&mt('Help: [_1]',$topic).'"'
1.973 raeburn 1320: .' title="'.$title.'" style="vertical-align:middle;"'.$imgid
1.763 bisitz 1321: .' /></a>';
1322: if ($text ne "") {
1323: $template.='</span>';
1324: }
1.44 bowersj2 1325: return $template;
1326:
1.106 bowersj2 1327: }
1328:
1329: # This is a quicky function for Latex cheatsheet editing, since it
1330: # appears in at least four places
1331: sub helpLatexCheatsheet {
1.1037 www 1332: my ($topic,$text,$not_author,$stayOnPage) = @_;
1.732 raeburn 1333: my $out;
1.106 bowersj2 1334: my $addOther = '';
1.732 raeburn 1335: if ($topic) {
1.1037 www 1336: $addOther = '<span>'.&help_open_topic($topic,&mt($text),$stayOnPage, undef, 600).'</span> ';
1.763 bisitz 1337: }
1338: $out = '<span>' # Start cheatsheet
1339: .$addOther
1340: .'<span>'
1.1037 www 1341: .&help_open_topic('Greek_Symbols',&mt('Greek Symbols'),$stayOnPage,undef,600)
1.763 bisitz 1342: .'</span> <span>'
1.1037 www 1343: .&help_open_topic('Other_Symbols',&mt('Other Symbols'),$stayOnPage,undef,600)
1.763 bisitz 1344: .'</span>';
1.732 raeburn 1345: unless ($not_author) {
1.1186 kruse 1346: $out .= '<span>'
1347: .&help_open_topic('Authoring_Output_Tags',&mt('Output Tags'),$stayOnPage,undef,600)
1348: .'</span> <span>'
1349: .&help_open_topic('Authoring_Multilingual_Problems',&mt('How to create problems in different languages'),$stayOnPage,undef,600)
1.763 bisitz 1350: .'</span>';
1.732 raeburn 1351: }
1.763 bisitz 1352: $out .= '</span>'; # End cheatsheet
1.732 raeburn 1353: return $out;
1.172 www 1354: }
1355:
1.430 albertel 1356: sub general_help {
1357: my $helptopic='Student_Intro';
1358: if ($env{'request.role'}=~/^(ca|au)/) {
1359: $helptopic='Authoring_Intro';
1.907 raeburn 1360: } elsif ($env{'request.role'}=~/^(cc|co)/) {
1.430 albertel 1361: $helptopic='Course_Coordination_Intro';
1.672 raeburn 1362: } elsif ($env{'request.role'}=~/^dc/) {
1363: $helptopic='Domain_Coordination_Intro';
1.430 albertel 1364: }
1365: return $helptopic;
1366: }
1367:
1368: sub update_help_link {
1369: my ($topic,$component_help,$faq,$bug,$stayOnPage) = @_;
1370: my $origurl = $ENV{'REQUEST_URI'};
1371: $origurl=~s|^/~|/priv/|;
1372: my $timestamp = time;
1373: foreach my $datum (\$topic,\$component_help,\$faq,\$bug,\$origurl) {
1374: $$datum = &escape($$datum);
1375: }
1376:
1377: my $banner_link = "/adm/helpmenu?page=banner&topic=$topic&component_help=$component_help&faq=$faq&bug=$bug&origurl=$origurl&stamp=$timestamp&stayonpage=$stayOnPage";
1378: my $output .= <<"ENDOUTPUT";
1379: <script type="text/javascript">
1.824 bisitz 1380: // <![CDATA[
1.430 albertel 1381: banner_link = '$banner_link';
1.824 bisitz 1382: // ]]>
1.430 albertel 1383: </script>
1384: ENDOUTPUT
1385: return $output;
1386: }
1387:
1388: # now just updates the help link and generates a blue icon
1.193 raeburn 1389: sub help_open_menu {
1.430 albertel 1390: my ($topic,$component_help,$faq,$bug,$stayOnPage,$width,$height,$text)
1.552 banghart 1391: = @_;
1.949 droeschl 1392: $stayOnPage = 1;
1.430 albertel 1393: my $output;
1394: if ($component_help) {
1395: if (!$text) {
1396: $output=&help_open_topic($component_help,undef,$stayOnPage,
1397: $width,$height);
1398: } else {
1399: my $help_text;
1400: $help_text=&unescape($topic);
1401: $output='<table><tr><td>'.
1402: &help_open_topic($component_help,$help_text,$stayOnPage,
1403: $width,$height).'</td></tr></table>';
1404: }
1405: }
1406: my $banner_link = &update_help_link($topic,$component_help,$faq,$bug,$stayOnPage);
1407: return $output.$banner_link;
1408: }
1409:
1410: sub top_nav_help {
1411: my ($text) = @_;
1.436 albertel 1412: $text = &mt($text);
1.949 droeschl 1413: my $stay_on_page = 1;
1414:
1.1168 raeburn 1415: my ($link,$banner_link);
1416: unless ($env{'request.noversionuri'} =~ m{^/adm/helpmenu}) {
1417: $link = ($stay_on_page) ? "javascript:helpMenu('display')"
1418: : "javascript:helpMenu('open')";
1419: $banner_link = &update_help_link(undef,undef,undef,undef,$stay_on_page);
1420: }
1.201 raeburn 1421: my $title = &mt('Get help');
1.1168 raeburn 1422: if ($link) {
1423: return <<"END";
1.436 albertel 1424: $banner_link
1.1159 raeburn 1425: <a href="$link" title="$title">$text</a>
1.436 albertel 1426: END
1.1168 raeburn 1427: } else {
1428: return ' '.$text.' ';
1429: }
1.436 albertel 1430: }
1431:
1432: sub help_menu_js {
1.1154 raeburn 1433: my ($httphost) = @_;
1.949 droeschl 1434: my $stayOnPage = 1;
1.436 albertel 1435: my $width = 620;
1436: my $height = 600;
1.430 albertel 1437: my $helptopic=&general_help();
1.1154 raeburn 1438: my $details_link = $httphost.'/adm/help/'.$helptopic.'.hlp';
1.261 albertel 1439: my $nothing=&Apache::lonhtmlcommon::javascript_nothing();
1.331 albertel 1440: my $start_page =
1441: &Apache::loncommon::start_page('Help Menu', undef,
1442: {'frameset' => 1,
1443: 'js_ready' => 1,
1.1154 raeburn 1444: 'use_absolute' => $httphost,
1.331 albertel 1445: 'add_entries' => {
1.1168 raeburn 1446: 'border' => '0',
1.579 raeburn 1447: 'rows' => "110,*",},});
1.331 albertel 1448: my $end_page =
1449: &Apache::loncommon::end_page({'frameset' => 1,
1450: 'js_ready' => 1,});
1451:
1.436 albertel 1452: my $template .= <<"ENDTEMPLATE";
1453: <script type="text/javascript">
1.877 bisitz 1454: // <![CDATA[
1.253 albertel 1455: // <!-- BEGIN LON-CAPA Internal
1.430 albertel 1456: var banner_link = '';
1.243 raeburn 1457: function helpMenu(target) {
1458: var caller = this;
1459: if (target == 'open') {
1460: var newWindow = null;
1461: try {
1.262 albertel 1462: newWindow = window.open($nothing,"helpmenu","HEIGHT=$height,WIDTH=$width,resizable=yes,scrollbars=yes" )
1.243 raeburn 1463: }
1464: catch(error) {
1465: writeHelp(caller);
1466: return;
1467: }
1468: if (newWindow) {
1469: caller = newWindow;
1470: }
1.193 raeburn 1471: }
1.243 raeburn 1472: writeHelp(caller);
1473: return;
1474: }
1475: function writeHelp(caller) {
1.1168 raeburn 1476: caller.document.writeln('$start_page\\n<frame name="bannerframe" src="'+banner_link+'" marginwidth="0" marginheight="0" frameborder="0">\\n');
1477: caller.document.writeln('<frame name="bodyframe" src="$details_link" marginwidth="0" marginheight="0" frameborder="0">\\n$end_page');
1478: caller.document.close();
1479: caller.focus();
1.193 raeburn 1480: }
1.877 bisitz 1481: // END LON-CAPA Internal -->
1.253 albertel 1482: // ]]>
1.436 albertel 1483: </script>
1.193 raeburn 1484: ENDTEMPLATE
1485: return $template;
1486: }
1487:
1.172 www 1488: sub help_open_bug {
1489: my ($topic, $text, $stayOnPage, $width, $height) = @_;
1.258 albertel 1490: unless ($env{'user.adv'}) { return ''; }
1.172 www 1491: unless ($Apache::lonnet::perlvar{'BugzillaHost'}) { return ''; }
1492: $text = "" if (not defined $text);
1493: $stayOnPage=1;
1.184 albertel 1494: $width = 600 if (not defined $width);
1495: $height = 600 if (not defined $height);
1.172 www 1496:
1497: $topic=~s/\W+/\+/g;
1498: my $link='';
1499: my $template='';
1.379 albertel 1500: my $url=$Apache::lonnet::perlvar{'BugzillaHost'}.'enter_bug.cgi?product=LON-CAPA&bug_file_loc='.
1501: &escape($ENV{'REQUEST_URI'}).'&component='.$topic;
1.172 www 1502: if (!$stayOnPage)
1503: {
1504: $link = "javascript:void(open('$url', 'Bugzilla', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
1505: }
1506: else
1507: {
1508: $link = $url;
1509: }
1.1314 raeburn 1510:
1511: my $target = ' target="_top"';
1512: if (($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) {
1513: $target = '';
1514: }
1.172 www 1515: # Add the text
1516: if ($text ne "")
1517: {
1518: $template .=
1519: "<table bgcolor='#AA3333' cellspacing='1' cellpadding='1' border='0'><tr>".
1.1314 raeburn 1520: "<td bgcolor='#FF5555'><a".$target." href=\"$link\"><span style=\"color:#FFFFFF;font-size:10pt;\">$text</span></a>";
1.172 www 1521: }
1522:
1523: # Add the graphic
1.179 matthew 1524: my $title = &mt('Report a Bug');
1.215 albertel 1525: my $bugicon=&lonhttpdurl("/adm/lonMisc/smallBug.gif");
1.172 www 1526: $template .= <<"ENDTEMPLATE";
1.1314 raeburn 1527: <a$target href="$link" title="$title"><img src="$bugicon" border="0" alt="(Bug: $topic)" /></a>
1.172 www 1528: ENDTEMPLATE
1529: if ($text ne '') { $template.='</td></tr></table>' };
1530: return $template;
1531:
1532: }
1533:
1534: sub help_open_faq {
1535: my ($topic, $text, $stayOnPage, $width, $height) = @_;
1.258 albertel 1536: unless ($env{'user.adv'}) { return ''; }
1.172 www 1537: unless ($Apache::lonnet::perlvar{'FAQHost'}) { return ''; }
1538: $text = "" if (not defined $text);
1539: $stayOnPage=1;
1540: $width = 350 if (not defined $width);
1541: $height = 400 if (not defined $height);
1542:
1543: $topic=~s/\W+/\+/g;
1544: my $link='';
1545: my $template='';
1546: my $url=$Apache::lonnet::perlvar{'FAQHost'}.'/fom/cache/'.$topic.'.html';
1547: if (!$stayOnPage)
1548: {
1549: $link = "javascript:void(open('$url', 'FAQ-O-Matic', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
1550: }
1551: else
1552: {
1553: $link = $url;
1554: }
1555:
1556: # Add the text
1557: if ($text ne "")
1558: {
1559: $template .=
1.173 www 1560: "<table bgcolor='#337733' cellspacing='1' cellpadding='1' border='0'><tr>".
1.705 tempelho 1561: "<td bgcolor='#448844'><a target=\"_top\" href=\"$link\"><span style=\"color:#FFFFFF; font-size:10pt;\">$text</span></a>";
1.172 www 1562: }
1563:
1564: # Add the graphic
1.179 matthew 1565: my $title = &mt('View the FAQ');
1.215 albertel 1566: my $faqicon=&lonhttpdurl("/adm/lonMisc/smallFAQ.gif");
1.172 www 1567: $template .= <<"ENDTEMPLATE";
1.436 albertel 1568: <a target="_top" href="$link" title="$title"><img src="$faqicon" border="0" alt="(FAQ: $topic)" /></a>
1.172 www 1569: ENDTEMPLATE
1570: if ($text ne '') { $template.='</td></tr></table>' };
1571: return $template;
1572:
1.44 bowersj2 1573: }
1.37 matthew 1574:
1.180 matthew 1575: ###############################################################
1576: ###############################################################
1577:
1.45 matthew 1578: =pod
1579:
1.648 raeburn 1580: =item * &change_content_javascript():
1.256 matthew 1581:
1582: This and the next function allow you to create small sections of an
1583: otherwise static HTML page that you can update on the fly with
1584: Javascript, even in Netscape 4.
1585:
1586: The Javascript fragment returned by this function (no E<lt>scriptE<gt> tag)
1587: must be written to the HTML page once. It will prove the Javascript
1588: function "change(name, content)". Calling the change function with the
1589: name of the section
1590: you want to update, matching the name passed to C<changable_area>, and
1591: the new content you want to put in there, will put the content into
1592: that area.
1593:
1594: B<Note>: Netscape 4 only reserves enough space for the changable area
1595: to contain room for the original contents. You need to "make space"
1596: for whatever changes you wish to make, and be B<sure> to check your
1597: code in Netscape 4. This feature in Netscape 4 is B<not> powerful;
1598: it's adequate for updating a one-line status display, but little more.
1599: This script will set the space to 100% width, so you only need to
1600: worry about height in Netscape 4.
1601:
1602: Modern browsers are much less limiting, and if you can commit to the
1603: user not using Netscape 4, this feature may be used freely with
1604: pretty much any HTML.
1605:
1606: =cut
1607:
1608: sub change_content_javascript {
1609: # If we're on Netscape 4, we need to use Layer-based code
1.258 albertel 1610: if ($env{'browser.type'} eq 'netscape' &&
1611: $env{'browser.version'} =~ /^4\./) {
1.256 matthew 1612: return (<<NETSCAPE4);
1613: function change(name, content) {
1614: doc = document.layers[name+"___escape"].layers[0].document;
1615: doc.open();
1616: doc.write(content);
1617: doc.close();
1618: }
1619: NETSCAPE4
1620: } else {
1621: # Otherwise, we need to use semi-standards-compliant code
1622: # (technically, "innerHTML" isn't standard but the equivalent
1623: # is really scary, and every useful browser supports it
1624: return (<<DOMBASED);
1625: function change(name, content) {
1626: element = document.getElementById(name);
1627: element.innerHTML = content;
1628: }
1629: DOMBASED
1630: }
1631: }
1632:
1633: =pod
1634:
1.648 raeburn 1635: =item * &changable_area($name,$origContent):
1.256 matthew 1636:
1637: This provides a "changable area" that can be modified on the fly via
1638: the Javascript code provided in C<change_content_javascript>. $name is
1639: the name you will use to reference the area later; do not repeat the
1640: same name on a given HTML page more then once. $origContent is what
1641: the area will originally contain, which can be left blank.
1642:
1643: =cut
1644:
1645: sub changable_area {
1646: my ($name, $origContent) = @_;
1647:
1.258 albertel 1648: if ($env{'browser.type'} eq 'netscape' &&
1649: $env{'browser.version'} =~ /^4\./) {
1.256 matthew 1650: # If this is netscape 4, we need to use the Layer tag
1651: return "<ilayer width='100%' id='${name}___escape' overflow='none'><layer width='100%' id='$name' overflow='none'>$origContent</layer></ilayer>";
1652: } else {
1653: return "<span id='$name'>$origContent</span>";
1654: }
1655: }
1656:
1657: =pod
1658:
1.648 raeburn 1659: =item * &viewport_geometry_js
1.590 raeburn 1660:
1661: Provides javascript object (Geometry) which can provide information about the viewport geometry for the client browser.
1662:
1663: =cut
1664:
1665:
1666: sub viewport_geometry_js {
1667: return <<"GEOMETRY";
1668: var Geometry = {};
1669: function init_geometry() {
1670: if (Geometry.init) { return };
1671: Geometry.init=1;
1672: if (window.innerHeight) {
1673: Geometry.getViewportHeight = function() { return window.innerHeight; };
1674: Geometry.getViewportWidth = function() { return window.innerWidth; };
1675: Geometry.getHorizontalScroll = function() { return window.pageXOffset; };
1676: Geometry.getVerticalScroll = function() { return window.pageYOffset; };
1677: }
1678: else if (document.documentElement && document.documentElement.clientHeight) {
1679: Geometry.getViewportHeight =
1680: function() { return document.documentElement.clientHeight; };
1681: Geometry.getViewportWidth =
1682: function() { return document.documentElement.clientWidth; };
1683:
1684: Geometry.getHorizontalScroll =
1685: function() { return document.documentElement.scrollLeft; };
1686: Geometry.getVerticalScroll =
1687: function() { return document.documentElement.scrollTop; };
1688: }
1689: else if (document.body.clientHeight) {
1690: Geometry.getViewportHeight =
1691: function() { return document.body.clientHeight; };
1692: Geometry.getViewportWidth =
1693: function() { return document.body.clientWidth; };
1694: Geometry.getHorizontalScroll =
1695: function() { return document.body.scrollLeft; };
1696: Geometry.getVerticalScroll =
1697: function() { return document.body.scrollTop; };
1698: }
1699: }
1700:
1701: GEOMETRY
1702: }
1703:
1704: =pod
1705:
1.648 raeburn 1706: =item * &viewport_size_js()
1.590 raeburn 1707:
1708: 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.
1709:
1710: =cut
1711:
1712: sub viewport_size_js {
1713: my $geometry = &viewport_geometry_js();
1714: return <<"DIMS";
1715:
1716: $geometry
1717:
1718: function getViewportDims(width,height) {
1719: init_geometry();
1720: width.value = Geometry.getViewportWidth();
1721: height.value = Geometry.getViewportHeight();
1722: return;
1723: }
1724:
1725: DIMS
1726: }
1727:
1728: =pod
1729:
1.648 raeburn 1730: =item * &resize_textarea_js()
1.565 albertel 1731:
1732: emits the needed javascript to resize a textarea to be as big as possible
1733:
1734: creates a function resize_textrea that takes two IDs first should be
1735: the id of the element to resize, second should be the id of a div that
1736: surrounds everything that comes after the textarea, this routine needs
1737: to be attached to the <body> for the onload and onresize events.
1738:
1.648 raeburn 1739: =back
1.565 albertel 1740:
1741: =cut
1742:
1743: sub resize_textarea_js {
1.590 raeburn 1744: my $geometry = &viewport_geometry_js();
1.565 albertel 1745: return <<"RESIZE";
1746: <script type="text/javascript">
1.824 bisitz 1747: // <![CDATA[
1.590 raeburn 1748: $geometry
1.565 albertel 1749:
1.588 albertel 1750: function getX(element) {
1751: var x = 0;
1752: while (element) {
1753: x += element.offsetLeft;
1754: element = element.offsetParent;
1755: }
1756: return x;
1757: }
1758: function getY(element) {
1759: var y = 0;
1760: while (element) {
1761: y += element.offsetTop;
1762: element = element.offsetParent;
1763: }
1764: return y;
1765: }
1766:
1767:
1.565 albertel 1768: function resize_textarea(textarea_id,bottom_id) {
1769: init_geometry();
1770: var textarea = document.getElementById(textarea_id);
1771: //alert(textarea);
1772:
1.588 albertel 1773: var textarea_top = getY(textarea);
1.565 albertel 1774: var textarea_height = textarea.offsetHeight;
1775: var bottom = document.getElementById(bottom_id);
1.588 albertel 1776: var bottom_top = getY(bottom);
1.565 albertel 1777: var bottom_height = bottom.offsetHeight;
1778: var window_height = Geometry.getViewportHeight();
1.588 albertel 1779: var fudge = 23;
1.565 albertel 1780: var new_height = window_height-fudge-textarea_top-bottom_height;
1781: if (new_height < 300) {
1782: new_height = 300;
1783: }
1784: textarea.style.height=new_height+'px';
1785: }
1.824 bisitz 1786: // ]]>
1.565 albertel 1787: </script>
1788: RESIZE
1789:
1790: }
1791:
1.1205 golterma 1792: sub colorfuleditor_js {
1.1248 raeburn 1793: my $browse_or_search;
1794: my $respath;
1795: my ($cnum,$cdom) = &crsauthor_url();
1796: if ($cnum) {
1797: $respath = "/res/$cdom/$cnum/";
1798: my %js_lt = &Apache::lonlocal::texthash(
1799: sunm => 'Sub-directory name',
1800: save => 'Save page to make this permanent',
1801: );
1802: &js_escape(\%js_lt);
1803: $browse_or_search = <<"END";
1804:
1805: function toggleChooser(form,element,titleid,only,search) {
1806: var disp = 'none';
1807: if (document.getElementById('chooser_'+element)) {
1808: var curr = document.getElementById('chooser_'+element).style.display;
1809: if (curr == 'none') {
1810: disp='inline';
1811: if (form.elements['chooser_'+element].length) {
1812: for (var i=0; i<form.elements['chooser_'+element].length; i++) {
1813: form.elements['chooser_'+element][i].checked = false;
1814: }
1815: }
1816: toggleResImport(form,element);
1817: }
1818: document.getElementById('chooser_'+element).style.display = disp;
1819: }
1820: }
1821:
1822: function toggleCrsFile(form,element,numdirs) {
1823: if (document.getElementById('chooser_'+element+'_crsres')) {
1824: var curr = document.getElementById('chooser_'+element+'_crsres').style.display;
1825: if (curr == 'none') {
1826: if (numdirs) {
1827: form.elements['coursepath_'+element].selectedIndex = 0;
1828: if (numdirs > 1) {
1829: window['select1'+element+'_changed']();
1830: }
1831: }
1832: }
1833: document.getElementById('chooser_'+element+'_crsres').style.display = 'block';
1834:
1835: }
1836: if (document.getElementById('chooser_'+element+'_upload')) {
1837: document.getElementById('chooser_'+element+'_upload').style.display = 'none';
1838: if (document.getElementById('uploadcrsres_'+element)) {
1839: document.getElementById('uploadcrsres_'+element).value = '';
1840: }
1841: }
1842: return;
1843: }
1844:
1845: function toggleCrsUpload(form,element,numcrsdirs) {
1846: if (document.getElementById('chooser_'+element+'_crsres')) {
1847: document.getElementById('chooser_'+element+'_crsres').style.display = 'none';
1848: }
1849: if (document.getElementById('chooser_'+element+'_upload')) {
1850: var curr = document.getElementById('chooser_'+element+'_upload').style.display;
1851: if (curr == 'none') {
1852: if (numcrsdirs) {
1853: form.elements['crsauthorpath_'+element].selectedIndex = 0;
1854: form.elements['newsubdir_'+element][0].checked = true;
1855: toggleNewsubdir(form,element);
1856: }
1857: }
1858: document.getElementById('chooser_'+element+'_upload').style.display = 'block';
1859: }
1860: return;
1861: }
1862:
1863: function toggleResImport(form,element) {
1864: var choices = new Array('crsres','upload');
1865: for (var i=0; i<choices.length; i++) {
1866: if (document.getElementById('chooser_'+element+'_'+choices[i])) {
1867: document.getElementById('chooser_'+element+'_'+choices[i]).style.display = 'none';
1868: }
1869: }
1870: }
1871:
1872: function toggleNewsubdir(form,element) {
1873: var newsub = form.elements['newsubdir_'+element];
1874: if (newsub) {
1875: if (newsub.length) {
1876: for (var j=0; j<newsub.length; j++) {
1877: if (newsub[j].checked) {
1878: if (document.getElementById('newsubdirname_'+element)) {
1879: if (newsub[j].value == '1') {
1880: document.getElementById('newsubdirname_'+element).type = "text";
1881: if (document.getElementById('newsubdir_'+element)) {
1882: document.getElementById('newsubdir_'+element).innerHTML = '<br />$js_lt{sunm}';
1883: }
1884: } else {
1885: document.getElementById('newsubdirname_'+element).type = "hidden";
1886: document.getElementById('newsubdirname_'+element).value = "";
1887: document.getElementById('newsubdir_'+element).innerHTML = "";
1888: }
1889: }
1890: break;
1891: }
1892: }
1893: }
1894: }
1895: }
1896:
1897: function updateCrsFile(form,element) {
1898: var directory = form.elements['coursepath_'+element];
1899: var filename = form.elements['coursefile_'+element];
1900: var path = directory.options[directory.selectedIndex].value;
1901: var file = filename.options[filename.selectedIndex].value;
1902: form.elements[element].value = '$respath';
1903: if (path == '/') {
1904: form.elements[element].value += file;
1905: } else {
1906: form.elements[element].value += path+'/'+file;
1907: }
1908: unClean();
1909: if (document.getElementById('previewimg_'+element)) {
1910: document.getElementById('previewimg_'+element).src = form.elements[element].value;
1911: var newsrc = document.getElementById('previewimg_'+element).src;
1912: }
1913: if (document.getElementById('showimg_'+element)) {
1914: document.getElementById('showimg_'+element).innerHTML = '($js_lt{save})';
1915: }
1916: toggleChooser(form,element);
1917: return;
1918: }
1919:
1920: function uploadDone(suffix,name) {
1921: if (name) {
1922: document.forms["lonhomework"].elements[suffix].value = name;
1923: unClean();
1924: toggleChooser(document.forms["lonhomework"],suffix);
1925: }
1926: }
1927:
1928: \$(document).ready(function(){
1929:
1930: \$(document).delegate('form :submit', 'click', function( event ) {
1931: if ( \$( this ).hasClass( "LC_uploadcrsres" ) ) {
1932: var buttonId = this.id;
1933: var suffix = buttonId.toString();
1934: suffix = suffix.replace(/^crsupload_/,'');
1935: event.preventDefault();
1936: document.lonhomework.target = 'crsupload_target_'+suffix;
1937: document.lonhomework.action = '/adm/coursepub?LC_uploadcrsres='+suffix;
1938: \$(this.form).submit();
1939: document.lonhomework.target = '';
1940: if (document.getElementById('crsuploadto_'+suffix)) {
1941: document.lonhomework.action = document.getElementById('crsuploadto_'+suffix).value;
1942: }
1943: return false;
1944: }
1945: });
1946: });
1947: END
1948: }
1.1205 golterma 1949: return <<"COLORFULEDIT"
1950: <script type="text/javascript">
1951: // <![CDATA[>
1952: function fold_box(curDepth, lastresource){
1953:
1954: // we need a list because there can be several blocks you need to fold in one tag
1955: var block = document.getElementsByName('foldblock_'+curDepth);
1956: // but there is only one folding button per tag
1957: var foldbutton = document.getElementById('folding_btn_'+curDepth);
1958:
1959: if(block.item(0).style.display == 'none'){
1960:
1961: foldbutton.value = '@{[&mt("Hide")]}';
1962: for (i = 0; i < block.length; i++){
1963: block.item(i).style.display = '';
1964: }
1965: }else{
1966:
1967: foldbutton.value = '@{[&mt("Show")]}';
1968: for (i = 0; i < block.length; i++){
1969: // block.item(i).style.visibility = 'collapse';
1970: block.item(i).style.display = 'none';
1971: }
1972: };
1973: saveState(lastresource);
1974: }
1975:
1976: function saveState (lastresource) {
1977:
1978: var tag_list = getTagList();
1979: if(tag_list != null){
1980: var timestamp = new Date().getTime();
1981: var key = lastresource;
1982:
1983: // the value pattern is: 'time;key1,value1;key2,value2; ... '
1984: // starting with timestamp
1985: var value = timestamp+';';
1986:
1987: // building the list of key-value pairs
1988: for(var i = 0; i < tag_list.length; i++){
1989: value += tag_list[i]+',';
1990: value += document.getElementsByName(tag_list[i])[0].style.display+';';
1991: }
1992:
1993: // only iterate whole storage if nothing to override
1994: if(localStorage.getItem(key) == null){
1995:
1996: // prevent storage from growing large
1997: if(localStorage.length > 50){
1998: var regex_getTimestamp = /^(?:\d)+;/;
1999: var oldest_timestamp = regex_getTimestamp.exec(localStorage.key(0));
2000: var oldest_key;
2001:
2002: for(var i = 1; i < localStorage.length; i++){
2003: if (regex_getTimestamp.exec(localStorage.key(i)) < oldest_timestamp) {
2004: oldest_key = localStorage.key(i);
2005: oldest_timestamp = regex_getTimestamp.exec(oldest_key);
2006: }
2007: }
2008: localStorage.removeItem(oldest_key);
2009: }
2010: }
2011: localStorage.setItem(key,value);
2012: }
2013: }
2014:
2015: // restore folding status of blocks (on page load)
2016: function restoreState (lastresource) {
2017: if(localStorage.getItem(lastresource) != null){
2018: var key = lastresource;
2019: var value = localStorage.getItem(key);
2020: var regex_delTimestamp = /^\d+;/;
2021:
2022: value.replace(regex_delTimestamp, '');
2023:
2024: var valueArr = value.split(';');
2025: var pairs;
2026: var elements;
2027: for (var i = 0; i < valueArr.length; i++){
2028: pairs = valueArr[i].split(',');
2029: elements = document.getElementsByName(pairs[0]);
2030:
2031: for (var j = 0; j < elements.length; j++){
2032: elements[j].style.display = pairs[1];
2033: if (pairs[1] == "none"){
2034: var regex_id = /([_\\d]+)\$/;
2035: regex_id.exec(pairs[0]);
2036: document.getElementById("folding_btn"+RegExp.\$1).value = "Show";
2037: }
2038: }
2039: }
2040: }
2041: }
2042:
2043: function getTagList () {
2044:
2045: var stringToSearch = document.lonhomework.innerHTML;
2046:
2047: var ret = new Array();
2048: var regex_findBlock = /(foldblock_.*?)"/g;
2049: var tag_list = stringToSearch.match(regex_findBlock);
2050:
2051: if(tag_list != null){
2052: for(var i = 0; i < tag_list.length; i++){
2053: ret.push(tag_list[i].replace(/"/, ''));
2054: }
2055: }
2056: return ret;
2057: }
2058:
2059: function saveScrollPosition (resource) {
2060: var tag_list = getTagList();
2061:
2062: // we dont always want to jump to the first block
2063: // 170 is roughly above the "Problem Editing" header. we just want to save if the user scrolled down further than this
2064: if(\$(window).scrollTop() > 170){
2065: if(tag_list != null){
2066: var result;
2067: for(var i = 0; i < tag_list.length; i++){
2068: if(isElementInViewport(tag_list[i])){
2069: result += tag_list[i]+';';
2070: }
2071: }
2072: sessionStorage.setItem('anchor_'+resource, result);
2073: }
2074: } else {
2075: // we dont need to save zero, just delete the item to leave everything tidy
2076: sessionStorage.removeItem('anchor_'+resource);
2077: }
2078: }
2079:
2080: function restoreScrollPosition(resource){
2081:
2082: var elem = sessionStorage.getItem('anchor_'+resource);
2083: if(elem != null){
2084: var tag_list = elem.split(';');
2085: var elem_list;
2086:
2087: for(var i = 0; i < tag_list.length; i++){
2088: elem_list = document.getElementsByName(tag_list[i]);
2089:
2090: if(elem_list.length > 0){
2091: elem = elem_list[0];
2092: break;
2093: }
2094: }
2095: elem.scrollIntoView();
2096: }
2097: }
2098:
2099: function isElementInViewport(el) {
2100:
2101: // change to last element instead of first
2102: var elem = document.getElementsByName(el);
2103: var rect = elem[0].getBoundingClientRect();
2104:
2105: return (
2106: rect.top >= 0 &&
2107: rect.left >= 0 &&
2108: rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
2109: rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
2110: );
2111: }
2112:
2113: function autosize(depth){
2114: var cmInst = window['cm'+depth];
2115: var fitsizeButton = document.getElementById('fitsize'+depth);
2116:
2117: // is fixed size, switching to dynamic
2118: if (sessionStorage.getItem("autosized_"+depth) == null) {
2119: cmInst.setSize("","auto");
2120: fitsizeButton.value = "@{[&mt('Fixed size')]}";
2121: sessionStorage.setItem("autosized_"+depth, "yes");
2122:
2123: // is dynamic size, switching to fixed
2124: } else {
2125: cmInst.setSize("","300px");
2126: fitsizeButton.value = "@{[&mt('Dynamic size')]}";
2127: sessionStorage.removeItem("autosized_"+depth);
2128: }
2129: }
2130:
1.1248 raeburn 2131: $browse_or_search
1.1205 golterma 2132:
2133: // ]]>
2134: </script>
2135: COLORFULEDIT
2136: }
2137:
2138: sub xmleditor_js {
2139: return <<XMLEDIT
2140: <script type="text/javascript" src="/adm/jQuery/addons/jquery-scrolltofixed.js"></script>
2141: <script type="text/javascript">
2142: // <![CDATA[>
2143:
2144: function saveScrollPosition (resource) {
2145:
2146: var scrollPos = \$(window).scrollTop();
2147: sessionStorage.setItem(resource,scrollPos);
2148: }
2149:
2150: function restoreScrollPosition(resource){
2151:
2152: var scrollPos = sessionStorage.getItem(resource);
2153: \$(window).scrollTop(scrollPos);
2154: }
2155:
2156: // unless internet explorer
2157: if (!(window.navigator.appName == "Microsoft Internet Explorer" && (document.documentMode || document.compatMode))){
2158:
2159: \$(document).ready(function() {
2160: \$(".LC_edit_actionbar").scrollToFixed(\{zIndex: 100\});
2161: });
2162: }
2163:
2164: // inserts text at cursor position into codemirror (xml editor only)
2165: function insertText(text){
2166: cm.focus();
2167: var curPos = cm.getCursor();
2168: cm.replaceRange(text.replace(/ESCAPEDSCRIPT/g,'script'), {line: curPos.line,ch: curPos.ch});
2169: }
2170: // ]]>
2171: </script>
2172: XMLEDIT
2173: }
2174:
2175: sub insert_folding_button {
2176: my $curDepth = $Apache::lonxml::curdepth;
2177: my $lastresource = $env{'request.ambiguous'};
2178:
2179: return "<input type=\"button\" id=\"folding_btn_$curDepth\"
2180: value=\"".&mt('Hide')."\" onclick=\"fold_box('$curDepth','$lastresource')\">";
2181: }
2182:
1.1248 raeburn 2183: sub crsauthor_url {
2184: my ($url) = @_;
2185: if ($url eq '') {
2186: $url = $ENV{'REQUEST_URI'};
2187: }
2188: my ($cnum,$cdom);
2189: if ($env{'request.course.id'}) {
2190: my ($audom,$auname) = ($url =~ m{^/priv/($match_domain)/($match_name)/});
2191: if ($audom ne '' && $auname ne '') {
2192: if (($env{'course.'.$env{'request.course.id'}.'.num'} eq $auname) &&
2193: ($env{'course.'.$env{'request.course.id'}.'.domain'} eq $audom)) {
2194: $cnum = $auname;
2195: $cdom = $audom;
2196: }
2197: }
2198: }
2199: return ($cnum,$cdom);
2200: }
2201:
2202: sub import_crsauthor_form {
1.1265 raeburn 2203: my ($form,$firstselectname,$secondselectname,$onchangefirst,$only,$suffix,$disabled) = @_;
1.1248 raeburn 2204: return (0) unless ($env{'request.course.id'});
2205: my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
2206: my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
2207: my $crshome = $env{'course.'.$env{'request.course.id'}.'.home'};
2208: return (0) unless (($cnum ne '') && ($cdom ne ''));
2209: my $londocroot = $Apache::lonnet::perlvar{'lonDocRoot'};
2210: my @ids=&Apache::lonnet::current_machine_ids();
2211: my ($output,$is_home,$relpath,%subdirs,%files,%selimport_menus);
2212:
2213: if (grep(/^\Q$crshome\E$/,@ids)) {
2214: $is_home = 1;
2215: }
2216: $relpath = "/priv/$cdom/$cnum";
2217: &Apache::lonnet::recursedirs($is_home,'priv',$londocroot,$relpath,'',\%subdirs,\%files);
2218: my %lt = &Apache::lonlocal::texthash (
2219: fnam => 'Filename',
2220: dire => 'Directory',
2221: );
2222: my $numdirs = scalar(keys(%files));
2223: my (%possexts,$singledir,@singledirfiles);
2224: if ($only) {
2225: map { $possexts{$_} = 1; } split(/\s*,\s*/,$only);
2226: }
2227: my (%nonemptydirs,$possdirs);
2228: if ($numdirs > 1) {
2229: my @order;
2230: foreach my $key (sort { lc($a) cmp lc($b) } (keys(%files))) {
2231: if (ref($files{$key}) eq 'HASH') {
2232: my $shown = $key;
2233: if ($key eq '') {
2234: $shown = '/';
2235: }
2236: my @ordered = ();
2237: foreach my $file (sort { lc($a) cmp lc($b) } (keys(%{$files{$key}}))) {
1.1315 raeburn 2238: next if ($file =~ /\.rights$/);
1.1248 raeburn 2239: if ($only) {
2240: my ($ext) = ($file =~ /\.([^.]+)$/);
2241: unless ($possexts{lc($ext)}) {
2242: next;
2243: }
2244: }
2245: $selimport_menus{$key}->{'select2'}->{$file} = $file;
2246: push(@ordered,$file);
2247: }
2248: if (@ordered) {
2249: push(@order,$key);
2250: $nonemptydirs{$key} = 1;
2251: $selimport_menus{$key}->{'text'} = $shown;
2252: $selimport_menus{$key}->{'default'} = '';
2253: $selimport_menus{$key}->{'select2'}->{''} = '';
2254: $selimport_menus{$key}->{'order'} = \@ordered;
2255: }
2256: }
2257: }
2258: $possdirs = scalar(keys(%nonemptydirs));
2259: if ($possdirs > 1) {
2260: my @order = sort { lc($a) cmp lc($b) } (keys(%nonemptydirs));
2261: $output = $lt{'dire'}.
2262: &linked_select_forms($form,'<br />'.
2263: $lt{'fnam'},'',
2264: $firstselectname,$secondselectname,
2265: \%selimport_menus,\@order,
2266: $onchangefirst,'',$suffix).'<br />';
2267: } elsif ($possdirs == 1) {
2268: $singledir = (keys(%nonemptydirs))[0];
2269: if (ref($selimport_menus{$singledir}->{'order'}) eq 'ARRAY') {
2270: @singledirfiles = @{$selimport_menus{$singledir}->{'order'}};
2271: }
2272: delete($selimport_menus{$singledir});
2273: }
2274: } elsif ($numdirs == 1) {
2275: $singledir = (keys(%files))[0];
2276: foreach my $file (sort { lc($a) cmp lc($b) } (keys(%{$files{$singledir}}))) {
2277: if ($only) {
2278: my ($ext) = ($file =~ /\.([^.]+)$/);
2279: unless ($possexts{lc($ext)}) {
2280: next;
2281: }
1.1315 raeburn 2282: } else {
2283: next if ($file =~ /\.rights$/);
1.1248 raeburn 2284: }
2285: push(@singledirfiles,$file);
2286: }
2287: if (@singledirfiles) {
1.1315 raeburn 2288: $possdirs = 1;
1.1248 raeburn 2289: }
2290: }
2291: if (($possdirs == 1) && (@singledirfiles)) {
2292: my $showdir = $singledir;
2293: if ($singledir eq '') {
2294: $showdir = '/';
2295: }
2296: $output = $lt{'dire'}.
2297: '<select name="'.$firstselectname.'">'.
2298: '<option value="'.$singledir.'">'.$showdir.'</option>'."\n".
2299: '</select><br />'.
2300: $lt{'fnam'}.'<select name="'.$secondselectname.'">'."\n".
2301: '<option value="" selected="selected">'.$lt{'se'}.'</option>'."\n";
2302: foreach my $file (@singledirfiles) {
2303: $output .= '<option value="'.$file.'">'.$file.'</option>'."\n";
2304: }
2305: $output .= '</select><br />'."\n";
2306: }
2307: return ($possdirs,$output);
2308: }
2309:
1.565 albertel 2310: =pod
2311:
1.256 matthew 2312: =head1 Excel and CSV file utility routines
2313:
2314: =cut
2315:
2316: ###############################################################
2317: ###############################################################
2318:
2319: =pod
2320:
1.1162 raeburn 2321: =over 4
2322:
1.648 raeburn 2323: =item * &csv_translate($text)
1.37 matthew 2324:
1.185 www 2325: Translate $text to allow it to be output as a 'comma separated values'
1.37 matthew 2326: format.
2327:
2328: =cut
2329:
1.180 matthew 2330: ###############################################################
2331: ###############################################################
1.37 matthew 2332: sub csv_translate {
2333: my $text = shift;
2334: $text =~ s/\"/\"\"/g;
1.209 albertel 2335: $text =~ s/\n/ /g;
1.37 matthew 2336: return $text;
2337: }
1.180 matthew 2338:
2339: ###############################################################
2340: ###############################################################
2341:
2342: =pod
2343:
1.648 raeburn 2344: =item * &define_excel_formats()
1.180 matthew 2345:
2346: Define some commonly used Excel cell formats.
2347:
2348: Currently supported formats:
2349:
2350: =over 4
2351:
2352: =item header
2353:
2354: =item bold
2355:
2356: =item h1
2357:
2358: =item h2
2359:
2360: =item h3
2361:
1.256 matthew 2362: =item h4
2363:
2364: =item i
2365:
1.180 matthew 2366: =item date
2367:
2368: =back
2369:
2370: Inputs: $workbook
2371:
2372: Returns: $format, a hash reference.
2373:
1.1057 foxr 2374:
1.180 matthew 2375: =cut
2376:
2377: ###############################################################
2378: ###############################################################
2379: sub define_excel_formats {
2380: my ($workbook) = @_;
2381: my $format;
2382: $format->{'header'} = $workbook->add_format(bold => 1,
2383: bottom => 1,
2384: align => 'center');
2385: $format->{'bold'} = $workbook->add_format(bold=>1);
2386: $format->{'h1'} = $workbook->add_format(bold=>1, size=>18);
2387: $format->{'h2'} = $workbook->add_format(bold=>1, size=>16);
2388: $format->{'h3'} = $workbook->add_format(bold=>1, size=>14);
1.255 matthew 2389: $format->{'h4'} = $workbook->add_format(bold=>1, size=>12);
1.246 matthew 2390: $format->{'i'} = $workbook->add_format(italic=>1);
1.180 matthew 2391: $format->{'date'} = $workbook->add_format(num_format=>
1.207 matthew 2392: 'mm/dd/yyyy hh:mm:ss');
1.180 matthew 2393: return $format;
2394: }
2395:
2396: ###############################################################
2397: ###############################################################
1.113 bowersj2 2398:
2399: =pod
2400:
1.648 raeburn 2401: =item * &create_workbook()
1.255 matthew 2402:
2403: Create an Excel worksheet. If it fails, output message on the
2404: request object and return undefs.
2405:
2406: Inputs: Apache request object
2407:
2408: Returns (undef) on failure,
2409: Excel worksheet object, scalar with filename, and formats
2410: from &Apache::loncommon::define_excel_formats on success
2411:
2412: =cut
2413:
2414: ###############################################################
2415: ###############################################################
2416: sub create_workbook {
2417: my ($r) = @_;
2418: #
2419: # Create the excel spreadsheet
2420: my $filename = '/prtspool/'.
1.258 albertel 2421: $env{'user.name'}.'_'.$env{'user.domain'}.'_'.
1.255 matthew 2422: time.'_'.rand(1000000000).'.xls';
2423: my $workbook = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
2424: if (! defined($workbook)) {
2425: $r->log_error("Error creating excel spreadsheet $filename: $!");
1.928 bisitz 2426: $r->print(
2427: '<p class="LC_error">'
2428: .&mt('Problems occurred in creating the new Excel file.')
2429: .' '.&mt('This error has been logged.')
2430: .' '.&mt('Please alert your LON-CAPA administrator.')
2431: .'</p>'
2432: );
1.255 matthew 2433: return (undef);
2434: }
2435: #
1.1014 foxr 2436: $workbook->set_tempdir(LONCAPA::tempdir());
1.255 matthew 2437: #
2438: my $format = &Apache::loncommon::define_excel_formats($workbook);
2439: return ($workbook,$filename,$format);
2440: }
2441:
2442: ###############################################################
2443: ###############################################################
2444:
2445: =pod
2446:
1.648 raeburn 2447: =item * &create_text_file()
1.113 bowersj2 2448:
1.542 raeburn 2449: Create a file to write to and eventually make available to the user.
1.256 matthew 2450: If file creation fails, outputs an error message on the request object and
2451: return undefs.
1.113 bowersj2 2452:
1.256 matthew 2453: Inputs: Apache request object, and file suffix
1.113 bowersj2 2454:
1.256 matthew 2455: Returns (undef) on failure,
2456: Filehandle and filename on success.
1.113 bowersj2 2457:
2458: =cut
2459:
1.256 matthew 2460: ###############################################################
2461: ###############################################################
2462: sub create_text_file {
2463: my ($r,$suffix) = @_;
2464: if (! defined($suffix)) { $suffix = 'txt'; };
2465: my $fh;
2466: my $filename = '/prtspool/'.
1.258 albertel 2467: $env{'user.name'}.'_'.$env{'user.domain'}.'_'.
1.256 matthew 2468: time.'_'.rand(1000000000).'.'.$suffix;
2469: $fh = Apache::File->new('>/home/httpd'.$filename);
2470: if (! defined($fh)) {
2471: $r->log_error("Couldn't open $filename for output $!");
1.928 bisitz 2472: $r->print(
2473: '<p class="LC_error">'
2474: .&mt('Problems occurred in creating the output file.')
2475: .' '.&mt('This error has been logged.')
2476: .' '.&mt('Please alert your LON-CAPA administrator.')
2477: .'</p>'
2478: );
1.113 bowersj2 2479: }
1.256 matthew 2480: return ($fh,$filename)
1.113 bowersj2 2481: }
2482:
2483:
1.256 matthew 2484: =pod
1.113 bowersj2 2485:
2486: =back
2487:
2488: =cut
1.37 matthew 2489:
2490: ###############################################################
1.33 matthew 2491: ## Home server <option> list generating code ##
2492: ###############################################################
1.35 matthew 2493:
1.169 www 2494: # ------------------------------------------
2495:
2496: sub domain_select {
1.1289 raeburn 2497: my ($name,$value,$multiple,$incdoms,$excdoms)=@_;
2498: my @possdoms;
2499: if (ref($incdoms) eq 'ARRAY') {
2500: @possdoms = @{$incdoms};
2501: } else {
2502: @possdoms = &Apache::lonnet::all_domains();
2503: }
2504:
1.169 www 2505: my %domains=map {
1.514 albertel 2506: $_ => $_.' '. &Apache::lonnet::domain($_,'description')
1.1289 raeburn 2507: } @possdoms;
2508:
2509: if ((ref($excdoms) eq 'ARRAY') && (@{$excdoms} > 0)) {
2510: foreach my $dom (@{$excdoms}) {
2511: delete($domains{$dom});
2512: }
2513: }
2514:
1.169 www 2515: if ($multiple) {
2516: $domains{''}=&mt('Any domain');
1.550 albertel 2517: $domains{'select_form_order'} = [sort {lc($a) cmp lc($b) } (keys(%domains))];
1.287 albertel 2518: return &multiple_select_form($name,$value,4,\%domains);
1.169 www 2519: } else {
1.550 albertel 2520: $domains{'select_form_order'} = [sort {lc($a) cmp lc($b) } (keys(%domains))];
1.970 raeburn 2521: return &select_form($name,$value,\%domains);
1.169 www 2522: }
2523: }
2524:
1.282 albertel 2525: #-------------------------------------------
2526:
2527: =pod
2528:
1.519 raeburn 2529: =head1 Routines for form select boxes
2530:
2531: =over 4
2532:
1.648 raeburn 2533: =item * &multiple_select_form($name,$value,$size,$hash,$order)
1.282 albertel 2534:
2535: Returns a string containing a <select> element int multiple mode
2536:
2537:
2538: Args:
2539: $name - name of the <select> element
1.506 raeburn 2540: $value - scalar or array ref of values that should already be selected
1.282 albertel 2541: $size - number of rows long the select element is
1.283 albertel 2542: $hash - the elements should be 'option' => 'shown text'
1.282 albertel 2543: (shown text should already have been &mt())
1.506 raeburn 2544: $order - (optional) array ref of the order to show the elements in
1.283 albertel 2545:
1.282 albertel 2546: =cut
2547:
2548: #-------------------------------------------
1.169 www 2549: sub multiple_select_form {
1.284 albertel 2550: my ($name,$value,$size,$hash,$order)=@_;
1.169 www 2551: my %selected = map { $_ => 1 } ref($value)?@{$value}:($value);
2552: my $output='';
1.191 matthew 2553: if (! defined($size)) {
2554: $size = 4;
1.283 albertel 2555: if (scalar(keys(%$hash))<4) {
2556: $size = scalar(keys(%$hash));
1.191 matthew 2557: }
2558: }
1.734 bisitz 2559: $output.="\n".'<select name="'.$name.'" size="'.$size.'" multiple="multiple">';
1.501 banghart 2560: my @order;
1.506 raeburn 2561: if (ref($order) eq 'ARRAY') {
2562: @order = @{$order};
2563: } else {
2564: @order = sort(keys(%$hash));
1.501 banghart 2565: }
2566: if (exists($$hash{'select_form_order'})) {
2567: @order = @{$$hash{'select_form_order'}};
2568: }
2569:
1.284 albertel 2570: foreach my $key (@order) {
1.356 albertel 2571: $output.='<option value="'.&HTML::Entities::encode($key,'"<>&').'" ';
1.284 albertel 2572: $output.='selected="selected" ' if ($selected{$key});
2573: $output.='>'.$hash->{$key}."</option>\n";
1.169 www 2574: }
2575: $output.="</select>\n";
2576: return $output;
2577: }
2578:
1.88 www 2579: #-------------------------------------------
2580:
2581: =pod
2582:
1.1254 raeburn 2583: =item * &select_form($defdom,$name,$hashref,$onchange,$readonly)
1.88 www 2584:
2585: Returns a string containing a <select name='$name' size='1'> form to
1.970 raeburn 2586: allow a user to select options from a ref to a hash containing:
2587: option_name => displayed text. An optional $onchange can include
1.1254 raeburn 2588: a javascript onchange item, e.g., onchange="this.form.submit();".
2589: An optional arg -- $readonly -- if true will cause the select form
2590: to be disabled, e.g., for the case where an instructor has a section-
2591: specific role, and is viewing/modifying parameters.
1.970 raeburn 2592:
1.88 www 2593: See lonrights.pm for an example invocation and use.
2594:
2595: =cut
2596:
2597: #-------------------------------------------
2598: sub select_form {
1.1228 raeburn 2599: my ($def,$name,$hashref,$onchange,$readonly) = @_;
1.970 raeburn 2600: return unless (ref($hashref) eq 'HASH');
2601: if ($onchange) {
2602: $onchange = ' onchange="'.$onchange.'"';
2603: }
1.1228 raeburn 2604: my $disabled;
2605: if ($readonly) {
2606: $disabled = ' disabled="disabled"';
2607: }
2608: my $selectform = "<select name=\"$name\" size=\"1\"$onchange$disabled>\n";
1.128 albertel 2609: my @keys;
1.970 raeburn 2610: if (exists($hashref->{'select_form_order'})) {
2611: @keys=@{$hashref->{'select_form_order'}};
1.128 albertel 2612: } else {
1.970 raeburn 2613: @keys=sort(keys(%{$hashref}));
1.128 albertel 2614: }
1.356 albertel 2615: foreach my $key (@keys) {
2616: $selectform.=
2617: '<option value="'.&HTML::Entities::encode($key,'"<>&').'" '.
2618: ($key eq $def ? 'selected="selected" ' : '').
1.970 raeburn 2619: ">".$hashref->{$key}."</option>\n";
1.88 www 2620: }
2621: $selectform.="</select>";
2622: return $selectform;
2623: }
2624:
1.475 www 2625: # For display filters
2626:
2627: sub display_filter {
1.1074 raeburn 2628: my ($context) = @_;
1.475 www 2629: if (!$env{'form.show'}) { $env{'form.show'}=10; }
1.477 www 2630: if (!$env{'form.displayfilter'}) { $env{'form.displayfilter'}='currentfolder'; }
1.1074 raeburn 2631: my $phraseinput = 'hidden';
2632: my $includeinput = 'hidden';
2633: my ($checked,$includetypestext);
2634: if ($env{'form.displayfilter'} eq 'containing') {
2635: $phraseinput = 'text';
2636: if ($context eq 'parmslog') {
2637: $includeinput = 'checkbox';
2638: if ($env{'form.includetypes'}) {
2639: $checked = ' checked="checked"';
2640: }
2641: $includetypestext = &mt('Include parameter types');
2642: }
2643: } else {
2644: $includetypestext = ' ';
2645: }
2646: my ($additional,$secondid,$thirdid);
2647: if ($context eq 'parmslog') {
2648: $additional =
2649: '<label><input type="'.$includeinput.'" name="includetypes"'.
2650: $checked.' name="includetypes" value="1" id="includetypes" />'.
2651: ' <span id="includetypestext">'.$includetypestext.'</span>'.
2652: '</label>';
2653: $secondid = 'includetypes';
2654: $thirdid = 'includetypestext';
2655: }
2656: my $onchange = "javascript:toggleHistoryOptions(this,'containingphrase','$context',
2657: '$secondid','$thirdid')";
2658: return '<span class="LC_nobreak"><label>'.&mt('Records: [_1]',
1.475 www 2659: &Apache::lonmeta::selectbox('show',$env{'form.show'},undef,
2660: (&mt('all'),10,20,50,100,1000,10000))).
1.714 bisitz 2661: '</label></span> <span class="LC_nobreak">'.
1.1074 raeburn 2662: &mt('Filter: [_1]',
1.477 www 2663: &select_form($env{'form.displayfilter'},
2664: 'displayfilter',
1.970 raeburn 2665: {'currentfolder' => 'Current folder/page',
1.477 www 2666: 'containing' => 'Containing phrase',
1.1074 raeburn 2667: 'none' => 'None'},$onchange)).' '.
2668: '<input type="'.$phraseinput.'" name="containingphrase" id="containingphrase" size="30" value="'.
2669: &HTML::Entities::encode($env{'form.containingphrase'}).
2670: '" />'.$additional;
2671: }
2672:
2673: sub display_filter_js {
2674: my $includetext = &mt('Include parameter types');
2675: return <<"ENDJS";
2676:
2677: function toggleHistoryOptions(setter,firstid,context,secondid,thirdid) {
2678: var firstType = 'hidden';
2679: if (setter.options[setter.selectedIndex].value == 'containing') {
2680: firstType = 'text';
2681: }
2682: firstObject = document.getElementById(firstid);
2683: if (typeof(firstObject) == 'object') {
2684: if (firstObject.type != firstType) {
2685: changeInputType(firstObject,firstType);
2686: }
2687: }
2688: if (context == 'parmslog') {
2689: var secondType = 'hidden';
2690: if (firstType == 'text') {
2691: secondType = 'checkbox';
2692: }
2693: secondObject = document.getElementById(secondid);
2694: if (typeof(secondObject) == 'object') {
2695: if (secondObject.type != secondType) {
2696: changeInputType(secondObject,secondType);
2697: }
2698: }
2699: var textItem = document.getElementById(thirdid);
2700: var currtext = textItem.innerHTML;
2701: var newtext;
2702: if (firstType == 'text') {
2703: newtext = '$includetext';
2704: } else {
2705: newtext = ' ';
2706: }
2707: if (currtext != newtext) {
2708: textItem.innerHTML = newtext;
2709: }
2710: }
2711: return;
2712: }
2713:
2714: function changeInputType(oldObject,newType) {
2715: var newObject = document.createElement('input');
2716: newObject.type = newType;
2717: if (oldObject.size) {
2718: newObject.size = oldObject.size;
2719: }
2720: if (oldObject.value) {
2721: newObject.value = oldObject.value;
2722: }
2723: if (oldObject.name) {
2724: newObject.name = oldObject.name;
2725: }
2726: if (oldObject.id) {
2727: newObject.id = oldObject.id;
2728: }
2729: oldObject.parentNode.replaceChild(newObject,oldObject);
2730: return;
2731: }
2732:
2733: ENDJS
1.475 www 2734: }
2735:
1.167 www 2736: sub gradeleveldescription {
2737: my $gradelevel=shift;
2738: my %gradelevels=(0 => 'Not specified',
2739: 1 => 'Grade 1',
2740: 2 => 'Grade 2',
2741: 3 => 'Grade 3',
2742: 4 => 'Grade 4',
2743: 5 => 'Grade 5',
2744: 6 => 'Grade 6',
2745: 7 => 'Grade 7',
2746: 8 => 'Grade 8',
2747: 9 => 'Grade 9',
2748: 10 => 'Grade 10',
2749: 11 => 'Grade 11',
2750: 12 => 'Grade 12',
2751: 13 => 'Grade 13',
2752: 14 => '100 Level',
2753: 15 => '200 Level',
2754: 16 => '300 Level',
2755: 17 => '400 Level',
2756: 18 => 'Graduate Level');
2757: return &mt($gradelevels{$gradelevel});
2758: }
2759:
1.163 www 2760: sub select_level_form {
2761: my ($deflevel,$name)=@_;
2762: unless ($deflevel) { $deflevel=0; }
1.167 www 2763: my $selectform = "<select name=\"$name\" size=\"1\">\n";
2764: for (my $i=0; $i<=18; $i++) {
2765: $selectform.="<option value=\"$i\" ".
1.253 albertel 2766: ($i==$deflevel ? 'selected="selected" ' : '').
1.167 www 2767: ">".&gradeleveldescription($i)."</option>\n";
2768: }
2769: $selectform.="</select>";
2770: return $selectform;
1.163 www 2771: }
1.167 www 2772:
1.35 matthew 2773: #-------------------------------------------
2774:
1.45 matthew 2775: =pod
2776:
1.1256 raeburn 2777: =item * &select_dom_form($defdom,$name,$includeempty,$showdomdesc,$onchange,$incdoms,$excdoms,$disabled)
1.35 matthew 2778:
2779: Returns a string containing a <select name='$name' size='1'> form to
2780: allow a user to select the domain to preform an operation in.
2781: See loncreateuser.pm for an example invocation and use.
2782:
1.90 www 2783: If the $includeempty flag is set, it also includes an empty choice ("no domain
2784: selected");
2785:
1.743 raeburn 2786: If the $showdomdesc flag is set, the domain name is followed by the domain description.
2787:
1.910 raeburn 2788: 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.
2789:
1.1121 raeburn 2790: The optional $incdoms is a reference to an array of domains which will be the only available options.
2791:
2792: The optional $excdoms is a reference to an array of domains which will be excluded from the available options.
1.563 raeburn 2793:
1.1256 raeburn 2794: The optional $disabled argument, if true, adds the disabled attribute to the select tag.
2795:
1.35 matthew 2796: =cut
2797:
2798: #-------------------------------------------
1.34 matthew 2799: sub select_dom_form {
1.1256 raeburn 2800: my ($defdom,$name,$includeempty,$showdomdesc,$onchange,$incdoms,$excdoms,$disabled) = @_;
1.872 raeburn 2801: if ($onchange) {
1.874 raeburn 2802: $onchange = ' onchange="'.$onchange.'"';
1.743 raeburn 2803: }
1.1256 raeburn 2804: if ($disabled) {
2805: $disabled = ' disabled="disabled"';
2806: }
1.1121 raeburn 2807: my (@domains,%exclude);
1.910 raeburn 2808: if (ref($incdoms) eq 'ARRAY') {
2809: @domains = sort {lc($a) cmp lc($b)} (@{$incdoms});
2810: } else {
2811: @domains = sort {lc($a) cmp lc($b)} (&Apache::lonnet::all_domains());
2812: }
1.90 www 2813: if ($includeempty) { @domains=('',@domains); }
1.1121 raeburn 2814: if (ref($excdoms) eq 'ARRAY') {
2815: map { $exclude{$_} = 1; } @{$excdoms};
2816: }
1.1256 raeburn 2817: my $selectdomain = "<select name=\"$name\" size=\"1\"$onchange$disabled>\n";
1.356 albertel 2818: foreach my $dom (@domains) {
1.1121 raeburn 2819: next if ($exclude{$dom});
1.356 albertel 2820: $selectdomain.="<option value=\"$dom\" ".
1.563 raeburn 2821: ($dom eq $defdom ? 'selected="selected" ' : '').'>'.$dom;
2822: if ($showdomdesc) {
2823: if ($dom ne '') {
2824: my $domdesc = &Apache::lonnet::domain($dom,'description');
2825: if ($domdesc ne '') {
2826: $selectdomain .= ' ('.$domdesc.')';
2827: }
2828: }
2829: }
2830: $selectdomain .= "</option>\n";
1.34 matthew 2831: }
2832: $selectdomain.="</select>";
2833: return $selectdomain;
2834: }
2835:
1.35 matthew 2836: #-------------------------------------------
2837:
1.45 matthew 2838: =pod
2839:
1.648 raeburn 2840: =item * &home_server_form_item($domain,$name,$defaultflag)
1.35 matthew 2841:
1.586 raeburn 2842: input: 4 arguments (two required, two optional) -
2843: $domain - domain of new user
2844: $name - name of form element
2845: $default - Value of 'default' causes a default item to be first
2846: option, and selected by default.
2847: $hide - Value of 'hide' causes hiding of the name of the server,
2848: if 1 server found, or default, if 0 found.
1.594 raeburn 2849: output: returns 2 items:
1.586 raeburn 2850: (a) form element which contains either:
2851: (i) <select name="$name">
2852: <option value="$hostid1">$hostid $servers{$hostid}</option>
2853: <option value="$hostid2">$hostid $servers{$hostid}</option>
2854: </select>
2855: form item if there are multiple library servers in $domain, or
2856: (ii) an <input type="hidden" name="$name" value="$hostid" /> form item
2857: if there is only one library server in $domain.
2858:
2859: (b) number of library servers found.
2860:
2861: See loncreateuser.pm for example of use.
1.35 matthew 2862:
2863: =cut
2864:
2865: #-------------------------------------------
1.586 raeburn 2866: sub home_server_form_item {
2867: my ($domain,$name,$default,$hide) = @_;
1.513 albertel 2868: my %servers = &Apache::lonnet::get_servers($domain,'library');
1.586 raeburn 2869: my $result;
2870: my $numlib = keys(%servers);
2871: if ($numlib > 1) {
2872: $result .= '<select name="'.$name.'" />'."\n";
2873: if ($default) {
1.804 bisitz 2874: $result .= '<option value="default" selected="selected">'.&mt('default').
1.586 raeburn 2875: '</option>'."\n";
2876: }
2877: foreach my $hostid (sort(keys(%servers))) {
2878: $result.= '<option value="'.$hostid.'">'.
2879: $hostid.' '.$servers{$hostid}."</option>\n";
2880: }
2881: $result .= '</select>'."\n";
2882: } elsif ($numlib == 1) {
2883: my $hostid;
2884: foreach my $item (keys(%servers)) {
2885: $hostid = $item;
2886: }
2887: $result .= '<input type="hidden" name="'.$name.'" value="'.
2888: $hostid.'" />';
2889: if (!$hide) {
2890: $result .= $hostid.' '.$servers{$hostid};
2891: }
2892: $result .= "\n";
2893: } elsif ($default) {
2894: $result .= '<input type="hidden" name="'.$name.
2895: '" value="default" />';
2896: if (!$hide) {
2897: $result .= &mt('default');
2898: }
2899: $result .= "\n";
1.33 matthew 2900: }
1.586 raeburn 2901: return ($result,$numlib);
1.33 matthew 2902: }
1.112 bowersj2 2903:
2904: =pod
2905:
1.534 albertel 2906: =back
2907:
1.112 bowersj2 2908: =cut
1.87 matthew 2909:
2910: ###############################################################
1.112 bowersj2 2911: ## Decoding User Agent ##
1.87 matthew 2912: ###############################################################
2913:
2914: =pod
2915:
1.112 bowersj2 2916: =head1 Decoding the User Agent
2917:
2918: =over 4
2919:
2920: =item * &decode_user_agent()
1.87 matthew 2921:
2922: Inputs: $r
2923:
2924: Outputs:
2925:
2926: =over 4
2927:
1.112 bowersj2 2928: =item * $httpbrowser
1.87 matthew 2929:
1.112 bowersj2 2930: =item * $clientbrowser
1.87 matthew 2931:
1.112 bowersj2 2932: =item * $clientversion
1.87 matthew 2933:
1.112 bowersj2 2934: =item * $clientmathml
1.87 matthew 2935:
1.112 bowersj2 2936: =item * $clientunicode
1.87 matthew 2937:
1.112 bowersj2 2938: =item * $clientos
1.87 matthew 2939:
1.1137 raeburn 2940: =item * $clientmobile
2941:
1.1141 raeburn 2942: =item * $clientinfo
2943:
1.1194 raeburn 2944: =item * $clientosversion
2945:
1.87 matthew 2946: =back
2947:
1.157 matthew 2948: =back
2949:
1.87 matthew 2950: =cut
2951:
2952: ###############################################################
2953: ###############################################################
2954: sub decode_user_agent {
1.247 albertel 2955: my ($r)=@_;
1.87 matthew 2956: my @browsertype=split(/\&/,$Apache::lonnet::perlvar{"lonBrowsDet"});
2957: my %mathcap=split(/\&/,$$Apache::lonnet::perlvar{"lonMathML"});
2958: my $httpbrowser=$ENV{"HTTP_USER_AGENT"};
1.247 albertel 2959: if (!$httpbrowser && $r) { $httpbrowser=$r->header_in('User-Agent'); }
1.87 matthew 2960: my $clientbrowser='unknown';
2961: my $clientversion='0';
2962: my $clientmathml='';
2963: my $clientunicode='0';
1.1137 raeburn 2964: my $clientmobile=0;
1.1194 raeburn 2965: my $clientosversion='';
1.87 matthew 2966: for (my $i=0;$i<=$#browsertype;$i++) {
1.1193 raeburn 2967: my ($bname,$match,$notmatch,$vreg,$minv,$univ)=split(/\%/,$browsertype[$i]);
1.87 matthew 2968: if (($httpbrowser=~/$match/i) && ($httpbrowser!~/$notmatch/i)) {
2969: $clientbrowser=$bname;
2970: $httpbrowser=~/$vreg/i;
2971: $clientversion=$1;
2972: $clientmathml=($clientversion>=$minv);
2973: $clientunicode=($clientversion>=$univ);
2974: }
2975: }
2976: my $clientos='unknown';
1.1141 raeburn 2977: my $clientinfo;
1.87 matthew 2978: if (($httpbrowser=~/linux/i) ||
2979: ($httpbrowser=~/unix/i) ||
2980: ($httpbrowser=~/ux/i) ||
2981: ($httpbrowser=~/solaris/i)) { $clientos='unix'; }
2982: if (($httpbrowser=~/vax/i) ||
2983: ($httpbrowser=~/vms/i)) { $clientos='vms'; }
2984: if ($httpbrowser=~/next/i) { $clientos='next'; }
2985: if (($httpbrowser=~/mac/i) ||
2986: ($httpbrowser=~/powerpc/i)) { $clientos='mac'; }
1.1194 raeburn 2987: if ($httpbrowser=~/win/i) {
2988: $clientos='win';
2989: if ($httpbrowser =~/Windows\s+NT\s+(\d+\.\d+)/i) {
2990: $clientosversion = $1;
2991: }
2992: }
1.87 matthew 2993: if ($httpbrowser=~/embed/i) { $clientos='pda'; }
1.1137 raeburn 2994: if ($httpbrowser=~/(Android|iPod|iPad|iPhone|webOS|Blackberry|Windows Phone|Opera m(?:ob|in)|Fennec)/i) {
2995: $clientmobile=lc($1);
2996: }
1.1141 raeburn 2997: if ($httpbrowser=~ m{Firefox/(\d+\.\d+)}) {
2998: $clientinfo = 'firefox-'.$1;
2999: } elsif ($httpbrowser=~ m{chromeframe/(\d+\.\d+)\.}) {
3000: $clientinfo = 'chromeframe-'.$1;
3001: }
1.87 matthew 3002: return ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,
1.1194 raeburn 3003: $clientunicode,$clientos,$clientmobile,$clientinfo,
3004: $clientosversion);
1.87 matthew 3005: }
3006:
1.32 matthew 3007: ###############################################################
3008: ## Authentication changing form generation subroutines ##
3009: ###############################################################
3010: ##
3011: ## All of the authform_xxxxxxx subroutines take their inputs in a
3012: ## hash, and have reasonable default values.
3013: ##
3014: ## formname = the name given in the <form> tag.
1.35 matthew 3015: #-------------------------------------------
3016:
1.45 matthew 3017: =pod
3018:
1.112 bowersj2 3019: =head1 Authentication Routines
3020:
3021: =over 4
3022:
1.648 raeburn 3023: =item * &authform_xxxxxx()
1.35 matthew 3024:
3025: The authform_xxxxxx subroutines provide javascript and html forms which
3026: handle some of the conveniences required for authentication forms.
3027: This is not an optimal method, but it works.
3028:
3029: =over 4
3030:
1.112 bowersj2 3031: =item * authform_header
1.35 matthew 3032:
1.112 bowersj2 3033: =item * authform_authorwarning
1.35 matthew 3034:
1.112 bowersj2 3035: =item * authform_nochange
1.35 matthew 3036:
1.112 bowersj2 3037: =item * authform_kerberos
1.35 matthew 3038:
1.112 bowersj2 3039: =item * authform_internal
1.35 matthew 3040:
1.112 bowersj2 3041: =item * authform_filesystem
1.35 matthew 3042:
1.1310 raeburn 3043: =item * authform_lti
3044:
1.35 matthew 3045: =back
3046:
1.648 raeburn 3047: See loncreateuser.pm for invocation and use examples.
1.157 matthew 3048:
1.35 matthew 3049: =cut
3050:
3051: #-------------------------------------------
1.32 matthew 3052: sub authform_header{
3053: my %in = (
3054: formname => 'cu',
1.80 albertel 3055: kerb_def_dom => '',
1.32 matthew 3056: @_,
3057: );
3058: $in{'formname'} = 'document.' . $in{'formname'};
3059: my $result='';
1.80 albertel 3060:
3061: #---------------------------------------------- Code for upper case translation
3062: my $Javascript_toUpperCase;
3063: unless ($in{kerb_def_dom}) {
3064: $Javascript_toUpperCase =<<"END";
3065: switch (choice) {
3066: case 'krb': currentform.elements[choicearg].value =
3067: currentform.elements[choicearg].value.toUpperCase();
3068: break;
3069: default:
3070: }
3071: END
3072: } else {
3073: $Javascript_toUpperCase = "";
3074: }
3075:
1.165 raeburn 3076: my $radioval = "'nochange'";
1.591 raeburn 3077: if (defined($in{'curr_authtype'})) {
3078: if ($in{'curr_authtype'} ne '') {
3079: $radioval = "'".$in{'curr_authtype'}."arg'";
3080: }
1.174 matthew 3081: }
1.165 raeburn 3082: my $argfield = 'null';
1.591 raeburn 3083: if (defined($in{'mode'})) {
1.165 raeburn 3084: if ($in{'mode'} eq 'modifycourse') {
1.591 raeburn 3085: if (defined($in{'curr_autharg'})) {
3086: if ($in{'curr_autharg'} ne '') {
1.165 raeburn 3087: $argfield = "'$in{'curr_autharg'}'";
3088: }
3089: }
3090: }
3091: }
3092:
1.32 matthew 3093: $result.=<<"END";
3094: var current = new Object();
1.165 raeburn 3095: current.radiovalue = $radioval;
3096: current.argfield = $argfield;
1.32 matthew 3097:
3098: function changed_radio(choice,currentform) {
3099: var choicearg = choice + 'arg';
3100: // If a radio button in changed, we need to change the argfield
3101: if (current.radiovalue != choice) {
3102: current.radiovalue = choice;
3103: if (current.argfield != null) {
3104: currentform.elements[current.argfield].value = '';
3105: }
3106: if (choice == 'nochange') {
3107: current.argfield = null;
3108: } else {
3109: current.argfield = choicearg;
3110: switch(choice) {
3111: case 'krb':
3112: currentform.elements[current.argfield].value =
3113: "$in{'kerb_def_dom'}";
3114: break;
3115: default:
3116: break;
3117: }
3118: }
3119: }
3120: return;
3121: }
1.22 www 3122:
1.32 matthew 3123: function changed_text(choice,currentform) {
3124: var choicearg = choice + 'arg';
3125: if (currentform.elements[choicearg].value !='') {
1.80 albertel 3126: $Javascript_toUpperCase
1.32 matthew 3127: // clear old field
3128: if ((current.argfield != choicearg) && (current.argfield != null)) {
3129: currentform.elements[current.argfield].value = '';
3130: }
3131: current.argfield = choicearg;
3132: }
3133: set_auth_radio_buttons(choice,currentform);
3134: return;
1.20 www 3135: }
1.32 matthew 3136:
3137: function set_auth_radio_buttons(newvalue,currentform) {
1.986 raeburn 3138: var numauthchoices = currentform.login.length;
3139: if (typeof numauthchoices == "undefined") {
3140: return;
3141: }
1.32 matthew 3142: var i=0;
1.986 raeburn 3143: while (i < numauthchoices) {
1.32 matthew 3144: if (currentform.login[i].value == newvalue) { break; }
3145: i++;
3146: }
1.986 raeburn 3147: if (i == numauthchoices) {
1.32 matthew 3148: return;
3149: }
3150: current.radiovalue = newvalue;
3151: currentform.login[i].checked = true;
3152: return;
3153: }
3154: END
3155: return $result;
3156: }
3157:
1.1106 raeburn 3158: sub authform_authorwarning {
1.32 matthew 3159: my $result='';
1.144 matthew 3160: $result='<i>'.
3161: &mt('As a general rule, only authors or co-authors should be '.
3162: 'filesystem authenticated '.
3163: '(which allows access to the server filesystem).')."</i>\n";
1.32 matthew 3164: return $result;
3165: }
3166:
1.1106 raeburn 3167: sub authform_nochange {
1.32 matthew 3168: my %in = (
3169: formname => 'document.cu',
3170: kerb_def_dom => 'MSU.EDU',
3171: @_,
3172: );
1.1106 raeburn 3173: my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.586 raeburn 3174: my $result;
1.1104 raeburn 3175: if (!$authnum) {
1.1105 raeburn 3176: $result = &mt('Under your current role you are not permitted to change login settings for this user');
1.586 raeburn 3177: } else {
3178: $result = '<label>'.&mt('[_1] Do not change login data',
3179: '<input type="radio" name="login" value="nochange" '.
3180: 'checked="checked" onclick="'.
1.281 albertel 3181: "javascript:changed_radio('nochange',$in{'formname'});".'" />').
3182: '</label>';
1.586 raeburn 3183: }
1.32 matthew 3184: return $result;
3185: }
3186:
1.591 raeburn 3187: sub authform_kerberos {
1.32 matthew 3188: my %in = (
3189: formname => 'document.cu',
3190: kerb_def_dom => 'MSU.EDU',
1.80 albertel 3191: kerb_def_auth => 'krb4',
1.32 matthew 3192: @_,
3193: );
1.586 raeburn 3194: my ($check4,$check5,$krbcheck,$krbarg,$krbver,$result,$authtype,
1.1259 raeburn 3195: $autharg,$jscall,$disabled);
1.1106 raeburn 3196: my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.80 albertel 3197: if ($in{'kerb_def_auth'} eq 'krb5') {
1.772 bisitz 3198: $check5 = ' checked="checked"';
1.80 albertel 3199: } else {
1.772 bisitz 3200: $check4 = ' checked="checked"';
1.80 albertel 3201: }
1.1259 raeburn 3202: if ($in{'readonly'}) {
3203: $disabled = ' disabled="disabled"';
3204: }
1.165 raeburn 3205: $krbarg = $in{'kerb_def_dom'};
1.591 raeburn 3206: if (defined($in{'curr_authtype'})) {
3207: if ($in{'curr_authtype'} eq 'krb') {
1.772 bisitz 3208: $krbcheck = ' checked="checked"';
1.623 raeburn 3209: if (defined($in{'mode'})) {
3210: if ($in{'mode'} eq 'modifyuser') {
3211: $krbcheck = '';
3212: }
3213: }
1.591 raeburn 3214: if (defined($in{'curr_kerb_ver'})) {
3215: if ($in{'curr_krb_ver'} eq '5') {
1.772 bisitz 3216: $check5 = ' checked="checked"';
1.591 raeburn 3217: $check4 = '';
3218: } else {
1.772 bisitz 3219: $check4 = ' checked="checked"';
1.591 raeburn 3220: $check5 = '';
3221: }
1.586 raeburn 3222: }
1.591 raeburn 3223: if (defined($in{'curr_autharg'})) {
1.165 raeburn 3224: $krbarg = $in{'curr_autharg'};
3225: }
1.586 raeburn 3226: if (!$can_assign{'krb4'} && !$can_assign{'krb5'}) {
1.591 raeburn 3227: if (defined($in{'curr_autharg'})) {
1.586 raeburn 3228: $result =
3229: &mt('Currently Kerberos authenticated with domain [_1] Version [_2].',
3230: $in{'curr_autharg'},$krbver);
3231: } else {
3232: $result =
3233: &mt('Currently Kerberos authenticated, Version [_1].',$krbver);
3234: }
3235: return $result;
3236: }
3237: }
3238: } else {
3239: if ($authnum == 1) {
1.784 bisitz 3240: $authtype = '<input type="hidden" name="login" value="krb" />';
1.165 raeburn 3241: }
3242: }
1.586 raeburn 3243: if (!$can_assign{'krb4'} && !$can_assign{'krb5'}) {
3244: return;
1.587 raeburn 3245: } elsif ($authtype eq '') {
1.591 raeburn 3246: if (defined($in{'mode'})) {
1.587 raeburn 3247: if ($in{'mode'} eq 'modifycourse') {
3248: if ($authnum == 1) {
1.1259 raeburn 3249: $authtype = '<input type="radio" name="login" value="krb"'.$disabled.' />';
1.587 raeburn 3250: }
3251: }
3252: }
1.586 raeburn 3253: }
3254: $jscall = "javascript:changed_radio('krb',$in{'formname'});";
3255: if ($authtype eq '') {
3256: $authtype = '<input type="radio" name="login" value="krb" '.
3257: 'onclick="'.$jscall.'" onchange="'.$jscall.'"'.
1.1259 raeburn 3258: $krbcheck.$disabled.' />';
1.586 raeburn 3259: }
3260: if (($can_assign{'krb4'} && $can_assign{'krb5'}) ||
1.1106 raeburn 3261: ($can_assign{'krb4'} && !$can_assign{'krb5'} &&
1.586 raeburn 3262: $in{'curr_authtype'} eq 'krb5') ||
1.1106 raeburn 3263: (!$can_assign{'krb4'} && $can_assign{'krb5'} &&
1.586 raeburn 3264: $in{'curr_authtype'} eq 'krb4')) {
3265: $result .= &mt
1.144 matthew 3266: ('[_1] Kerberos authenticated with domain [_2] '.
1.281 albertel 3267: '[_3] Version 4 [_4] Version 5 [_5]',
1.586 raeburn 3268: '<label>'.$authtype,
1.281 albertel 3269: '</label><input type="text" size="10" name="krbarg" '.
1.165 raeburn 3270: 'value="'.$krbarg.'" '.
1.1259 raeburn 3271: 'onchange="'.$jscall.'"'.$disabled.' />',
3272: '<label><input type="radio" name="krbver" value="4" '.$check4.$disabled.' />',
3273: '</label><label><input type="radio" name="krbver" value="5" '.$check5.$disabled.' />',
1.281 albertel 3274: '</label>');
1.586 raeburn 3275: } elsif ($can_assign{'krb4'}) {
3276: $result .= &mt
3277: ('[_1] Kerberos authenticated with domain [_2] '.
3278: '[_3] Version 4 [_4]',
3279: '<label>'.$authtype,
3280: '</label><input type="text" size="10" name="krbarg" '.
3281: 'value="'.$krbarg.'" '.
1.1259 raeburn 3282: 'onchange="'.$jscall.'"'.$disabled.' />',
1.586 raeburn 3283: '<label><input type="hidden" name="krbver" value="4" />',
3284: '</label>');
3285: } elsif ($can_assign{'krb5'}) {
3286: $result .= &mt
3287: ('[_1] Kerberos authenticated with domain [_2] '.
3288: '[_3] Version 5 [_4]',
3289: '<label>'.$authtype,
3290: '</label><input type="text" size="10" name="krbarg" '.
3291: 'value="'.$krbarg.'" '.
1.1259 raeburn 3292: 'onchange="'.$jscall.'"'.$disabled.' />',
1.586 raeburn 3293: '<label><input type="hidden" name="krbver" value="5" />',
3294: '</label>');
3295: }
1.32 matthew 3296: return $result;
3297: }
3298:
1.1106 raeburn 3299: sub authform_internal {
1.586 raeburn 3300: my %in = (
1.32 matthew 3301: formname => 'document.cu',
3302: kerb_def_dom => 'MSU.EDU',
3303: @_,
3304: );
1.1259 raeburn 3305: my ($intcheck,$intarg,$result,$authtype,$autharg,$jscall,$disabled);
1.1106 raeburn 3306: my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.1259 raeburn 3307: if ($in{'readonly'}) {
3308: $disabled = ' disabled="disabled"';
3309: }
1.591 raeburn 3310: if (defined($in{'curr_authtype'})) {
3311: if ($in{'curr_authtype'} eq 'int') {
1.586 raeburn 3312: if ($can_assign{'int'}) {
1.772 bisitz 3313: $intcheck = 'checked="checked" ';
1.623 raeburn 3314: if (defined($in{'mode'})) {
3315: if ($in{'mode'} eq 'modifyuser') {
3316: $intcheck = '';
3317: }
3318: }
1.591 raeburn 3319: if (defined($in{'curr_autharg'})) {
1.586 raeburn 3320: $intarg = $in{'curr_autharg'};
3321: }
3322: } else {
3323: $result = &mt('Currently internally authenticated.');
3324: return $result;
1.165 raeburn 3325: }
3326: }
1.586 raeburn 3327: } else {
3328: if ($authnum == 1) {
1.784 bisitz 3329: $authtype = '<input type="hidden" name="login" value="int" />';
1.586 raeburn 3330: }
3331: }
3332: if (!$can_assign{'int'}) {
3333: return;
1.587 raeburn 3334: } elsif ($authtype eq '') {
1.591 raeburn 3335: if (defined($in{'mode'})) {
1.587 raeburn 3336: if ($in{'mode'} eq 'modifycourse') {
3337: if ($authnum == 1) {
1.1259 raeburn 3338: $authtype = '<input type="radio" name="login" value="int"'.$disabled.' />';
1.587 raeburn 3339: }
3340: }
3341: }
1.165 raeburn 3342: }
1.586 raeburn 3343: $jscall = "javascript:changed_radio('int',$in{'formname'});";
3344: if ($authtype eq '') {
3345: $authtype = '<input type="radio" name="login" value="int" '.$intcheck.
1.1259 raeburn 3346: ' onchange="'.$jscall.'" onclick="'.$jscall.'"'.$disabled.' />';
1.586 raeburn 3347: }
1.605 bisitz 3348: $autharg = '<input type="password" size="10" name="intarg" value="'.
1.1259 raeburn 3349: $intarg.'" onchange="'.$jscall.'"'.$disabled.' />';
1.586 raeburn 3350: $result = &mt
1.144 matthew 3351: ('[_1] Internally authenticated (with initial password [_2])',
1.586 raeburn 3352: '<label>'.$authtype,'</label>'.$autharg);
1.1259 raeburn 3353: $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 3354: return $result;
3355: }
3356:
1.1104 raeburn 3357: sub authform_local {
1.32 matthew 3358: my %in = (
3359: formname => 'document.cu',
3360: kerb_def_dom => 'MSU.EDU',
3361: @_,
3362: );
1.1259 raeburn 3363: my ($loccheck,$locarg,$result,$authtype,$autharg,$jscall,$disabled);
1.1106 raeburn 3364: my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.1259 raeburn 3365: if ($in{'readonly'}) {
3366: $disabled = ' disabled="disabled"';
3367: }
1.591 raeburn 3368: if (defined($in{'curr_authtype'})) {
3369: if ($in{'curr_authtype'} eq 'loc') {
1.586 raeburn 3370: if ($can_assign{'loc'}) {
1.772 bisitz 3371: $loccheck = 'checked="checked" ';
1.623 raeburn 3372: if (defined($in{'mode'})) {
3373: if ($in{'mode'} eq 'modifyuser') {
3374: $loccheck = '';
3375: }
3376: }
1.591 raeburn 3377: if (defined($in{'curr_autharg'})) {
1.586 raeburn 3378: $locarg = $in{'curr_autharg'};
3379: }
3380: } else {
3381: $result = &mt('Currently using local (institutional) authentication.');
3382: return $result;
1.165 raeburn 3383: }
3384: }
1.586 raeburn 3385: } else {
3386: if ($authnum == 1) {
1.784 bisitz 3387: $authtype = '<input type="hidden" name="login" value="loc" />';
1.586 raeburn 3388: }
3389: }
3390: if (!$can_assign{'loc'}) {
3391: return;
1.587 raeburn 3392: } elsif ($authtype eq '') {
1.591 raeburn 3393: if (defined($in{'mode'})) {
1.587 raeburn 3394: if ($in{'mode'} eq 'modifycourse') {
3395: if ($authnum == 1) {
1.1259 raeburn 3396: $authtype = '<input type="radio" name="login" value="loc"'.$disabled.' />';
1.587 raeburn 3397: }
3398: }
3399: }
1.165 raeburn 3400: }
1.586 raeburn 3401: $jscall = "javascript:changed_radio('loc',$in{'formname'});";
3402: if ($authtype eq '') {
3403: $authtype = '<input type="radio" name="login" value="loc" '.
3404: $loccheck.' onchange="'.$jscall.'" onclick="'.
1.1259 raeburn 3405: $jscall.'"'.$disabled.' />';
1.586 raeburn 3406: }
3407: $autharg = '<input type="text" size="10" name="locarg" value="'.
1.1259 raeburn 3408: $locarg.'" onchange="'.$jscall.'"'.$disabled.' />';
1.586 raeburn 3409: $result = &mt('[_1] Local Authentication with argument [_2]',
3410: '<label>'.$authtype,'</label>'.$autharg);
1.32 matthew 3411: return $result;
3412: }
3413:
1.1106 raeburn 3414: sub authform_filesystem {
1.32 matthew 3415: my %in = (
3416: formname => 'document.cu',
3417: kerb_def_dom => 'MSU.EDU',
3418: @_,
3419: );
1.1259 raeburn 3420: my ($fsyscheck,$result,$authtype,$autharg,$jscall,$disabled);
1.1106 raeburn 3421: my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.1259 raeburn 3422: if ($in{'readonly'}) {
3423: $disabled = ' disabled="disabled"';
3424: }
1.591 raeburn 3425: if (defined($in{'curr_authtype'})) {
3426: if ($in{'curr_authtype'} eq 'fsys') {
1.586 raeburn 3427: if ($can_assign{'fsys'}) {
1.772 bisitz 3428: $fsyscheck = 'checked="checked" ';
1.623 raeburn 3429: if (defined($in{'mode'})) {
3430: if ($in{'mode'} eq 'modifyuser') {
3431: $fsyscheck = '';
3432: }
3433: }
1.586 raeburn 3434: } else {
3435: $result = &mt('Currently Filesystem Authenticated.');
3436: return $result;
1.1259 raeburn 3437: }
1.586 raeburn 3438: }
3439: } else {
3440: if ($authnum == 1) {
1.784 bisitz 3441: $authtype = '<input type="hidden" name="login" value="fsys" />';
1.586 raeburn 3442: }
3443: }
3444: if (!$can_assign{'fsys'}) {
3445: return;
1.587 raeburn 3446: } elsif ($authtype eq '') {
1.591 raeburn 3447: if (defined($in{'mode'})) {
1.587 raeburn 3448: if ($in{'mode'} eq 'modifycourse') {
3449: if ($authnum == 1) {
1.1259 raeburn 3450: $authtype = '<input type="radio" name="login" value="fsys"'.$disabled.' />';
1.587 raeburn 3451: }
3452: }
3453: }
1.586 raeburn 3454: }
3455: $jscall = "javascript:changed_radio('fsys',$in{'formname'});";
3456: if ($authtype eq '') {
3457: $authtype = '<input type="radio" name="login" value="fsys" '.
3458: $fsyscheck.' onchange="'.$jscall.'" onclick="'.
1.1259 raeburn 3459: $jscall.'"'.$disabled.' />';
1.586 raeburn 3460: }
1.1310 raeburn 3461: $autharg = '<input type="password" size="10" name="fsysarg" value=""'.
1.1259 raeburn 3462: ' onchange="'.$jscall.'"'.$disabled.' />';
1.586 raeburn 3463: $result = &mt
1.144 matthew 3464: ('[_1] Filesystem Authenticated (with initial password [_2])',
1.1310 raeburn 3465: '<label>'.$authtype,'</label>'.$autharg);
3466: return $result;
3467: }
3468:
3469: sub authform_lti {
3470: my %in = (
3471: formname => 'document.cu',
3472: kerb_def_dom => 'MSU.EDU',
3473: @_,
3474: );
3475: my ($lticheck,$result,$authtype,$autharg,$jscall,$disabled);
3476: my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
3477: if ($in{'readonly'}) {
3478: $disabled = ' disabled="disabled"';
3479: }
3480: if (defined($in{'curr_authtype'})) {
3481: if ($in{'curr_authtype'} eq 'lti') {
3482: if ($can_assign{'lti'}) {
3483: $lticheck = 'checked="checked" ';
3484: if (defined($in{'mode'})) {
3485: if ($in{'mode'} eq 'modifyuser') {
3486: $lticheck = '';
3487: }
3488: }
3489: } else {
3490: $result = &mt('Currently LTI Authenticated.');
3491: return $result;
3492: }
3493: }
3494: } else {
3495: if ($authnum == 1) {
3496: $authtype = '<input type="hidden" name="login" value="lti" />';
3497: }
3498: }
3499: if (!$can_assign{'lti'}) {
3500: return;
3501: } elsif ($authtype eq '') {
3502: if (defined($in{'mode'})) {
3503: if ($in{'mode'} eq 'modifycourse') {
3504: if ($authnum == 1) {
3505: $authtype = '<input type="radio" name="login" value="lti"'.$disabled.' />';
3506: }
3507: }
3508: }
3509: }
3510: $jscall = "javascript:changed_radio('lti',$in{'formname'});";
3511: if (($authtype eq '') && (($in{'mode'} eq 'modifycourse') || ($in{'curr_authtype'} ne 'lti'))) {
3512: $authtype = '<input type="radio" name="login" value="lti" '.
3513: $lticheck.' onchange="'.$jscall.'" onclick="'.
3514: $jscall.'"'.$disabled.' />';
3515: }
3516: $autharg = '<input type="hidden" name="ltiarg" value="" />';
3517: if ($authtype) {
3518: $result = &mt('[_1] LTI Authenticated',
3519: '<label>'.$authtype.'</label>'.$autharg);
3520: } else {
3521: $result = '<b>'.&mt('LTI Authenticated').'</b>'.
3522: $autharg;
3523: }
1.32 matthew 3524: return $result;
3525: }
3526:
1.586 raeburn 3527: sub get_assignable_auth {
3528: my ($dom) = @_;
3529: if ($dom eq '') {
3530: $dom = $env{'request.role.domain'};
3531: }
3532: my %can_assign = (
3533: krb4 => 1,
3534: krb5 => 1,
3535: int => 1,
3536: loc => 1,
1.1310 raeburn 3537: lti => 1,
1.586 raeburn 3538: );
3539: my %domconfig = &Apache::lonnet::get_dom('configuration',['usercreation'],$dom);
3540: if (ref($domconfig{'usercreation'}) eq 'HASH') {
3541: if (ref($domconfig{'usercreation'}{'authtypes'}) eq 'HASH') {
3542: my $authhash = $domconfig{'usercreation'}{'authtypes'};
3543: my $context;
3544: if ($env{'request.role'} =~ /^au/) {
3545: $context = 'author';
1.1259 raeburn 3546: } elsif ($env{'request.role'} =~ /^(dc|dh)/) {
1.586 raeburn 3547: $context = 'domain';
3548: } elsif ($env{'request.course.id'}) {
3549: $context = 'course';
3550: }
3551: if ($context) {
3552: if (ref($authhash->{$context}) eq 'HASH') {
3553: %can_assign = %{$authhash->{$context}};
3554: }
3555: }
3556: }
3557: }
3558: my $authnum = 0;
3559: foreach my $key (keys(%can_assign)) {
3560: if ($can_assign{$key}) {
3561: $authnum ++;
3562: }
3563: }
3564: if ($can_assign{'krb4'} && $can_assign{'krb5'}) {
3565: $authnum --;
3566: }
3567: return ($authnum,%can_assign);
3568: }
3569:
1.80 albertel 3570: ###############################################################
3571: ## Get Kerberos Defaults for Domain ##
3572: ###############################################################
3573: ##
3574: ## Returns default kerberos version and an associated argument
3575: ## as listed in file domain.tab. If not listed, provides
3576: ## appropriate default domain and kerberos version.
3577: ##
3578: #-------------------------------------------
3579:
3580: =pod
3581:
1.648 raeburn 3582: =item * &get_kerberos_defaults()
1.80 albertel 3583:
3584: get_kerberos_defaults($target_domain) returns the default kerberos
1.641 raeburn 3585: version and domain. If not found, it defaults to version 4 and the
3586: domain of the server.
1.80 albertel 3587:
1.648 raeburn 3588: =over 4
3589:
1.80 albertel 3590: ($def_version, $def_krb_domain) = &get_kerberos_defaults($target_domain);
3591:
1.648 raeburn 3592: =back
3593:
3594: =back
3595:
1.80 albertel 3596: =cut
3597:
3598: #-------------------------------------------
3599: sub get_kerberos_defaults {
3600: my $domain=shift;
1.641 raeburn 3601: my ($krbdef,$krbdefdom);
3602: my %domdefaults = &Apache::lonnet::get_domain_defaults($domain);
3603: if (($domdefaults{'auth_def'} =~/^krb(4|5)$/) && ($domdefaults{'auth_arg_def'} ne '')) {
3604: $krbdef = $domdefaults{'auth_def'};
3605: $krbdefdom = $domdefaults{'auth_arg_def'};
3606: } else {
1.80 albertel 3607: $ENV{'SERVER_NAME'}=~/(\w+\.\w+)$/;
3608: my $krbdefdom=$1;
3609: $krbdefdom=~tr/a-z/A-Z/;
3610: $krbdef = "krb4";
3611: }
3612: return ($krbdef,$krbdefdom);
3613: }
1.112 bowersj2 3614:
1.32 matthew 3615:
1.46 matthew 3616: ###############################################################
3617: ## Thesaurus Functions ##
3618: ###############################################################
1.20 www 3619:
1.46 matthew 3620: =pod
1.20 www 3621:
1.112 bowersj2 3622: =head1 Thesaurus Functions
3623:
3624: =over 4
3625:
1.648 raeburn 3626: =item * &initialize_keywords()
1.46 matthew 3627:
3628: Initializes the package variable %Keywords if it is empty. Uses the
3629: package variable $thesaurus_db_file.
3630:
3631: =cut
3632:
3633: ###################################################
3634:
3635: sub initialize_keywords {
3636: return 1 if (scalar keys(%Keywords));
3637: # If we are here, %Keywords is empty, so fill it up
3638: # Make sure the file we need exists...
3639: if (! -e $thesaurus_db_file) {
3640: &Apache::lonnet::logthis("Attempt to access $thesaurus_db_file".
3641: " failed because it does not exist");
3642: return 0;
3643: }
3644: # Set up the hash as a database
3645: my %thesaurus_db;
3646: if (! tie(%thesaurus_db,'GDBM_File',
1.53 albertel 3647: $thesaurus_db_file,&GDBM_READER(),0640)){
1.46 matthew 3648: &Apache::lonnet::logthis("Could not tie \%thesaurus_db to ".
3649: $thesaurus_db_file);
3650: return 0;
3651: }
3652: # Get the average number of appearances of a word.
3653: my $avecount = $thesaurus_db{'average.count'};
3654: # Put keywords (those that appear > average) into %Keywords
3655: while (my ($word,$data)=each (%thesaurus_db)) {
3656: my ($count,undef) = split /:/,$data;
3657: $Keywords{$word}++ if ($count > $avecount);
3658: }
3659: untie %thesaurus_db;
3660: # Remove special values from %Keywords.
1.356 albertel 3661: foreach my $value ('total.count','average.count') {
3662: delete($Keywords{$value}) if (exists($Keywords{$value}));
1.586 raeburn 3663: }
1.46 matthew 3664: return 1;
3665: }
3666:
3667: ###################################################
3668:
3669: =pod
3670:
1.648 raeburn 3671: =item * &keyword($word)
1.46 matthew 3672:
3673: Returns true if $word is a keyword. A keyword is a word that appears more
3674: than the average number of times in the thesaurus database. Calls
3675: &initialize_keywords
3676:
3677: =cut
3678:
3679: ###################################################
1.20 www 3680:
3681: sub keyword {
1.46 matthew 3682: return if (!&initialize_keywords());
3683: my $word=lc(shift());
3684: $word=~s/\W//g;
3685: return exists($Keywords{$word});
1.20 www 3686: }
1.46 matthew 3687:
3688: ###############################################################
3689:
3690: =pod
1.20 www 3691:
1.648 raeburn 3692: =item * &get_related_words()
1.46 matthew 3693:
1.160 matthew 3694: Look up a word in the thesaurus. Takes a scalar argument and returns
1.46 matthew 3695: an array of words. If the keyword is not in the thesaurus, an empty array
3696: will be returned. The order of the words returned is determined by the
3697: database which holds them.
3698:
3699: Uses global $thesaurus_db_file.
3700:
1.1057 foxr 3701:
1.46 matthew 3702: =cut
3703:
3704: ###############################################################
3705: sub get_related_words {
3706: my $keyword = shift;
3707: my %thesaurus_db;
3708: if (! -e $thesaurus_db_file) {
3709: &Apache::lonnet::logthis("Attempt to access $thesaurus_db_file ".
3710: "failed because the file does not exist");
3711: return ();
3712: }
3713: if (! tie(%thesaurus_db,'GDBM_File',
1.53 albertel 3714: $thesaurus_db_file,&GDBM_READER(),0640)){
1.46 matthew 3715: return ();
3716: }
3717: my @Words=();
1.429 www 3718: my $count=0;
1.46 matthew 3719: if (exists($thesaurus_db{$keyword})) {
1.356 albertel 3720: # The first element is the number of times
3721: # the word appears. We do not need it now.
1.429 www 3722: my (undef,@RelatedWords) = (split(/:/,$thesaurus_db{$keyword}));
3723: my (undef,$mostfrequentcount)=split(/\,/,$RelatedWords[0]);
3724: my $threshold=$mostfrequentcount/10;
3725: foreach my $possibleword (@RelatedWords) {
3726: my ($word,$wordcount)=split(/\,/,$possibleword);
3727: if ($wordcount>$threshold) {
3728: push(@Words,$word);
3729: $count++;
3730: if ($count>10) { last; }
3731: }
1.20 www 3732: }
3733: }
1.46 matthew 3734: untie %thesaurus_db;
3735: return @Words;
1.14 harris41 3736: }
1.1090 foxr 3737: ###############################################################
3738: #
3739: # Spell checking
3740: #
3741:
3742: =pod
3743:
1.1142 raeburn 3744: =back
3745:
1.1090 foxr 3746: =head1 Spell checking
3747:
3748: =over 4
3749:
3750: =item * &check_spelling($wordlist $language)
3751:
3752: Takes a string containing words and feeds it to an external
3753: spellcheck program via a pipeline. Returns a string containing
3754: them mis-spelled words.
3755:
3756: Parameters:
3757:
3758: =over 4
3759:
3760: =item - $wordlist
3761:
3762: String that will be fed into the spellcheck program.
3763:
3764: =item - $language
3765:
3766: Language string that specifies the language for which the spell
3767: check will be performed.
3768:
3769: =back
3770:
3771: =back
3772:
3773: Note: This sub assumes that aspell is installed.
3774:
3775:
3776: =cut
3777:
1.46 matthew 3778:
1.1090 foxr 3779: sub check_spelling {
3780: my ($wordlist, $language) = @_;
1.1091 foxr 3781: my @misspellings;
3782:
3783: # Generate the speller and set the langauge.
3784: # if explicitly selected:
1.1090 foxr 3785:
1.1091 foxr 3786: my $speller = Text::Aspell->new;
1.1090 foxr 3787: if ($language) {
1.1091 foxr 3788: $speller->set_option('lang', $language);
1.1090 foxr 3789: }
3790:
1.1091 foxr 3791: # Turn the word list into an array of words by splittingon whitespace
1.1090 foxr 3792:
1.1091 foxr 3793: my @words = split(/\s+/, $wordlist);
1.1090 foxr 3794:
1.1091 foxr 3795: foreach my $word (@words) {
3796: if(! $speller->check($word)) {
3797: push(@misspellings, $word);
1.1090 foxr 3798: }
3799: }
1.1091 foxr 3800: return join(' ', @misspellings);
3801:
1.1090 foxr 3802: }
3803:
1.61 www 3804: # -------------------------------------------------------------- Plaintext name
1.81 albertel 3805: =pod
3806:
1.112 bowersj2 3807: =head1 User Name Functions
3808:
3809: =over 4
3810:
1.648 raeburn 3811: =item * &plainname($uname,$udom,$first)
1.81 albertel 3812:
1.112 bowersj2 3813: Takes a users logon name and returns it as a string in
1.226 albertel 3814: "first middle last generation" form
3815: if $first is set to 'lastname' then it returns it as
3816: 'lastname generation, firstname middlename' if their is a lastname
1.81 albertel 3817:
3818: =cut
1.61 www 3819:
1.295 www 3820:
1.81 albertel 3821: ###############################################################
1.61 www 3822: sub plainname {
1.226 albertel 3823: my ($uname,$udom,$first)=@_;
1.537 albertel 3824: return if (!defined($uname) || !defined($udom));
1.295 www 3825: my %names=&getnames($uname,$udom);
1.226 albertel 3826: my $name=&Apache::lonnet::format_name($names{'firstname'},
3827: $names{'middlename'},
3828: $names{'lastname'},
3829: $names{'generation'},$first);
3830: $name=~s/^\s+//;
1.62 www 3831: $name=~s/\s+$//;
3832: $name=~s/\s+/ /g;
1.353 albertel 3833: if ($name !~ /\S/) { $name=$uname.':'.$udom; }
1.62 www 3834: return $name;
1.61 www 3835: }
1.66 www 3836:
3837: # -------------------------------------------------------------------- Nickname
1.81 albertel 3838: =pod
3839:
1.648 raeburn 3840: =item * &nickname($uname,$udom)
1.81 albertel 3841:
3842: Gets a users name and returns it as a string as
3843:
3844: ""nickname""
1.66 www 3845:
1.81 albertel 3846: if the user has a nickname or
3847:
3848: "first middle last generation"
3849:
3850: if the user does not
3851:
3852: =cut
1.66 www 3853:
3854: sub nickname {
3855: my ($uname,$udom)=@_;
1.537 albertel 3856: return if (!defined($uname) || !defined($udom));
1.295 www 3857: my %names=&getnames($uname,$udom);
1.68 albertel 3858: my $name=$names{'nickname'};
1.66 www 3859: if ($name) {
3860: $name='"'.$name.'"';
3861: } else {
3862: $name=$names{'firstname'}.' '.$names{'middlename'}.' '.
3863: $names{'lastname'}.' '.$names{'generation'};
3864: $name=~s/\s+$//;
3865: $name=~s/\s+/ /g;
3866: }
3867: return $name;
3868: }
3869:
1.295 www 3870: sub getnames {
3871: my ($uname,$udom)=@_;
1.537 albertel 3872: return if (!defined($uname) || !defined($udom));
1.433 albertel 3873: if ($udom eq 'public' && $uname eq 'public') {
3874: return ('lastname' => &mt('Public'));
3875: }
1.295 www 3876: my $id=$uname.':'.$udom;
3877: my ($names,$cached)=&Apache::lonnet::is_cached_new('namescache',$id);
3878: if ($cached) {
3879: return %{$names};
3880: } else {
3881: my %loadnames=&Apache::lonnet::get('environment',
3882: ['firstname','middlename','lastname','generation','nickname'],
3883: $udom,$uname);
3884: &Apache::lonnet::do_cache_new('namescache',$id,\%loadnames);
3885: return %loadnames;
3886: }
3887: }
1.61 www 3888:
1.542 raeburn 3889: # -------------------------------------------------------------------- getemails
1.648 raeburn 3890:
1.542 raeburn 3891: =pod
3892:
1.648 raeburn 3893: =item * &getemails($uname,$udom)
1.542 raeburn 3894:
3895: Gets a user's email information and returns it as a hash with keys:
3896: notification, critnotification, permanentemail
3897:
3898: For notification and critnotification, values are comma-separated lists
1.648 raeburn 3899: of e-mail addresses; for permanentemail, value is a single e-mail address.
1.542 raeburn 3900:
1.648 raeburn 3901:
1.542 raeburn 3902: =cut
3903:
1.648 raeburn 3904:
1.466 albertel 3905: sub getemails {
3906: my ($uname,$udom)=@_;
3907: if ($udom eq 'public' && $uname eq 'public') {
3908: return;
3909: }
1.467 www 3910: if (!$udom) { $udom=$env{'user.domain'}; }
3911: if (!$uname) { $uname=$env{'user.name'}; }
1.466 albertel 3912: my $id=$uname.':'.$udom;
3913: my ($names,$cached)=&Apache::lonnet::is_cached_new('emailscache',$id);
3914: if ($cached) {
3915: return %{$names};
3916: } else {
3917: my %loadnames=&Apache::lonnet::get('environment',
3918: ['notification','critnotification',
3919: 'permanentemail'],
3920: $udom,$uname);
3921: &Apache::lonnet::do_cache_new('emailscache',$id,\%loadnames);
3922: return %loadnames;
3923: }
3924: }
3925:
1.551 albertel 3926: sub flush_email_cache {
3927: my ($uname,$udom)=@_;
3928: if (!$udom) { $udom =$env{'user.domain'}; }
3929: if (!$uname) { $uname=$env{'user.name'}; }
3930: return if ($udom eq 'public' && $uname eq 'public');
3931: my $id=$uname.':'.$udom;
3932: &Apache::lonnet::devalidate_cache_new('emailscache',$id);
3933: }
3934:
1.728 raeburn 3935: # -------------------------------------------------------------------- getlangs
3936:
3937: =pod
3938:
3939: =item * &getlangs($uname,$udom)
3940:
3941: Gets a user's language preference and returns it as a hash with key:
3942: language.
3943:
3944: =cut
3945:
3946:
3947: sub getlangs {
3948: my ($uname,$udom) = @_;
3949: if (!$udom) { $udom =$env{'user.domain'}; }
3950: if (!$uname) { $uname=$env{'user.name'}; }
3951: my $id=$uname.':'.$udom;
3952: my ($langs,$cached)=&Apache::lonnet::is_cached_new('userlangs',$id);
3953: if ($cached) {
3954: return %{$langs};
3955: } else {
3956: my %loadlangs=&Apache::lonnet::get('environment',['languages'],
3957: $udom,$uname);
3958: &Apache::lonnet::do_cache_new('userlangs',$id,\%loadlangs);
3959: return %loadlangs;
3960: }
3961: }
3962:
3963: sub flush_langs_cache {
3964: my ($uname,$udom)=@_;
3965: if (!$udom) { $udom =$env{'user.domain'}; }
3966: if (!$uname) { $uname=$env{'user.name'}; }
3967: return if ($udom eq 'public' && $uname eq 'public');
3968: my $id=$uname.':'.$udom;
3969: &Apache::lonnet::devalidate_cache_new('userlangs',$id);
3970: }
3971:
1.61 www 3972: # ------------------------------------------------------------------ Screenname
1.81 albertel 3973:
3974: =pod
3975:
1.648 raeburn 3976: =item * &screenname($uname,$udom)
1.81 albertel 3977:
3978: Gets a users screenname and returns it as a string
3979:
3980: =cut
1.61 www 3981:
3982: sub screenname {
3983: my ($uname,$udom)=@_;
1.258 albertel 3984: if ($uname eq $env{'user.name'} &&
3985: $udom eq $env{'user.domain'}) {return $env{'environment.screenname'};}
1.212 albertel 3986: my %names=&Apache::lonnet::get('environment',['screenname'],$udom,$uname);
1.68 albertel 3987: return $names{'screenname'};
1.62 www 3988: }
3989:
1.212 albertel 3990:
1.802 bisitz 3991: # ------------------------------------------------------------- Confirm Wrapper
3992: =pod
3993:
1.1142 raeburn 3994: =item * &confirmwrapper($message)
1.802 bisitz 3995:
3996: Wrap messages about completion of operation in box
3997:
3998: =cut
3999:
4000: sub confirmwrapper {
4001: my ($message)=@_;
4002: if ($message) {
4003: return "\n".'<div class="LC_confirm_box">'."\n"
4004: .$message."\n"
4005: .'</div>'."\n";
4006: } else {
4007: return $message;
4008: }
4009: }
4010:
1.62 www 4011: # ------------------------------------------------------------- Message Wrapper
4012:
4013: sub messagewrapper {
1.369 www 4014: my ($link,$username,$domain,$subject,$text)=@_;
1.62 www 4015: return
1.441 albertel 4016: '<a href="/adm/email?compose=individual&'.
4017: 'recname='.$username.'&recdom='.$domain.
4018: '&subject='.&escape($subject).'&text='.&escape($text).'" '.
1.200 matthew 4019: 'title="'.&mt('Send message').'">'.$link.'</a>';
1.74 www 4020: }
1.802 bisitz 4021:
1.74 www 4022: # --------------------------------------------------------------- Notes Wrapper
4023:
4024: sub noteswrapper {
4025: my ($link,$un,$do)=@_;
4026: return
1.896 amueller 4027: "<a href='/adm/email?recordftf=retrieve&recname=$un&recdom=$do'>$link</a>";
1.62 www 4028: }
1.802 bisitz 4029:
1.62 www 4030: # ------------------------------------------------------------- Aboutme Wrapper
4031:
4032: sub aboutmewrapper {
1.1070 raeburn 4033: my ($link,$username,$domain,$target,$class)=@_;
1.447 raeburn 4034: if (!defined($username) && !defined($domain)) {
4035: return;
4036: }
1.1096 raeburn 4037: return '<a href="/adm/'.$domain.'/'.$username.'/aboutme"'.
1.1070 raeburn 4038: ($target?' target="'.$target.'"':'').($class?' class="'.$class.'"':'').' title="'.&mt("View this user's personal information page").'">'.$link.'</a>';
1.62 www 4039: }
4040:
4041: # ------------------------------------------------------------ Syllabus Wrapper
4042:
4043: sub syllabuswrapper {
1.707 bisitz 4044: my ($linktext,$coursedir,$domain)=@_;
1.208 matthew 4045: return qq{<a href="/public/$domain/$coursedir/syllabus">$linktext</a>};
1.61 www 4046: }
1.14 harris41 4047:
1.802 bisitz 4048: # -----------------------------------------------------------------------------
4049:
1.208 matthew 4050: sub track_student_link {
1.887 raeburn 4051: my ($linktext,$sname,$sdom,$target,$start,$only_body) = @_;
1.268 albertel 4052: my $link ="/adm/trackstudent?";
1.208 matthew 4053: my $title = 'View recent activity';
4054: if (defined($sname) && $sname !~ /^\s*$/ &&
4055: defined($sdom) && $sdom !~ /^\s*$/) {
1.268 albertel 4056: $link .= "selected_student=$sname:$sdom";
1.208 matthew 4057: $title .= ' of this student';
1.268 albertel 4058: }
1.208 matthew 4059: if (defined($target) && $target !~ /^\s*$/) {
4060: $target = qq{target="$target"};
4061: } else {
4062: $target = '';
4063: }
1.268 albertel 4064: if ($start) { $link.='&start='.$start; }
1.887 raeburn 4065: if ($only_body) { $link .= '&only_body=1'; }
1.554 albertel 4066: $title = &mt($title);
4067: $linktext = &mt($linktext);
1.448 albertel 4068: return qq{<a href="$link" title="$title" $target>$linktext</a>}.
4069: &help_open_topic('View_recent_activity');
1.208 matthew 4070: }
4071:
1.781 raeburn 4072: sub slot_reservations_link {
4073: my ($linktext,$sname,$sdom,$target) = @_;
4074: my $link ="/adm/slotrequest?command=showresv&origin=aboutme";
4075: my $title = 'View slot reservation history';
4076: if (defined($sname) && $sname !~ /^\s*$/ &&
4077: defined($sdom) && $sdom !~ /^\s*$/) {
4078: $link .= "&uname=$sname&udom=$sdom";
4079: $title .= ' of this student';
4080: }
4081: if (defined($target) && $target !~ /^\s*$/) {
4082: $target = qq{target="$target"};
4083: } else {
4084: $target = '';
4085: }
4086: $title = &mt($title);
4087: $linktext = &mt($linktext);
4088: return qq{<a href="$link" title="$title" $target>$linktext</a>};
4089: # FIXME uncomment when help item created: &help_open_topic('Slot_Reservation_History');
4090:
4091: }
4092:
1.508 www 4093: # ===================================================== Display a student photo
4094:
4095:
1.509 albertel 4096: sub student_image_tag {
1.508 www 4097: my ($domain,$user)=@_;
4098: my $imgsrc=&Apache::lonnet::studentphoto($domain,$user,'jpg');
4099: if (($imgsrc) && ($imgsrc ne '/adm/lonKaputt/lonlogo_broken.gif')) {
4100: return '<img src="'.$imgsrc.'" align="right" />';
4101: } else {
4102: return '';
4103: }
4104: }
4105:
1.112 bowersj2 4106: =pod
4107:
4108: =back
4109:
4110: =head1 Access .tab File Data
4111:
4112: =over 4
4113:
1.648 raeburn 4114: =item * &languageids()
1.112 bowersj2 4115:
4116: returns list of all language ids
4117:
4118: =cut
4119:
1.14 harris41 4120: sub languageids {
1.16 harris41 4121: return sort(keys(%language));
1.14 harris41 4122: }
4123:
1.112 bowersj2 4124: =pod
4125:
1.648 raeburn 4126: =item * &languagedescription()
1.112 bowersj2 4127:
4128: returns description of a specified language id
4129:
4130: =cut
4131:
1.14 harris41 4132: sub languagedescription {
1.125 www 4133: my $code=shift;
4134: return ($supported_language{$code}?'* ':'').
4135: $language{$code}.
1.126 www 4136: ($supported_language{$code}?' ('.&mt('interface available').')':'');
1.145 www 4137: }
4138:
1.1048 foxr 4139: =pod
4140:
4141: =item * &plainlanguagedescription
4142:
4143: Returns both the plain language description (e.g. 'Creoles and Pidgins, English-based (Other)')
4144: and the language character encoding (e.g. ISO) separated by a ' - ' string.
4145:
4146: =cut
4147:
1.145 www 4148: sub plainlanguagedescription {
4149: my $code=shift;
4150: return $language{$code};
4151: }
4152:
1.1048 foxr 4153: =pod
4154:
4155: =item * &supportedlanguagecode
4156:
4157: Returns the supported language code (e.g. sptutf maps to pt) given a language
4158: code.
4159:
4160: =cut
4161:
1.145 www 4162: sub supportedlanguagecode {
4163: my $code=shift;
4164: return $supported_language{$code};
1.97 www 4165: }
4166:
1.112 bowersj2 4167: =pod
4168:
1.1048 foxr 4169: =item * &latexlanguage()
4170:
4171: Given a language key code returns the correspondnig language to use
4172: to select the correct hyphenation on LaTeX printouts. This is undef if there
4173: is no supported hyphenation for the language code.
4174:
4175: =cut
4176:
4177: sub latexlanguage {
4178: my $code = shift;
4179: return $latex_language{$code};
4180: }
4181:
4182: =pod
4183:
4184: =item * &latexhyphenation()
4185:
4186: Same as above but what's supplied is the language as it might be stored
4187: in the metadata.
4188:
4189: =cut
4190:
4191: sub latexhyphenation {
4192: my $key = shift;
4193: return $latex_language_bykey{$key};
4194: }
4195:
4196: =pod
4197:
1.648 raeburn 4198: =item * ©rightids()
1.112 bowersj2 4199:
4200: returns list of all copyrights
4201:
4202: =cut
4203:
4204: sub copyrightids {
4205: return sort(keys(%cprtag));
4206: }
4207:
4208: =pod
4209:
1.648 raeburn 4210: =item * ©rightdescription()
1.112 bowersj2 4211:
4212: returns description of a specified copyright id
4213:
4214: =cut
4215:
4216: sub copyrightdescription {
1.166 www 4217: return &mt($cprtag{shift(@_)});
1.112 bowersj2 4218: }
1.197 matthew 4219:
4220: =pod
4221:
1.648 raeburn 4222: =item * &source_copyrightids()
1.192 taceyjo1 4223:
4224: returns list of all source copyrights
4225:
4226: =cut
4227:
4228: sub source_copyrightids {
4229: return sort(keys(%scprtag));
4230: }
4231:
4232: =pod
4233:
1.648 raeburn 4234: =item * &source_copyrightdescription()
1.192 taceyjo1 4235:
4236: returns description of a specified source copyright id
4237:
4238: =cut
4239:
4240: sub source_copyrightdescription {
4241: return &mt($scprtag{shift(@_)});
4242: }
1.112 bowersj2 4243:
4244: =pod
4245:
1.648 raeburn 4246: =item * &filecategories()
1.112 bowersj2 4247:
4248: returns list of all file categories
4249:
4250: =cut
4251:
4252: sub filecategories {
4253: return sort(keys(%category_extensions));
4254: }
4255:
4256: =pod
4257:
1.648 raeburn 4258: =item * &filecategorytypes()
1.112 bowersj2 4259:
4260: returns list of file types belonging to a given file
4261: category
4262:
4263: =cut
4264:
4265: sub filecategorytypes {
1.356 albertel 4266: my ($cat) = @_;
1.1248 raeburn 4267: if (ref($category_extensions{lc($cat)}) eq 'ARRAY') {
4268: return @{$category_extensions{lc($cat)}};
4269: } else {
4270: return ();
4271: }
1.112 bowersj2 4272: }
4273:
4274: =pod
4275:
1.648 raeburn 4276: =item * &fileembstyle()
1.112 bowersj2 4277:
4278: returns embedding style for a specified file type
4279:
4280: =cut
4281:
4282: sub fileembstyle {
4283: return $fe{lc(shift(@_))};
1.169 www 4284: }
4285:
1.351 www 4286: sub filemimetype {
4287: return $fm{lc(shift(@_))};
4288: }
4289:
1.169 www 4290:
4291: sub filecategoryselect {
4292: my ($name,$value)=@_;
1.189 matthew 4293: return &select_form($value,$name,
1.970 raeburn 4294: {'' => &mt('Any category'), map { $_,$_ } sort(keys(%category_extensions))});
1.112 bowersj2 4295: }
4296:
4297: =pod
4298:
1.648 raeburn 4299: =item * &filedescription()
1.112 bowersj2 4300:
4301: returns description for a specified file type
4302:
4303: =cut
4304:
4305: sub filedescription {
1.188 matthew 4306: my $file_description = $fd{lc(shift())};
4307: $file_description =~ s:([\[\]]):~$1:g;
4308: return &mt($file_description);
1.112 bowersj2 4309: }
4310:
4311: =pod
4312:
1.648 raeburn 4313: =item * &filedescriptionex()
1.112 bowersj2 4314:
4315: returns description for a specified file type with
4316: extra formatting
4317:
4318: =cut
4319:
4320: sub filedescriptionex {
4321: my $ex=shift;
1.188 matthew 4322: my $file_description = $fd{lc($ex)};
4323: $file_description =~ s:([\[\]]):~$1:g;
4324: return '.'.$ex.' '.&mt($file_description);
1.112 bowersj2 4325: }
4326:
4327: # End of .tab access
4328: =pod
4329:
4330: =back
4331:
4332: =cut
4333:
4334: # ------------------------------------------------------------------ File Types
4335: sub fileextensions {
4336: return sort(keys(%fe));
4337: }
4338:
1.97 www 4339: # ----------------------------------------------------------- Display Languages
4340: # returns a hash with all desired display languages
4341: #
4342:
4343: sub display_languages {
4344: my %languages=();
1.695 raeburn 4345: foreach my $lang (&Apache::lonlocal::preferred_languages()) {
1.356 albertel 4346: $languages{$lang}=1;
1.97 www 4347: }
4348: &get_unprocessed_cgi($ENV{'QUERY_STRING'},['displaylanguage']);
1.258 albertel 4349: if ($env{'form.displaylanguage'}) {
1.356 albertel 4350: foreach my $lang (split(/\s*(\,|\;|\:)\s*/,$env{'form.displaylanguage'})) {
4351: $languages{$lang}=1;
1.97 www 4352: }
4353: }
4354: return %languages;
1.14 harris41 4355: }
4356:
1.582 albertel 4357: sub languages {
4358: my ($possible_langs) = @_;
1.695 raeburn 4359: my @preferred_langs = &Apache::lonlocal::preferred_languages();
1.582 albertel 4360: if (!ref($possible_langs)) {
4361: if( wantarray ) {
4362: return @preferred_langs;
4363: } else {
4364: return $preferred_langs[0];
4365: }
4366: }
4367: my %possibilities = map { $_ => 1 } (@$possible_langs);
4368: my @preferred_possibilities;
4369: foreach my $preferred_lang (@preferred_langs) {
4370: if (exists($possibilities{$preferred_lang})) {
4371: push(@preferred_possibilities, $preferred_lang);
4372: }
4373: }
4374: if( wantarray ) {
4375: return @preferred_possibilities;
4376: }
4377: return $preferred_possibilities[0];
4378: }
4379:
1.742 raeburn 4380: sub user_lang {
4381: my ($touname,$toudom,$fromcid) = @_;
4382: my @userlangs;
4383: if (($fromcid ne '') && ($env{'course.'.$fromcid.'.languages'} ne '')) {
4384: @userlangs=(@userlangs,split(/\s*(\,|\;|\:)\s*/,
4385: $env{'course.'.$fromcid.'.languages'}));
4386: } else {
4387: my %langhash = &getlangs($touname,$toudom);
4388: if ($langhash{'languages'} ne '') {
4389: @userlangs = split(/\s*(\,|\;|\:)\s*/,$langhash{'languages'});
4390: } else {
4391: my %domdefs = &Apache::lonnet::get_domain_defaults($toudom);
4392: if ($domdefs{'lang_def'} ne '') {
4393: @userlangs = ($domdefs{'lang_def'});
4394: }
4395: }
4396: }
4397: my @languages=&Apache::lonlocal::get_genlanguages(@userlangs);
4398: my $user_lh = Apache::localize->get_handle(@languages);
4399: return $user_lh;
4400: }
4401:
4402:
1.112 bowersj2 4403: ###############################################################
4404: ## Student Answer Attempts ##
4405: ###############################################################
4406:
4407: =pod
4408:
4409: =head1 Alternate Problem Views
4410:
4411: =over 4
4412:
1.648 raeburn 4413: =item * &get_previous_attempt($symb, $username, $domain, $course,
1.1199 raeburn 4414: $getattempt, $regexp, $gradesub, $usec, $identifier)
1.112 bowersj2 4415:
4416: Return string with previous attempt on problem. Arguments:
4417:
4418: =over 4
4419:
4420: =item * $symb: Problem, including path
4421:
4422: =item * $username: username of the desired student
4423:
4424: =item * $domain: domain of the desired student
1.14 harris41 4425:
1.112 bowersj2 4426: =item * $course: Course ID
1.14 harris41 4427:
1.112 bowersj2 4428: =item * $getattempt: Leave blank for all attempts, otherwise put
4429: something
1.14 harris41 4430:
1.112 bowersj2 4431: =item * $regexp: if string matches this regexp, the string will be
4432: sent to $gradesub
1.14 harris41 4433:
1.112 bowersj2 4434: =item * $gradesub: routine that processes the string if it matches $regexp
1.14 harris41 4435:
1.1199 raeburn 4436: =item * $usec: section of the desired student
4437:
4438: =item * $identifier: counter for student (multiple students one problem) or
4439: problem (one student; whole sequence).
4440:
1.112 bowersj2 4441: =back
1.14 harris41 4442:
1.112 bowersj2 4443: The output string is a table containing all desired attempts, if any.
1.16 harris41 4444:
1.112 bowersj2 4445: =cut
1.1 albertel 4446:
4447: sub get_previous_attempt {
1.1199 raeburn 4448: my ($symb,$username,$domain,$course,$getattempt,$regexp,$gradesub,$usec,$identifier)=@_;
1.1 albertel 4449: my $prevattempts='';
1.43 ng 4450: no strict 'refs';
1.1 albertel 4451: if ($symb) {
1.3 albertel 4452: my (%returnhash)=
4453: &Apache::lonnet::restore($symb,$course,$domain,$username);
1.1 albertel 4454: if ($returnhash{'version'}) {
4455: my %lasthash=();
4456: my $version;
4457: for ($version=1;$version<=$returnhash{'version'};$version++) {
1.1212 raeburn 4458: foreach my $key (reverse(sort(split(/\:/,$returnhash{$version.':keys'})))) {
4459: if ($key =~ /\.rawrndseed$/) {
4460: my ($id) = ($key =~ /^(.+)\.rawrndseed$/);
4461: $lasthash{$id.'.rndseed'} = $returnhash{$version.':'.$key};
4462: } else {
4463: $lasthash{$key}=$returnhash{$version.':'.$key};
4464: }
1.19 harris41 4465: }
1.1 albertel 4466: }
1.596 albertel 4467: $prevattempts=&start_data_table().&start_data_table_header_row();
4468: $prevattempts.='<th>'.&mt('History').'</th>';
1.1199 raeburn 4469: my (%typeparts,%lasthidden,%regraded,%hidestatus);
1.945 raeburn 4470: my $showsurv=&Apache::lonnet::allowed('vas',$env{'request.course.id'});
1.356 albertel 4471: foreach my $key (sort(keys(%lasthash))) {
4472: my ($ign,@parts) = split(/\./,$key);
1.41 ng 4473: if ($#parts > 0) {
1.31 albertel 4474: my $data=$parts[-1];
1.989 raeburn 4475: next if ($data eq 'foilorder');
1.31 albertel 4476: pop(@parts);
1.1010 www 4477: $prevattempts.='<th>'.&mt('Part ').join('.',@parts).'<br />'.$data.' </th>';
1.945 raeburn 4478: if ($data eq 'type') {
4479: unless ($showsurv) {
4480: my $id = join(',',@parts);
4481: $typeparts{$ign.'.'.$id} = $lasthash{$key};
1.978 raeburn 4482: if (($lasthash{$key} eq 'anonsurvey') || ($lasthash{$key} eq 'anonsurveycred')) {
4483: $lasthidden{$ign.'.'.$id} = 1;
4484: }
1.945 raeburn 4485: }
1.1199 raeburn 4486: if ($identifier ne '') {
4487: my $id = join(',',@parts);
4488: if (&Apache::lonnet::EXT("resource.$id.problemstatus",$symb,
4489: $domain,$username,$usec,undef,$course) =~ /^no/) {
4490: $hidestatus{$ign.'.'.$id} = 1;
4491: }
4492: }
4493: } elsif ($data eq 'regrader') {
4494: if (($identifier ne '') && (@parts)) {
1.1200 raeburn 4495: my $id = join(',',@parts);
4496: $regraded{$ign.'.'.$id} = 1;
1.1199 raeburn 4497: }
1.1010 www 4498: }
1.31 albertel 4499: } else {
1.41 ng 4500: if ($#parts == 0) {
4501: $prevattempts.='<th>'.$parts[0].'</th>';
4502: } else {
4503: $prevattempts.='<th>'.$ign.'</th>';
4504: }
1.31 albertel 4505: }
1.16 harris41 4506: }
1.596 albertel 4507: $prevattempts.=&end_data_table_header_row();
1.40 ng 4508: if ($getattempt eq '') {
1.1199 raeburn 4509: my (%solved,%resets,%probstatus);
1.1200 raeburn 4510: if (($identifier ne '') && (keys(%regraded) > 0)) {
4511: for ($version=1;$version<=$returnhash{'version'};$version++) {
4512: foreach my $id (keys(%regraded)) {
4513: if (($returnhash{$version.':'.$id.'.regrader'}) &&
4514: ($returnhash{$version.':'.$id.'.tries'} eq '') &&
4515: ($returnhash{$version.':'.$id.'.award'} eq '')) {
4516: push(@{$resets{$id}},$version);
1.1199 raeburn 4517: }
4518: }
4519: }
1.1200 raeburn 4520: }
4521: for ($version=1;$version<=$returnhash{'version'};$version++) {
1.1199 raeburn 4522: my (@hidden,@unsolved);
1.945 raeburn 4523: if (%typeparts) {
4524: foreach my $id (keys(%typeparts)) {
1.1199 raeburn 4525: if (($returnhash{$version.':'.$id.'.type'} eq 'anonsurvey') ||
4526: ($returnhash{$version.':'.$id.'.type'} eq 'anonsurveycred')) {
1.945 raeburn 4527: push(@hidden,$id);
1.1199 raeburn 4528: } elsif ($identifier ne '') {
4529: unless (($returnhash{$version.':'.$id.'.type'} eq 'survey') ||
4530: ($returnhash{$version.':'.$id.'.type'} eq 'surveycred') ||
4531: ($hidestatus{$id})) {
1.1200 raeburn 4532: next if ((ref($resets{$id}) eq 'ARRAY') && grep(/^\Q$version\E$/,@{$resets{$id}}));
1.1199 raeburn 4533: if ($returnhash{$version.':'.$id.'.solved'} eq 'correct_by_student') {
4534: push(@{$solved{$id}},$version);
4535: } elsif (($returnhash{$version.':'.$id.'.solved'} ne '') &&
4536: (ref($solved{$id}) eq 'ARRAY')) {
4537: my $skip;
4538: if (ref($resets{$id}) eq 'ARRAY') {
4539: foreach my $reset (@{$resets{$id}}) {
4540: if ($reset > $solved{$id}[-1]) {
4541: $skip=1;
4542: last;
4543: }
4544: }
4545: }
4546: unless ($skip) {
4547: my ($ign,$partslist) = split(/\./,$id,2);
4548: push(@unsolved,$partslist);
4549: }
4550: }
4551: }
1.945 raeburn 4552: }
4553: }
4554: }
4555: $prevattempts.=&start_data_table_row().
1.1199 raeburn 4556: '<td>'.&mt('Transaction [_1]',$version);
4557: if (@unsolved) {
4558: $prevattempts .= '<span class="LC_nobreak"><label>'.
4559: '<input type="checkbox" name="HIDE'.$identifier.'" value="'.$version.':'.join('_',@unsolved).'" />'.
4560: &mt('Hide').'</label></span>';
4561: }
4562: $prevattempts .= '</td>';
1.945 raeburn 4563: if (@hidden) {
4564: foreach my $key (sort(keys(%lasthash))) {
1.989 raeburn 4565: next if ($key =~ /\.foilorder$/);
1.945 raeburn 4566: my $hide;
4567: foreach my $id (@hidden) {
4568: if ($key =~ /^\Q$id\E/) {
4569: $hide = 1;
4570: last;
4571: }
4572: }
4573: if ($hide) {
4574: my ($id,$data) = ($key =~ /^(.+)\.([^.]+)$/);
4575: if (($data eq 'award') || ($data eq 'awarddetail')) {
4576: my $value = &format_previous_attempt_value($key,
4577: $returnhash{$version.':'.$key});
1.1173 kruse 4578: $prevattempts.='<td>'.$value.' </td>';
1.945 raeburn 4579: } else {
4580: $prevattempts.='<td> </td>';
4581: }
4582: } else {
4583: if ($key =~ /\./) {
1.1212 raeburn 4584: my $value = $returnhash{$version.':'.$key};
4585: if ($key =~ /\.rndseed$/) {
4586: my ($id) = ($key =~ /^(.+)\.[^.]+$/);
4587: if (exists($returnhash{$version.':'.$id.'.rawrndseed'})) {
4588: $value = $returnhash{$version.':'.$id.'.rawrndseed'};
4589: }
4590: }
4591: $prevattempts.='<td>'.&format_previous_attempt_value($key,$value).
4592: ' </td>';
1.945 raeburn 4593: } else {
4594: $prevattempts.='<td> </td>';
4595: }
4596: }
4597: }
4598: } else {
4599: foreach my $key (sort(keys(%lasthash))) {
1.989 raeburn 4600: next if ($key =~ /\.foilorder$/);
1.1212 raeburn 4601: my $value = $returnhash{$version.':'.$key};
4602: if ($key =~ /\.rndseed$/) {
4603: my ($id) = ($key =~ /^(.+)\.[^.]+$/);
4604: if (exists($returnhash{$version.':'.$id.'.rawrndseed'})) {
4605: $value = $returnhash{$version.':'.$id.'.rawrndseed'};
4606: }
4607: }
4608: $prevattempts.='<td>'.&format_previous_attempt_value($key,$value).
4609: ' </td>';
1.945 raeburn 4610: }
4611: }
4612: $prevattempts.=&end_data_table_row();
1.40 ng 4613: }
1.1 albertel 4614: }
1.945 raeburn 4615: my @currhidden = keys(%lasthidden);
1.596 albertel 4616: $prevattempts.=&start_data_table_row().'<td>'.&mt('Current').'</td>';
1.356 albertel 4617: foreach my $key (sort(keys(%lasthash))) {
1.989 raeburn 4618: next if ($key =~ /\.foilorder$/);
1.945 raeburn 4619: if (%typeparts) {
4620: my $hidden;
4621: foreach my $id (@currhidden) {
4622: if ($key =~ /^\Q$id\E/) {
4623: $hidden = 1;
4624: last;
4625: }
4626: }
4627: if ($hidden) {
4628: my ($id,$data) = ($key =~ /^(.+)\.([^.]+)$/);
4629: if (($data eq 'award') || ($data eq 'awarddetail')) {
4630: my $value = &format_previous_attempt_value($key,$lasthash{$key});
4631: if ($key =~/$regexp$/ && (defined &$gradesub)) {
4632: $value = &$gradesub($value);
4633: }
1.1173 kruse 4634: $prevattempts.='<td>'. $value.' </td>';
1.945 raeburn 4635: } else {
4636: $prevattempts.='<td> </td>';
4637: }
4638: } else {
4639: my $value = &format_previous_attempt_value($key,$lasthash{$key});
4640: if ($key =~/$regexp$/ && (defined &$gradesub)) {
4641: $value = &$gradesub($value);
4642: }
1.1173 kruse 4643: $prevattempts.='<td>'.$value.' </td>';
1.945 raeburn 4644: }
4645: } else {
4646: my $value = &format_previous_attempt_value($key,$lasthash{$key});
4647: if ($key =~/$regexp$/ && (defined &$gradesub)) {
4648: $value = &$gradesub($value);
4649: }
1.1173 kruse 4650: $prevattempts.='<td>'.$value.' </td>';
1.945 raeburn 4651: }
1.16 harris41 4652: }
1.596 albertel 4653: $prevattempts.= &end_data_table_row().&end_data_table();
1.1 albertel 4654: } else {
1.1305 raeburn 4655: my $msg;
4656: if ($symb =~ /ext\.tool$/) {
4657: $msg = &mt('No grade passed back.');
4658: } else {
4659: $msg = &mt('Nothing submitted - no attempts.');
4660: }
1.596 albertel 4661: $prevattempts=
4662: &start_data_table().&start_data_table_row().
1.1305 raeburn 4663: '<td>'.$msg.'</td>'.
1.596 albertel 4664: &end_data_table_row().&end_data_table();
1.1 albertel 4665: }
4666: } else {
1.596 albertel 4667: $prevattempts=
4668: &start_data_table().&start_data_table_row().
4669: '<td>'.&mt('No data.').'</td>'.
4670: &end_data_table_row().&end_data_table();
1.1 albertel 4671: }
1.10 albertel 4672: }
4673:
1.581 albertel 4674: sub format_previous_attempt_value {
4675: my ($key,$value) = @_;
1.1011 www 4676: if (($key =~ /timestamp/) || ($key=~/duedate/)) {
1.1173 kruse 4677: $value = &Apache::lonlocal::locallocaltime($value);
1.581 albertel 4678: } elsif (ref($value) eq 'ARRAY') {
1.1173 kruse 4679: $value = &HTML::Entities::encode('('.join(', ', @{ $value }).')','"<>&');
1.988 raeburn 4680: } elsif ($key =~ /answerstring$/) {
4681: my %answers = &Apache::lonnet::str2hash($value);
1.1173 kruse 4682: my @answer = %answers;
4683: %answers = map {&HTML::Entities::encode($_, '"<>&')} @answer;
1.988 raeburn 4684: my @anskeys = sort(keys(%answers));
4685: if (@anskeys == 1) {
4686: my $answer = $answers{$anskeys[0]};
1.1001 raeburn 4687: if ($answer =~ m{\0}) {
4688: $answer =~ s{\0}{,}g;
1.988 raeburn 4689: }
4690: my $tag_internal_answer_name = 'INTERNAL';
4691: if ($anskeys[0] eq $tag_internal_answer_name) {
4692: $value = $answer;
4693: } else {
4694: $value = $anskeys[0].'='.$answer;
4695: }
4696: } else {
4697: foreach my $ans (@anskeys) {
4698: my $answer = $answers{$ans};
1.1001 raeburn 4699: if ($answer =~ m{\0}) {
4700: $answer =~ s{\0}{,}g;
1.988 raeburn 4701: }
4702: $value .= $ans.'='.$answer.'<br />';;
4703: }
4704: }
1.581 albertel 4705: } else {
1.1173 kruse 4706: $value = &HTML::Entities::encode(&unescape($value), '"<>&');
1.581 albertel 4707: }
4708: return $value;
4709: }
4710:
4711:
1.107 albertel 4712: sub relative_to_absolute {
4713: my ($url,$output)=@_;
4714: my $parser=HTML::TokeParser->new(\$output);
4715: my $token;
4716: my $thisdir=$url;
4717: my @rlinks=();
4718: while ($token=$parser->get_token) {
4719: if ($token->[0] eq 'S') {
4720: if ($token->[1] eq 'a') {
4721: if ($token->[2]->{'href'}) {
4722: $rlinks[$#rlinks+1]=$token->[2]->{'href'};
4723: }
4724: } elsif ($token->[1] eq 'img' || $token->[1] eq 'embed' ) {
4725: $rlinks[$#rlinks+1]=$token->[2]->{'src'};
4726: } elsif ($token->[1] eq 'base') {
4727: $thisdir=$token->[2]->{'href'};
4728: }
4729: }
4730: }
4731: $thisdir=~s-/[^/]*$--;
1.356 albertel 4732: foreach my $link (@rlinks) {
1.726 raeburn 4733: unless (($link=~/^https?\:\/\//i) ||
1.356 albertel 4734: ($link=~/^\//) ||
4735: ($link=~/^javascript:/i) ||
4736: ($link=~/^mailto:/i) ||
4737: ($link=~/^\#/)) {
4738: my $newlocation=&Apache::lonnet::hreflocation($thisdir,$link);
4739: $output=~s/(\"|\'|\=\s*)\Q$link\E(\"|\'|\s|\>)/$1$newlocation$2/;
1.107 albertel 4740: }
4741: }
4742: # -------------------------------------------------- Deal with Applet codebases
4743: $output=~s/(\<applet[^\>]+)(codebase\=[^\S\>]+)*([^\>]*)\>/$1.($2?$2:' codebase="'.$thisdir.'"').$3.'>'/gei;
4744: return $output;
4745: }
4746:
1.112 bowersj2 4747: =pod
4748:
1.648 raeburn 4749: =item * &get_student_view()
1.112 bowersj2 4750:
4751: show a snapshot of what student was looking at
4752:
4753: =cut
4754:
1.10 albertel 4755: sub get_student_view {
1.186 albertel 4756: my ($symb,$username,$domain,$courseid,$target,$moreenv) = @_;
1.114 www 4757: my ($map,$id,$feedurl) = &Apache::lonnet::decode_symb($symb);
1.186 albertel 4758: my (%form);
1.10 albertel 4759: my @elements=('symb','courseid','domain','username');
4760: foreach my $element (@elements) {
1.186 albertel 4761: $form{'grade_'.$element}=eval '$'.$element #'
1.10 albertel 4762: }
1.186 albertel 4763: if (defined($moreenv)) {
4764: %form=(%form,%{$moreenv});
4765: }
1.236 albertel 4766: if (defined($target)) { $form{'grade_target'} = $target; }
1.107 albertel 4767: $feedurl=&Apache::lonnet::clutter($feedurl);
1.1306 raeburn 4768: if (($feedurl =~ /ext\.tool$/) && ($target eq 'tex')) {
4769: $feedurl =~ s{^/adm/wrapper}{};
4770: }
1.650 www 4771: my ($userview,$response)=&Apache::lonnet::ssi_body($feedurl,%form);
1.11 albertel 4772: $userview=~s/\<body[^\>]*\>//gi;
4773: $userview=~s/\<\/body\>//gi;
4774: $userview=~s/\<html\>//gi;
4775: $userview=~s/\<\/html\>//gi;
4776: $userview=~s/\<head\>//gi;
4777: $userview=~s/\<\/head\>//gi;
4778: $userview=~s/action\s*\=/would_be_action\=/gi;
1.107 albertel 4779: $userview=&relative_to_absolute($feedurl,$userview);
1.650 www 4780: if (wantarray) {
4781: return ($userview,$response);
4782: } else {
4783: return $userview;
4784: }
4785: }
4786:
4787: sub get_student_view_with_retries {
4788: my ($symb,$retries,$username,$domain,$courseid,$target,$moreenv) = @_;
4789:
4790: my $ok = 0; # True if we got a good response.
4791: my $content;
4792: my $response;
4793:
4794: # Try to get the student_view done. within the retries count:
4795:
4796: do {
4797: ($content, $response) = &get_student_view($symb,$username,$domain,$courseid,$target,$moreenv);
4798: $ok = $response->is_success;
4799: if (!$ok) {
4800: &Apache::lonnet::logthis("Failed get_student_view_with_retries on $symb: ".$response->is_success.', '.$response->code.', '.$response->message);
4801: }
4802: $retries--;
4803: } while (!$ok && ($retries > 0));
4804:
4805: if (!$ok) {
4806: $content = ''; # On error return an empty content.
4807: }
1.651 www 4808: if (wantarray) {
4809: return ($content, $response);
4810: } else {
4811: return $content;
4812: }
1.11 albertel 4813: }
4814:
1.112 bowersj2 4815: =pod
4816:
1.648 raeburn 4817: =item * &get_student_answers()
1.112 bowersj2 4818:
4819: show a snapshot of how student was answering problem
4820:
4821: =cut
4822:
1.11 albertel 4823: sub get_student_answers {
1.100 sakharuk 4824: my ($symb,$username,$domain,$courseid,%form) = @_;
1.114 www 4825: my ($map,$id,$feedurl) = &Apache::lonnet::decode_symb($symb);
1.186 albertel 4826: my (%moreenv);
1.11 albertel 4827: my @elements=('symb','courseid','domain','username');
4828: foreach my $element (@elements) {
1.186 albertel 4829: $moreenv{'grade_'.$element}=eval '$'.$element #'
1.10 albertel 4830: }
1.186 albertel 4831: $moreenv{'grade_target'}='answer';
4832: %moreenv=(%form,%moreenv);
1.497 raeburn 4833: $feedurl = &Apache::lonnet::clutter($feedurl);
4834: my $userview=&Apache::lonnet::ssi($feedurl,%moreenv);
1.10 albertel 4835: return $userview;
1.1 albertel 4836: }
1.116 albertel 4837:
4838: =pod
4839:
4840: =item * &submlink()
4841:
1.242 albertel 4842: Inputs: $text $uname $udom $symb $target
1.116 albertel 4843:
4844: Returns: A link to grades.pm such as to see the SUBM view of a student
4845:
4846: =cut
4847:
4848: ###############################################
4849: sub submlink {
1.242 albertel 4850: my ($text,$uname,$udom,$symb,$target)=@_;
1.116 albertel 4851: if (!($uname && $udom)) {
4852: (my $cursymb, my $courseid,$udom,$uname)=
1.463 albertel 4853: &Apache::lonnet::whichuser($symb);
1.116 albertel 4854: if (!$symb) { $symb=$cursymb; }
4855: }
1.254 matthew 4856: if (!$symb) { $symb=&Apache::lonnet::symbread(); }
1.369 www 4857: $symb=&escape($symb);
1.960 bisitz 4858: if ($target) { $target=" target=\"$target\""; }
4859: return
4860: '<a href="/adm/grades?command=submission'.
4861: '&symb='.$symb.
4862: '&student='.$uname.
4863: '&userdom='.$udom.'"'.
4864: $target.'>'.$text.'</a>';
1.242 albertel 4865: }
4866: ##############################################
4867:
4868: =pod
4869:
4870: =item * &pgrdlink()
4871:
4872: Inputs: $text $uname $udom $symb $target
4873:
4874: Returns: A link to grades.pm such as to see the PGRD view of a student
4875:
4876: =cut
4877:
4878: ###############################################
4879: sub pgrdlink {
4880: my $link=&submlink(@_);
4881: $link=~s/(&command=submission)/$1&showgrading=yes/;
4882: return $link;
4883: }
4884: ##############################################
4885:
4886: =pod
4887:
4888: =item * &pprmlink()
4889:
4890: Inputs: $text $uname $udom $symb $target
4891:
4892: Returns: A link to parmset.pm such as to see the PPRM view of a
1.283 albertel 4893: student and a specific resource
1.242 albertel 4894:
4895: =cut
4896:
4897: ###############################################
4898: sub pprmlink {
4899: my ($text,$uname,$udom,$symb,$target)=@_;
4900: if (!($uname && $udom)) {
4901: (my $cursymb, my $courseid,$udom,$uname)=
1.463 albertel 4902: &Apache::lonnet::whichuser($symb);
1.242 albertel 4903: if (!$symb) { $symb=$cursymb; }
4904: }
1.254 matthew 4905: if (!$symb) { $symb=&Apache::lonnet::symbread(); }
1.369 www 4906: $symb=&escape($symb);
1.242 albertel 4907: if ($target) { $target="target=\"$target\""; }
1.595 albertel 4908: return '<a href="/adm/parmset?command=set&'.
4909: 'symb='.$symb.'&uname='.$uname.
4910: '&udom='.$udom.'" '.$target.'>'.$text.'</a>';
1.116 albertel 4911: }
4912: ##############################################
1.37 matthew 4913:
1.112 bowersj2 4914: =pod
4915:
4916: =back
4917:
4918: =cut
4919:
1.37 matthew 4920: ###############################################
1.51 www 4921:
4922:
4923: sub timehash {
1.687 raeburn 4924: my ($thistime) = @_;
4925: my $timezone = &Apache::lonlocal::gettimezone();
4926: my $dt = DateTime->from_epoch(epoch => $thistime)
4927: ->set_time_zone($timezone);
4928: my $wday = $dt->day_of_week();
4929: if ($wday == 7) { $wday = 0; }
4930: return ( 'second' => $dt->second(),
4931: 'minute' => $dt->minute(),
4932: 'hour' => $dt->hour(),
4933: 'day' => $dt->day_of_month(),
4934: 'month' => $dt->month(),
4935: 'year' => $dt->year(),
4936: 'weekday' => $wday,
4937: 'dayyear' => $dt->day_of_year(),
4938: 'dlsav' => $dt->is_dst() );
1.51 www 4939: }
4940:
1.370 www 4941: sub utc_string {
4942: my ($date)=@_;
1.371 www 4943: return strftime("%Y%m%dT%H%M%SZ",gmtime($date));
1.370 www 4944: }
4945:
1.51 www 4946: sub maketime {
4947: my %th=@_;
1.687 raeburn 4948: my ($epoch_time,$timezone,$dt);
4949: $timezone = &Apache::lonlocal::gettimezone();
4950: eval {
4951: $dt = DateTime->new( year => $th{'year'},
4952: month => $th{'month'},
4953: day => $th{'day'},
4954: hour => $th{'hour'},
4955: minute => $th{'minute'},
4956: second => $th{'second'},
4957: time_zone => $timezone,
4958: );
4959: };
4960: if (!$@) {
4961: $epoch_time = $dt->epoch;
4962: if ($epoch_time) {
4963: return $epoch_time;
4964: }
4965: }
1.51 www 4966: return POSIX::mktime(
4967: ($th{'seconds'},$th{'minutes'},$th{'hours'},
1.210 www 4968: $th{'day'},$th{'month'}-1,$th{'year'}-1900,0,0,-1));
1.70 www 4969: }
4970:
4971: #########################################
1.51 www 4972:
4973: sub findallcourses {
1.482 raeburn 4974: my ($roles,$uname,$udom) = @_;
1.355 albertel 4975: my %roles;
4976: if (ref($roles)) { %roles = map { $_ => 1 } @{$roles}; }
1.348 albertel 4977: my %courses;
1.51 www 4978: my $now=time;
1.482 raeburn 4979: if (!defined($uname)) {
4980: $uname = $env{'user.name'};
4981: }
4982: if (!defined($udom)) {
4983: $udom = $env{'user.domain'};
4984: }
4985: if (($uname ne $env{'user.name'}) || ($udom ne $env{'user.domain'})) {
1.1073 raeburn 4986: my %roleshash = &Apache::lonnet::dump('roles',$udom,$uname);
1.482 raeburn 4987: if (!%roles) {
4988: %roles = (
4989: cc => 1,
1.907 raeburn 4990: co => 1,
1.482 raeburn 4991: in => 1,
4992: ep => 1,
4993: ta => 1,
4994: cr => 1,
4995: st => 1,
4996: );
4997: }
4998: foreach my $entry (keys(%roleshash)) {
4999: my ($trole,$tend,$tstart) = split(/_/,$roleshash{$entry});
5000: if ($trole =~ /^cr/) {
5001: next if (!exists($roles{$trole}) && !exists($roles{'cr'}));
5002: } else {
5003: next if (!exists($roles{$trole}));
5004: }
5005: if ($tend) {
5006: next if ($tend < $now);
5007: }
5008: if ($tstart) {
5009: next if ($tstart > $now);
5010: }
1.1058 raeburn 5011: my ($cdom,$cnum,$sec,$cnumpart,$secpart,$role);
1.482 raeburn 5012: (undef,$cdom,$cnumpart,$secpart) = split(/\//,$entry);
1.1058 raeburn 5013: my $value = $trole.'/'.$cdom.'/';
1.482 raeburn 5014: if ($secpart eq '') {
5015: ($cnum,$role) = split(/_/,$cnumpart);
5016: $sec = 'none';
1.1058 raeburn 5017: $value .= $cnum.'/';
1.482 raeburn 5018: } else {
5019: $cnum = $cnumpart;
5020: ($sec,$role) = split(/_/,$secpart);
1.1058 raeburn 5021: $value .= $cnum.'/'.$sec;
5022: }
5023: if (ref($courses{$cdom.'_'.$cnum}{$sec}) eq 'ARRAY') {
5024: unless (grep(/^\Q$value\E$/,@{$courses{$cdom.'_'.$cnum}{$sec}})) {
5025: push(@{$courses{$cdom.'_'.$cnum}{$sec}},$value);
5026: }
5027: } else {
5028: @{$courses{$cdom.'_'.$cnum}{$sec}} = ($value);
1.490 raeburn 5029: }
1.482 raeburn 5030: }
5031: } else {
5032: foreach my $key (keys(%env)) {
1.483 albertel 5033: if ( $key=~m{^user\.role\.(\w+)\./($match_domain)/($match_courseid)/?(\w*)$} ||
5034: $key=~m{^user\.role\.(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_courseid)/?(\w*)$}) {
1.482 raeburn 5035: my ($role,$cdom,$cnum,$sec) = ($1,$2,$3,$4);
5036: next if ($role eq 'ca' || $role eq 'aa');
5037: next if (%roles && !exists($roles{$role}));
5038: my ($starttime,$endtime)=split(/\./,$env{$key});
5039: my $active=1;
5040: if ($starttime) {
5041: if ($now<$starttime) { $active=0; }
5042: }
5043: if ($endtime) {
5044: if ($now>$endtime) { $active=0; }
5045: }
5046: if ($active) {
1.1058 raeburn 5047: my $value = $role.'/'.$cdom.'/'.$cnum.'/';
1.482 raeburn 5048: if ($sec eq '') {
5049: $sec = 'none';
1.1058 raeburn 5050: } else {
5051: $value .= $sec;
5052: }
5053: if (ref($courses{$cdom.'_'.$cnum}{$sec}) eq 'ARRAY') {
5054: unless (grep(/^\Q$value\E$/,@{$courses{$cdom.'_'.$cnum}{$sec}})) {
5055: push(@{$courses{$cdom.'_'.$cnum}{$sec}},$value);
5056: }
5057: } else {
5058: @{$courses{$cdom.'_'.$cnum}{$sec}} = ($value);
1.482 raeburn 5059: }
1.474 raeburn 5060: }
5061: }
1.51 www 5062: }
5063: }
1.474 raeburn 5064: return %courses;
1.51 www 5065: }
1.37 matthew 5066:
1.54 www 5067: ###############################################
1.474 raeburn 5068:
5069: sub blockcheck {
1.1189 raeburn 5070: my ($setters,$activity,$uname,$udom,$url,$is_course) = @_;
1.490 raeburn 5071:
1.1189 raeburn 5072: if (defined($udom) && defined($uname)) {
5073: # If uname and udom are for a course, check for blocks in the course.
5074: if (($is_course) || (&Apache::lonnet::is_course($udom,$uname))) {
5075: my ($startblock,$endblock,$triggerblock) =
5076: &get_blocks($setters,$activity,$udom,$uname,$url);
5077: return ($startblock,$endblock,$triggerblock);
5078: }
5079: } else {
1.490 raeburn 5080: $udom = $env{'user.domain'};
5081: $uname = $env{'user.name'};
5082: }
5083:
1.502 raeburn 5084: my $startblock = 0;
5085: my $endblock = 0;
1.1062 raeburn 5086: my $triggerblock = '';
1.482 raeburn 5087: my %live_courses = &findallcourses(undef,$uname,$udom);
1.474 raeburn 5088:
1.490 raeburn 5089: # If uname is for a user, and activity is course-specific, i.e.,
5090: # boards, chat or groups, check for blocking in current course only.
1.474 raeburn 5091:
1.490 raeburn 5092: if (($activity eq 'boards' || $activity eq 'chat' ||
1.1282 raeburn 5093: $activity eq 'groups' || $activity eq 'printout' ||
5094: $activity eq 'reinit' || $activity eq 'alert') &&
1.1189 raeburn 5095: ($env{'request.course.id'})) {
1.490 raeburn 5096: foreach my $key (keys(%live_courses)) {
5097: if ($key ne $env{'request.course.id'}) {
5098: delete($live_courses{$key});
5099: }
5100: }
5101: }
5102:
5103: my $otheruser = 0;
5104: my %own_courses;
5105: if ((($uname ne $env{'user.name'})) || ($udom ne $env{'user.domain'})) {
5106: # Resource belongs to user other than current user.
5107: $otheruser = 1;
5108: # Gather courses for current user
5109: %own_courses =
5110: &findallcourses(undef,$env{'user.name'},$env{'user.domain'});
5111: }
5112:
5113: # Gather active course roles - course coordinator, instructor,
5114: # exam proctor, ta, student, or custom role.
1.474 raeburn 5115:
5116: foreach my $course (keys(%live_courses)) {
1.482 raeburn 5117: my ($cdom,$cnum);
5118: if ((defined($env{'course.'.$course.'.domain'})) && (defined($env{'course.'.$course.'.num'}))) {
5119: $cdom = $env{'course.'.$course.'.domain'};
5120: $cnum = $env{'course.'.$course.'.num'};
5121: } else {
1.490 raeburn 5122: ($cdom,$cnum) = split(/_/,$course);
1.482 raeburn 5123: }
5124: my $no_ownblock = 0;
5125: my $no_userblock = 0;
1.533 raeburn 5126: if ($otheruser && $activity ne 'com') {
1.490 raeburn 5127: # Check if current user has 'evb' priv for this
5128: if (defined($own_courses{$course})) {
5129: foreach my $sec (keys(%{$own_courses{$course}})) {
5130: my $checkrole = 'cm./'.$cdom.'/'.$cnum;
5131: if ($sec ne 'none') {
5132: $checkrole .= '/'.$sec;
5133: }
5134: if (&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) {
5135: $no_ownblock = 1;
5136: last;
5137: }
5138: }
5139: }
5140: # if they have 'evb' priv and are currently not playing student
5141: next if (($no_ownblock) &&
5142: ($env{'request.role'} !~ m{^st\./$cdom/$cnum}));
5143: }
1.474 raeburn 5144: foreach my $sec (keys(%{$live_courses{$course}})) {
1.482 raeburn 5145: my $checkrole = 'cm./'.$cdom.'/'.$cnum;
1.474 raeburn 5146: if ($sec ne 'none') {
1.482 raeburn 5147: $checkrole .= '/'.$sec;
1.474 raeburn 5148: }
1.490 raeburn 5149: if ($otheruser) {
5150: # Resource belongs to user other than current user.
5151: # Assemble privs for that user, and check for 'evb' priv.
1.1058 raeburn 5152: my (%allroles,%userroles);
5153: if (ref($live_courses{$course}{$sec}) eq 'ARRAY') {
5154: foreach my $entry (@{$live_courses{$course}{$sec}}) {
5155: my ($trole,$tdom,$tnum,$tsec);
5156: if ($entry =~ /^cr/) {
5157: ($trole,$tdom,$tnum,$tsec) =
5158: ($entry =~ m|^(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_username)/?(\w*)$|);
5159: } else {
5160: ($trole,$tdom,$tnum,$tsec) = split(/\//,$entry);
5161: }
5162: my ($spec,$area,$trest);
5163: $area = '/'.$tdom.'/'.$tnum;
5164: $trest = $tnum;
5165: if ($tsec ne '') {
5166: $area .= '/'.$tsec;
5167: $trest .= '/'.$tsec;
5168: }
5169: $spec = $trole.'.'.$area;
5170: if ($trole =~ /^cr/) {
5171: &Apache::lonnet::custom_roleprivs(\%allroles,$trole,
5172: $tdom,$spec,$trest,$area);
5173: } else {
5174: &Apache::lonnet::standard_roleprivs(\%allroles,$trole,
5175: $tdom,$spec,$trest,$area);
5176: }
5177: }
1.1276 raeburn 5178: my ($author,$adv,$rar) = &Apache::lonnet::set_userprivs(\%userroles,\%allroles);
1.1058 raeburn 5179: if ($userroles{'user.priv.'.$checkrole} =~ /evb\&([^\:]*)/) {
5180: if ($1) {
5181: $no_userblock = 1;
5182: last;
5183: }
1.486 raeburn 5184: }
5185: }
1.490 raeburn 5186: } else {
5187: # Resource belongs to current user
5188: # Check for 'evb' priv via lonnet::allowed().
1.482 raeburn 5189: if (&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) {
5190: $no_ownblock = 1;
5191: last;
5192: }
1.474 raeburn 5193: }
5194: }
5195: # if they have the evb priv and are currently not playing student
1.482 raeburn 5196: next if (($no_ownblock) &&
1.491 albertel 5197: ($env{'request.role'} !~ m{^st\./\Q$cdom\E/\Q$cnum\E}));
1.482 raeburn 5198: next if ($no_userblock);
1.474 raeburn 5199:
1.1303 raeburn 5200: # Retrieve blocking times and identity of blocker for course
1.490 raeburn 5201: # of specified user, unless user has 'evb' privilege.
1.1284 raeburn 5202:
1.1062 raeburn 5203: my ($start,$end,$trigger) =
5204: &get_blocks($setters,$activity,$cdom,$cnum,$url);
1.502 raeburn 5205: if (($start != 0) &&
5206: (($startblock == 0) || ($startblock > $start))) {
5207: $startblock = $start;
1.1062 raeburn 5208: if ($trigger ne '') {
5209: $triggerblock = $trigger;
5210: }
1.502 raeburn 5211: }
5212: if (($end != 0) &&
5213: (($endblock == 0) || ($endblock < $end))) {
5214: $endblock = $end;
1.1062 raeburn 5215: if ($trigger ne '') {
5216: $triggerblock = $trigger;
5217: }
1.502 raeburn 5218: }
1.490 raeburn 5219: }
1.1062 raeburn 5220: return ($startblock,$endblock,$triggerblock);
1.490 raeburn 5221: }
5222:
5223: sub get_blocks {
1.1062 raeburn 5224: my ($setters,$activity,$cdom,$cnum,$url) = @_;
1.490 raeburn 5225: my $startblock = 0;
5226: my $endblock = 0;
1.1062 raeburn 5227: my $triggerblock = '';
1.490 raeburn 5228: my $course = $cdom.'_'.$cnum;
5229: $setters->{$course} = {};
5230: $setters->{$course}{'staff'} = [];
5231: $setters->{$course}{'times'} = [];
1.1062 raeburn 5232: $setters->{$course}{'triggers'} = [];
5233: my (@blockers,%triggered);
5234: my $now = time;
5235: my %commblocks = &Apache::lonnet::get_comm_blocks($cdom,$cnum);
5236: if ($activity eq 'docs') {
5237: @blockers = &Apache::lonnet::has_comm_blocking('bre',undef,$url,\%commblocks);
5238: foreach my $block (@blockers) {
5239: if ($block =~ /^firstaccess____(.+)$/) {
5240: my $item = $1;
5241: my $type = 'map';
5242: my $timersymb = $item;
5243: if ($item eq 'course') {
5244: $type = 'course';
5245: } elsif ($item =~ /___\d+___/) {
5246: $type = 'resource';
5247: } else {
5248: $timersymb = &Apache::lonnet::symbread($item);
5249: }
5250: my $start = $env{'course.'.$cdom.'_'.$cnum.'.firstaccess.'.$timersymb};
5251: my $end = $start + $env{'course.'.$cdom.'_'.$cnum.'.timerinterval.'.$timersymb};
5252: $triggered{$block} = {
5253: start => $start,
5254: end => $end,
5255: type => $type,
5256: };
5257: }
5258: }
5259: } else {
5260: foreach my $block (keys(%commblocks)) {
5261: if ($block =~ m/^(\d+)____(\d+)$/) {
5262: my ($start,$end) = ($1,$2);
5263: if ($start <= time && $end >= time) {
5264: if (ref($commblocks{$block}) eq 'HASH') {
5265: if (ref($commblocks{$block}{'blocks'}) eq 'HASH') {
5266: if ($commblocks{$block}{'blocks'}{$activity} eq 'on') {
5267: unless(grep(/^\Q$block\E$/,@blockers)) {
5268: push(@blockers,$block);
5269: }
5270: }
5271: }
5272: }
5273: }
5274: } elsif ($block =~ /^firstaccess____(.+)$/) {
5275: my $item = $1;
5276: my $timersymb = $item;
5277: my $type = 'map';
5278: if ($item eq 'course') {
5279: $type = 'course';
5280: } elsif ($item =~ /___\d+___/) {
5281: $type = 'resource';
5282: } else {
5283: $timersymb = &Apache::lonnet::symbread($item);
5284: }
5285: my $start = $env{'course.'.$cdom.'_'.$cnum.'.firstaccess.'.$timersymb};
5286: my $end = $start + $env{'course.'.$cdom.'_'.$cnum.'.timerinterval.'.$timersymb};
5287: if ($start && $end) {
5288: if (($start <= time) && ($end >= time)) {
1.1281 raeburn 5289: if (ref($commblocks{$block}) eq 'HASH') {
5290: if (ref($commblocks{$block}{'blocks'}) eq 'HASH') {
5291: if ($commblocks{$block}{'blocks'}{$activity} eq 'on') {
5292: unless(grep(/^\Q$block\E$/,@blockers)) {
5293: push(@blockers,$block);
5294: $triggered{$block} = {
5295: start => $start,
5296: end => $end,
5297: type => $type,
5298: };
5299: }
5300: }
5301: }
1.1062 raeburn 5302: }
5303: }
1.490 raeburn 5304: }
1.1062 raeburn 5305: }
5306: }
5307: }
5308: foreach my $blocker (@blockers) {
5309: my ($staff_name,$staff_dom,$title,$blocks) =
5310: &parse_block_record($commblocks{$blocker});
5311: push(@{$$setters{$course}{'staff'}},[$staff_name,$staff_dom]);
5312: my ($start,$end,$triggertype);
5313: if ($blocker =~ m/^(\d+)____(\d+)$/) {
5314: ($start,$end) = ($1,$2);
5315: } elsif (ref($triggered{$blocker}) eq 'HASH') {
5316: $start = $triggered{$blocker}{'start'};
5317: $end = $triggered{$blocker}{'end'};
5318: $triggertype = $triggered{$blocker}{'type'};
5319: }
5320: if ($start) {
5321: push(@{$$setters{$course}{'times'}}, [$start,$end]);
5322: if ($triggertype) {
5323: push(@{$$setters{$course}{'triggers'}},$triggertype);
5324: } else {
5325: push(@{$$setters{$course}{'triggers'}},0);
5326: }
5327: if ( ($startblock == 0) || ($startblock > $start) ) {
5328: $startblock = $start;
5329: if ($triggertype) {
5330: $triggerblock = $blocker;
1.474 raeburn 5331: }
5332: }
1.1062 raeburn 5333: if ( ($endblock == 0) || ($endblock < $end) ) {
5334: $endblock = $end;
5335: if ($triggertype) {
5336: $triggerblock = $blocker;
5337: }
5338: }
1.474 raeburn 5339: }
5340: }
1.1062 raeburn 5341: return ($startblock,$endblock,$triggerblock);
1.474 raeburn 5342: }
5343:
5344: sub parse_block_record {
5345: my ($record) = @_;
5346: my ($setuname,$setudom,$title,$blocks);
5347: if (ref($record) eq 'HASH') {
5348: ($setuname,$setudom) = split(/:/,$record->{'setter'});
5349: $title = &unescape($record->{'event'});
5350: $blocks = $record->{'blocks'};
5351: } else {
5352: my @data = split(/:/,$record,3);
5353: if (scalar(@data) eq 2) {
5354: $title = $data[1];
5355: ($setuname,$setudom) = split(/@/,$data[0]);
5356: } else {
5357: ($setuname,$setudom,$title) = @data;
5358: }
5359: $blocks = { 'com' => 'on' };
5360: }
5361: return ($setuname,$setudom,$title,$blocks);
5362: }
5363:
1.854 kalberla 5364: sub blocking_status {
1.1189 raeburn 5365: my ($activity,$uname,$udom,$url,$is_course) = @_;
1.1061 raeburn 5366: my %setters;
1.890 droeschl 5367:
1.1061 raeburn 5368: # check for active blocking
1.1062 raeburn 5369: my ($startblock,$endblock,$triggerblock) =
1.1189 raeburn 5370: &blockcheck(\%setters,$activity,$uname,$udom,$url,$is_course);
1.1062 raeburn 5371: my $blocked = 0;
5372: if ($startblock && $endblock) {
5373: $blocked = 1;
5374: }
1.890 droeschl 5375:
1.1061 raeburn 5376: # caller just wants to know whether a block is active
5377: if (!wantarray) { return $blocked; }
5378:
5379: # build a link to a popup window containing the details
5380: my $querystring = "?activity=$activity";
5381: # $uname and $udom decide whose portfolio the user is trying to look at
1.1232 raeburn 5382: if (($activity eq 'port') || ($activity eq 'passwd')) {
5383: $querystring .= "&udom=$udom" if ($udom =~ /^$match_domain$/);
5384: $querystring .= "&uname=$uname" if ($uname =~ /^$match_username$/);
1.1062 raeburn 5385: } elsif ($activity eq 'docs') {
5386: $querystring .= '&url='.&HTML::Entities::encode($url,'&"');
5387: }
1.1061 raeburn 5388:
5389: my $output .= <<'END_MYBLOCK';
5390: function openWindow(url, wdwName, w, h, toolbar,scrollbar) {
5391: var options = "width=" + w + ",height=" + h + ",";
5392: options += "resizable=yes,scrollbars="+scrollbar+",status=no,";
5393: options += "menubar=no,toolbar="+toolbar+",location=no,directories=no";
5394: var newWin = window.open(url, wdwName, options);
5395: newWin.focus();
5396: }
1.890 droeschl 5397: END_MYBLOCK
1.854 kalberla 5398:
1.1061 raeburn 5399: $output = Apache::lonhtmlcommon::scripttag($output);
1.890 droeschl 5400:
1.1061 raeburn 5401: my $popupUrl = "/adm/blockingstatus/$querystring";
1.1062 raeburn 5402: my $text = &mt('Communication Blocked');
1.1217 raeburn 5403: my $class = 'LC_comblock';
1.1062 raeburn 5404: if ($activity eq 'docs') {
5405: $text = &mt('Content Access Blocked');
1.1217 raeburn 5406: $class = '';
1.1063 raeburn 5407: } elsif ($activity eq 'printout') {
5408: $text = &mt('Printing Blocked');
1.1232 raeburn 5409: } elsif ($activity eq 'passwd') {
5410: $text = &mt('Password Changing Blocked');
1.1282 raeburn 5411: } elsif ($activity eq 'alert') {
5412: $text = &mt('Checking Critical Messages Blocked');
5413: } elsif ($activity eq 'reinit') {
5414: $text = &mt('Checking Course Update Blocked');
1.1062 raeburn 5415: }
1.1061 raeburn 5416: $output .= <<"END_BLOCK";
1.1217 raeburn 5417: <div class='$class'>
1.869 kalberla 5418: <a onclick='openWindow("$popupUrl","Blocking Table",600,300,"no","no");return false;' href='/adm/blockingstatus/$querystring'
1.890 droeschl 5419: title='$text'>
5420: <img class='LC_noBorder LC_middle' title='$text' src='/res/adm/pages/comblock.png' alt='$text'/></a>
1.869 kalberla 5421: <a onclick='openWindow("$popupUrl","Blocking Table",600,300,"no","no");return false;' href='/adm/blockingstatus/$querystring'
1.890 droeschl 5422: title='$text'>$text</a>
1.867 kalberla 5423: </div>
5424:
5425: END_BLOCK
1.474 raeburn 5426:
1.1061 raeburn 5427: return ($blocked, $output);
1.854 kalberla 5428: }
1.490 raeburn 5429:
1.60 matthew 5430: ###############################################
5431:
1.682 raeburn 5432: sub check_ip_acc {
1.1201 raeburn 5433: my ($acc,$clientip)=@_;
1.682 raeburn 5434: &Apache::lonxml::debug("acc is $acc");
5435: if (!defined($acc) || $acc =~ /^\s*$/ || $acc =~/^\s*no\s*$/i) {
5436: return 1;
5437: }
1.1219 raeburn 5438: my $allowed;
1.1252 raeburn 5439: my $ip=$ENV{'REMOTE_ADDR'} || $clientip || $env{'request.host'};
1.682 raeburn 5440:
5441: my $name;
1.1219 raeburn 5442: my %access = (
5443: allowfrom => 1,
5444: denyfrom => 0,
5445: );
5446: my @allows;
5447: my @denies;
5448: foreach my $item (split(',',$acc)) {
5449: $item =~ s/^\s*//;
5450: $item =~ s/\s*$//;
5451: my $pattern;
5452: if ($item =~ /^\!(.+)$/) {
5453: push(@denies,$1);
5454: } else {
5455: push(@allows,$item);
5456: }
5457: }
5458: my $numdenies = scalar(@denies);
5459: my $numallows = scalar(@allows);
5460: my $count = 0;
5461: foreach my $pattern (@denies,@allows) {
5462: $count ++;
5463: my $acctype = 'allowfrom';
5464: if ($count <= $numdenies) {
5465: $acctype = 'denyfrom';
5466: }
1.682 raeburn 5467: if ($pattern =~ /\*$/) {
5468: #35.8.*
5469: $pattern=~s/\*//;
1.1219 raeburn 5470: if ($ip =~ /^\Q$pattern\E/) { $allowed=$access{$acctype}; }
1.682 raeburn 5471: } elsif ($pattern =~ /(\d+\.\d+\.\d+)\.\[(\d+)-(\d+)\]$/) {
5472: #35.8.3.[34-56]
5473: my $low=$2;
5474: my $high=$3;
5475: $pattern=$1;
5476: if ($ip =~ /^\Q$pattern\E/) {
5477: my $last=(split(/\./,$ip))[3];
1.1219 raeburn 5478: if ($last <=$high && $last >=$low) { $allowed=$access{$acctype}; }
1.682 raeburn 5479: }
5480: } elsif ($pattern =~ /^\*/) {
5481: #*.msu.edu
5482: $pattern=~s/\*//;
5483: if (!defined($name)) {
5484: use Socket;
5485: my $netaddr=inet_aton($ip);
5486: ($name)=gethostbyaddr($netaddr,AF_INET);
5487: }
1.1219 raeburn 5488: if ($name =~ /\Q$pattern\E$/i) { $allowed=$access{$acctype}; }
1.682 raeburn 5489: } elsif ($pattern =~ /\d+\.\d+\.\d+\.\d+/) {
5490: #127.0.0.1
1.1219 raeburn 5491: if ($ip =~ /^\Q$pattern\E/) { $allowed=$access{$acctype}; }
1.682 raeburn 5492: } else {
5493: #some.name.com
5494: if (!defined($name)) {
5495: use Socket;
5496: my $netaddr=inet_aton($ip);
5497: ($name)=gethostbyaddr($netaddr,AF_INET);
5498: }
1.1219 raeburn 5499: if ($name =~ /\Q$pattern\E$/i) { $allowed=$access{$acctype}; }
5500: }
5501: if ($allowed =~ /^(0|1)$/) { last; }
5502: }
5503: if ($allowed eq '') {
5504: if ($numdenies && !$numallows) {
5505: $allowed = 1;
5506: } else {
5507: $allowed = 0;
1.682 raeburn 5508: }
5509: }
5510: return $allowed;
5511: }
5512:
5513: ###############################################
5514:
1.60 matthew 5515: =pod
5516:
1.112 bowersj2 5517: =head1 Domain Template Functions
5518:
5519: =over 4
5520:
5521: =item * &determinedomain()
1.60 matthew 5522:
5523: Inputs: $domain (usually will be undef)
5524:
1.63 www 5525: Returns: Determines which domain should be used for designs
1.60 matthew 5526:
5527: =cut
1.54 www 5528:
1.60 matthew 5529: ###############################################
1.63 www 5530: sub determinedomain {
5531: my $domain=shift;
1.531 albertel 5532: if (! $domain) {
1.60 matthew 5533: # Determine domain if we have not been given one
1.893 raeburn 5534: $domain = &Apache::lonnet::default_login_domain();
1.258 albertel 5535: if ($env{'user.domain'}) { $domain=$env{'user.domain'}; }
5536: if ($env{'request.role.domain'}) {
5537: $domain=$env{'request.role.domain'};
1.60 matthew 5538: }
5539: }
1.63 www 5540: return $domain;
5541: }
5542: ###############################################
1.517 raeburn 5543:
1.518 albertel 5544: sub devalidate_domconfig_cache {
5545: my ($udom)=@_;
5546: &Apache::lonnet::devalidate_cache_new('domainconfig',$udom);
5547: }
5548:
5549: # ---------------------- Get domain configuration for a domain
5550: sub get_domainconf {
5551: my ($udom) = @_;
5552: my $cachetime=1800;
5553: my ($result,$cached)=&Apache::lonnet::is_cached_new('domainconfig',$udom);
5554: if (defined($cached)) { return %{$result}; }
5555:
5556: my %domconfig = &Apache::lonnet::get_dom('configuration',
1.948 raeburn 5557: ['login','rolecolors','autoenroll'],$udom);
1.632 raeburn 5558: my (%designhash,%legacy);
1.518 albertel 5559: if (keys(%domconfig) > 0) {
5560: if (ref($domconfig{'login'}) eq 'HASH') {
1.632 raeburn 5561: if (keys(%{$domconfig{'login'}})) {
5562: foreach my $key (keys(%{$domconfig{'login'}})) {
1.699 raeburn 5563: if (ref($domconfig{'login'}{$key}) eq 'HASH') {
1.1208 raeburn 5564: if (($key eq 'loginvia') || ($key eq 'headtag')) {
5565: if (ref($domconfig{'login'}{$key}) eq 'HASH') {
5566: foreach my $hostname (keys(%{$domconfig{'login'}{$key}})) {
5567: if (ref($domconfig{'login'}{$key}{$hostname}) eq 'HASH') {
5568: if ($key eq 'loginvia') {
5569: if ($domconfig{'login'}{'loginvia'}{$hostname}{'server'}) {
5570: my $server = $domconfig{'login'}{'loginvia'}{$hostname}{'server'};
5571: $designhash{$udom.'.login.loginvia'} = $server;
5572: if ($domconfig{'login'}{'loginvia'}{$hostname}{'serverpath'} eq 'custom') {
5573:
5574: $designhash{$udom.'.login.loginvia_'.$hostname} = $server.':'.$domconfig{'login'}{'loginvia'}{$hostname}{'custompath'};
5575: } else {
5576: $designhash{$udom.'.login.loginvia_'.$hostname} = $server.':'.$domconfig{'login'}{'loginvia'}{$hostname}{'serverpath'};
5577: }
1.948 raeburn 5578: }
1.1208 raeburn 5579: } elsif ($key eq 'headtag') {
5580: if ($domconfig{'login'}{'headtag'}{$hostname}{'url'}) {
5581: $designhash{$udom.'.login.headtag_'.$hostname} = $domconfig{'login'}{'headtag'}{$hostname}{'url'};
1.948 raeburn 5582: }
1.946 raeburn 5583: }
1.1208 raeburn 5584: if ($domconfig{'login'}{$key}{$hostname}{'exempt'}) {
5585: $designhash{$udom.'.login.'.$key.'_exempt_'.$hostname} = $domconfig{'login'}{$key}{$hostname}{'exempt'};
5586: }
1.946 raeburn 5587: }
5588: }
5589: }
5590: } else {
5591: foreach my $img (keys(%{$domconfig{'login'}{$key}})) {
5592: $designhash{$udom.'.login.'.$key.'_'.$img} =
5593: $domconfig{'login'}{$key}{$img};
5594: }
1.699 raeburn 5595: }
5596: } else {
5597: $designhash{$udom.'.login.'.$key}=$domconfig{'login'}{$key};
5598: }
1.632 raeburn 5599: }
5600: } else {
5601: $legacy{'login'} = 1;
1.518 albertel 5602: }
1.632 raeburn 5603: } else {
5604: $legacy{'login'} = 1;
1.518 albertel 5605: }
5606: if (ref($domconfig{'rolecolors'}) eq 'HASH') {
1.632 raeburn 5607: if (keys(%{$domconfig{'rolecolors'}})) {
5608: foreach my $role (keys(%{$domconfig{'rolecolors'}})) {
5609: if (ref($domconfig{'rolecolors'}{$role}) eq 'HASH') {
5610: foreach my $item (keys(%{$domconfig{'rolecolors'}{$role}})) {
5611: $designhash{$udom.'.'.$role.'.'.$item}=$domconfig{'rolecolors'}{$role}{$item};
5612: }
1.518 albertel 5613: }
5614: }
1.632 raeburn 5615: } else {
5616: $legacy{'rolecolors'} = 1;
1.518 albertel 5617: }
1.632 raeburn 5618: } else {
5619: $legacy{'rolecolors'} = 1;
1.518 albertel 5620: }
1.948 raeburn 5621: if (ref($domconfig{'autoenroll'}) eq 'HASH') {
5622: if ($domconfig{'autoenroll'}{'co-owners'}) {
5623: $designhash{$udom.'.autoassign.co-owners'}=$domconfig{'autoenroll'}{'co-owners'};
5624: }
5625: }
1.632 raeburn 5626: if (keys(%legacy) > 0) {
5627: my %legacyhash = &get_legacy_domconf($udom);
5628: foreach my $item (keys(%legacyhash)) {
5629: if ($item =~ /^\Q$udom\E\.login/) {
5630: if ($legacy{'login'}) {
5631: $designhash{$item} = $legacyhash{$item};
5632: }
5633: } else {
5634: if ($legacy{'rolecolors'}) {
5635: $designhash{$item} = $legacyhash{$item};
5636: }
1.518 albertel 5637: }
5638: }
5639: }
1.632 raeburn 5640: } else {
5641: %designhash = &get_legacy_domconf($udom);
1.518 albertel 5642: }
5643: &Apache::lonnet::do_cache_new('domainconfig',$udom,\%designhash,
5644: $cachetime);
5645: return %designhash;
5646: }
5647:
1.632 raeburn 5648: sub get_legacy_domconf {
5649: my ($udom) = @_;
5650: my %legacyhash;
5651: my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors';
5652: my $designfile = $designdir.'/'.$udom.'.tab';
5653: if (-e $designfile) {
1.1317 raeburn 5654: if ( open (my $fh,'<',$designfile) ) {
1.632 raeburn 5655: while (my $line = <$fh>) {
5656: next if ($line =~ /^\#/);
5657: chomp($line);
5658: my ($key,$val)=(split(/\=/,$line));
5659: if ($val) { $legacyhash{$udom.'.'.$key}=$val; }
5660: }
5661: close($fh);
5662: }
5663: }
1.1026 raeburn 5664: if (-e $Apache::lonnet::perlvar{'lonDocRoot'}.'/adm/lonDomLogos/'.$udom.'.gif') {
1.632 raeburn 5665: $legacyhash{$udom.'.login.domlogo'} = "/adm/lonDomLogos/$udom.gif";
5666: }
5667: return %legacyhash;
5668: }
5669:
1.63 www 5670: =pod
5671:
1.112 bowersj2 5672: =item * &domainlogo()
1.63 www 5673:
5674: Inputs: $domain (usually will be undef)
5675:
5676: Returns: A link to a domain logo, if the domain logo exists.
5677: If the domain logo does not exist, a description of the domain.
5678:
5679: =cut
1.112 bowersj2 5680:
1.63 www 5681: ###############################################
5682: sub domainlogo {
1.517 raeburn 5683: my $domain = &determinedomain(shift);
1.518 albertel 5684: my %designhash = &get_domainconf($domain);
1.517 raeburn 5685: # See if there is a logo
5686: if ($designhash{$domain.'.login.domlogo'} ne '') {
1.519 raeburn 5687: my $imgsrc = $designhash{$domain.'.login.domlogo'};
1.538 albertel 5688: if ($imgsrc =~ m{^/(adm|res)/}) {
5689: if ($imgsrc =~ m{^/res/}) {
5690: my $local_name = &Apache::lonnet::filelocation('',$imgsrc);
5691: &Apache::lonnet::repcopy($local_name);
5692: }
5693: $imgsrc = &lonhttpdurl($imgsrc);
1.519 raeburn 5694: }
5695: return '<img src="'.$imgsrc.'" alt="'.$domain.'" />';
1.514 albertel 5696: } elsif (defined(&Apache::lonnet::domain($domain,'description'))) {
5697: return &Apache::lonnet::domain($domain,'description');
1.59 www 5698: } else {
1.60 matthew 5699: return '';
1.59 www 5700: }
5701: }
1.63 www 5702: ##############################################
5703:
5704: =pod
5705:
1.112 bowersj2 5706: =item * &designparm()
1.63 www 5707:
5708: Inputs: $which parameter; $domain (usually will be undef)
5709:
5710: Returns: value of designparamter $which
5711:
5712: =cut
1.112 bowersj2 5713:
1.397 albertel 5714:
1.400 albertel 5715: ##############################################
1.397 albertel 5716: sub designparm {
5717: my ($which,$domain)=@_;
5718: if (exists($env{'environment.color.'.$which})) {
1.817 bisitz 5719: return $env{'environment.color.'.$which};
1.96 www 5720: }
1.63 www 5721: $domain=&determinedomain($domain);
1.1016 raeburn 5722: my %domdesign;
5723: unless ($domain eq 'public') {
5724: %domdesign = &get_domainconf($domain);
5725: }
1.520 raeburn 5726: my $output;
1.517 raeburn 5727: if ($domdesign{$domain.'.'.$which} ne '') {
1.817 bisitz 5728: $output = $domdesign{$domain.'.'.$which};
1.63 www 5729: } else {
1.520 raeburn 5730: $output = $defaultdesign{$which};
5731: }
5732: if (($which =~ /^(student|coordinator|author|admin)\.img$/) ||
1.635 raeburn 5733: ($which =~ /login\.(img|logo|domlogo|login)/)) {
1.538 albertel 5734: if ($output =~ m{^/(adm|res)/}) {
1.817 bisitz 5735: if ($output =~ m{^/res/}) {
5736: my $local_name = &Apache::lonnet::filelocation('',$output);
5737: &Apache::lonnet::repcopy($local_name);
5738: }
1.520 raeburn 5739: $output = &lonhttpdurl($output);
5740: }
1.63 www 5741: }
1.520 raeburn 5742: return $output;
1.63 www 5743: }
1.59 www 5744:
1.822 bisitz 5745: ##############################################
5746: =pod
5747:
1.832 bisitz 5748: =item * &authorspace()
5749:
1.1028 raeburn 5750: Inputs: $url (usually will be undef).
1.832 bisitz 5751:
1.1132 raeburn 5752: Returns: Path to Authoring Space containing the resource or
1.1028 raeburn 5753: directory being viewed (or for which action is being taken).
5754: If $url is provided, and begins /priv/<domain>/<uname>
5755: the path will be that portion of the $context argument.
5756: Otherwise the path will be for the author space of the current
5757: user when the current role is author, or for that of the
5758: co-author/assistant co-author space when the current role
5759: is co-author or assistant co-author.
1.832 bisitz 5760:
5761: =cut
5762:
5763: sub authorspace {
1.1028 raeburn 5764: my ($url) = @_;
5765: if ($url ne '') {
5766: if ($url =~ m{^(/priv/$match_domain/$match_username/)}) {
5767: return $1;
5768: }
5769: }
1.832 bisitz 5770: my $caname = '';
1.1024 www 5771: my $cadom = '';
1.1028 raeburn 5772: if ($env{'request.role'} =~ /^(?:ca|aa)/) {
1.1024 www 5773: ($cadom,$caname) =
1.832 bisitz 5774: ($env{'request.role'}=~/($match_domain)\/($match_username)$/);
1.1028 raeburn 5775: } elsif ($env{'request.role'} =~ m{^au\./($match_domain)/}) {
1.832 bisitz 5776: $caname = $env{'user.name'};
1.1024 www 5777: $cadom = $env{'user.domain'};
1.832 bisitz 5778: }
1.1028 raeburn 5779: if (($caname ne '') && ($cadom ne '')) {
5780: return "/priv/$cadom/$caname/";
5781: }
5782: return;
1.832 bisitz 5783: }
5784:
5785: ##############################################
5786: =pod
5787:
1.822 bisitz 5788: =item * &head_subbox()
5789:
5790: Inputs: $content (contains HTML code with page functions, etc.)
5791:
5792: Returns: HTML div with $content
5793: To be included in page header
5794:
5795: =cut
5796:
5797: sub head_subbox {
5798: my ($content)=@_;
5799: my $output =
1.993 raeburn 5800: '<div class="LC_head_subbox">'
1.822 bisitz 5801: .$content
5802: .'</div>'
5803: }
5804:
5805: ##############################################
5806: =pod
5807:
5808: =item * &CSTR_pageheader()
5809:
1.1026 raeburn 5810: Input: (optional) filename from which breadcrumb trail is built.
5811: In most cases no input as needed, as $env{'request.filename'}
5812: is appropriate for use in building the breadcrumb trail.
1.822 bisitz 5813:
5814: Returns: HTML div with CSTR path and recent box
1.1132 raeburn 5815: To be included on Authoring Space pages
1.822 bisitz 5816:
5817: =cut
5818:
5819: sub CSTR_pageheader {
1.1026 raeburn 5820: my ($trailfile) = @_;
5821: if ($trailfile eq '') {
5822: $trailfile = $env{'request.filename'};
5823: }
5824:
5825: # this is for resources; directories have customtitle, and crumbs
5826: # and select recent are created in lonpubdir.pm
5827:
5828: my $londocroot = $Apache::lonnet::perlvar{'lonDocRoot'};
1.1022 www 5829: my ($udom,$uname,$thisdisfn)=
1.1113 raeburn 5830: ($trailfile =~ m{^\Q$londocroot\E/priv/([^/]+)/([^/]+)(?:|/(.*))$});
1.1026 raeburn 5831: my $formaction = "/priv/$udom/$uname/$thisdisfn";
5832: $formaction =~ s{/+}{/}g;
1.822 bisitz 5833:
5834: my $parentpath = '';
5835: my $lastitem = '';
5836: if ($thisdisfn =~ m-(.+/)([^/]*)$-) {
5837: $parentpath = $1;
5838: $lastitem = $2;
5839: } else {
5840: $lastitem = $thisdisfn;
5841: }
1.921 bisitz 5842:
1.1246 raeburn 5843: my ($crsauthor,$title);
5844: if (($env{'request.course.id'}) &&
5845: ($env{'course.'.$env{'request.course.id'}.'.num'} eq $uname) &&
1.1247 raeburn 5846: ($env{'course.'.$env{'request.course.id'}.'.domain'} eq $udom)) {
1.1246 raeburn 5847: $crsauthor = 1;
5848: $title = &mt('Course Authoring Space');
5849: } else {
5850: $title = &mt('Authoring Space');
5851: }
5852:
1.1314 raeburn 5853: my ($target,$crumbtarget) = (' target="_top"','_top'); #FIXME lonpubdir: target="_parent"
1.1313 raeburn 5854: if (($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) {
1.1314 raeburn 5855: $target = '';
5856: $crumbtarget = '';
1.1313 raeburn 5857: }
5858:
1.921 bisitz 5859: my $output =
1.822 bisitz 5860: '<div>'
5861: .&Apache::loncommon::help_open_menu('','',3,'Authoring') #FIXME: Broken? Where is it?
1.1246 raeburn 5862: .'<b>'.$title.'</b> '
1.1314 raeburn 5863: .'<form name="dirs" method="post" action="'.$formaction.'"'.$target.'>'
5864: .&Apache::lonhtmlcommon::crumbs($uname.'/'.$parentpath,$crumbtarget,'/priv/'.$udom,undef,undef);
1.921 bisitz 5865:
5866: if ($lastitem) {
5867: $output .=
5868: '<span class="LC_filename">'
5869: .$lastitem
5870: .'</span>';
5871: }
1.1245 raeburn 5872:
1.1246 raeburn 5873: if ($crsauthor) {
5874: $output .= '</form>'.&Apache::lonmenu::constspaceform();
5875: } else {
5876: $output .=
5877: '<br />'
1.1314 raeburn 5878: #FIXME lonpubdir: &Apache::lonhtmlcommon::crumbs($uname.$thisdisfn.'/',$crumbtarget,'/priv','','+1',1)."</b></tt><br />"
1.1246 raeburn 5879: .&Apache::lonhtmlcommon::select_recent('construct','recent','this.form.action=this.form.recent.value;this.form.submit()')
5880: .'</form>'
5881: .&Apache::lonmenu::constspaceform();
5882: }
5883: $output .= '</div>';
1.921 bisitz 5884:
5885: return $output;
1.822 bisitz 5886: }
5887:
1.60 matthew 5888: ###############################################
5889: ###############################################
5890:
5891: =pod
5892:
1.112 bowersj2 5893: =back
5894:
1.549 albertel 5895: =head1 HTML Helpers
1.112 bowersj2 5896:
5897: =over 4
5898:
5899: =item * &bodytag()
1.60 matthew 5900:
5901: Returns a uniform header for LON-CAPA web pages.
5902:
5903: Inputs:
5904:
1.112 bowersj2 5905: =over 4
5906:
5907: =item * $title, A title to be displayed on the page.
5908:
5909: =item * $function, the current role (can be undef).
5910:
5911: =item * $addentries, extra parameters for the <body> tag.
5912:
5913: =item * $bodyonly, if defined, only return the <body> tag.
5914:
5915: =item * $domain, if defined, force a given domain.
5916:
5917: =item * $forcereg, if page should register as content page (relevant for
1.86 www 5918: text interface only)
1.60 matthew 5919:
1.814 bisitz 5920: =item * $no_nav_bar, if true, keep the 'what is this' info but remove the
5921: navigational links
1.317 albertel 5922:
1.338 albertel 5923: =item * $bgcolor, used to override the bgcolor on a webpage to a specific value
5924:
1.460 albertel 5925: =item * $args, optional argument valid values are
5926: no_auto_mt_title -> prevents &mt()ing the title arg
1.1274 raeburn 5927: use_absolute -> for external resource or syllabus, this will
5928: contain https://<hostname> if server uses
5929: https (as per hosts.tab), but request is for http
5930: hostname -> hostname, from $r->hostname().
1.460 albertel 5931:
1.1096 raeburn 5932: =item * $advtoolsref, optional argument, ref to an array containing
5933: inlineremote items to be added in "Functions" menu below
5934: breadcrumbs.
5935:
1.1316 raeburn 5936: =item * $ltiscope, optional argument, will be one of: resource, map or
5937: course, if LON-CAPA is in LTI Provider context. Value is
5938: the scope of use, i.e., launch was for access to a single, a map
5939: or the entire course.
5940:
5941: =item * $ltiuri, optional argument, if LON-CAPA is in LTI Provider
5942: context, this will contain the URL for the landing item in
5943: the course, after launch from an LTI Consumer
5944:
1.1318 raeburn 5945: =item * $ltimenu, optional argument, if LON-CAPA is in LTI Provider
5946: context, this will contain a reference to hash of items
5947: to be included in the page header and/or inline menu.
5948:
1.112 bowersj2 5949: =back
5950:
1.60 matthew 5951: Returns: A uniform header for LON-CAPA web pages.
5952: If $bodyonly is nonzero, a string containing a <body> tag will be returned.
5953: If $bodyonly is undef or zero, an html string containing a <body> tag and
5954: other decorations will be returned.
5955:
5956: =cut
5957:
1.54 www 5958: sub bodytag {
1.831 bisitz 5959: my ($title,$function,$addentries,$bodyonly,$domain,$forcereg,
1.1318 raeburn 5960: $no_nav_bar,$bgcolor,$args,$advtoolsref,$ltiscope,$ltiuri,$ltimenu)=@_;
1.339 albertel 5961:
1.954 raeburn 5962: my $public;
5963: if ((($env{'user.name'} eq 'public') && ($env{'user.domain'} eq 'public'))
5964: || ($env{'user.name'} eq '') && ($env{'user.domain'} eq '')) {
5965: $public = 1;
5966: }
1.460 albertel 5967: if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
1.1154 raeburn 5968: my $httphost = $args->{'use_absolute'};
1.1274 raeburn 5969: my $hostname = $args->{'hostname'};
1.339 albertel 5970:
1.183 matthew 5971: $function = &get_users_function() if (!$function);
1.339 albertel 5972: my $img = &designparm($function.'.img',$domain);
5973: my $font = &designparm($function.'.font',$domain);
5974: my $pgbg = $bgcolor || &designparm($function.'.pgbg',$domain);
5975:
1.803 bisitz 5976: my %design = ( 'style' => 'margin-top: 0',
1.535 albertel 5977: 'bgcolor' => $pgbg,
1.339 albertel 5978: 'text' => $font,
5979: 'alink' => &designparm($function.'.alink',$domain),
5980: 'vlink' => &designparm($function.'.vlink',$domain),
5981: 'link' => &designparm($function.'.link',$domain),);
1.438 albertel 5982: @design{keys(%$addentries)} = @$addentries{keys(%$addentries)};
1.339 albertel 5983:
1.63 www 5984: # role and realm
1.1178 raeburn 5985: my ($role,$realm) = split(m{\./},$env{'request.role'},2);
5986: if ($realm) {
5987: $realm = '/'.$realm;
5988: }
1.378 raeburn 5989: if ($role eq 'ca') {
1.479 albertel 5990: my ($rdom,$rname) = ($realm =~ m{^/($match_domain)/($match_username)$});
1.500 albertel 5991: $realm = &plainname($rname,$rdom);
1.378 raeburn 5992: }
1.55 www 5993: # realm
1.258 albertel 5994: if ($env{'request.course.id'}) {
1.378 raeburn 5995: if ($env{'request.role'} !~ /^cr/) {
5996: $role = &Apache::lonnet::plaintext($role,&course_type());
1.1257 raeburn 5997: } elsif ($role =~ m{^cr/($match_domain)/\1-domainconfig/(\w+)$}) {
1.1269 raeburn 5998: if ($env{'request.role.desc'}) {
5999: $role = $env{'request.role.desc'};
6000: } else {
6001: $role = &mt('Helpdesk[_1]',' '.$2);
6002: }
1.1257 raeburn 6003: } else {
6004: $role = (split(/\//,$role,4))[-1];
1.378 raeburn 6005: }
1.898 raeburn 6006: if ($env{'request.course.sec'}) {
6007: $role .= (' 'x2).'- '.&mt('section:').' '.$env{'request.course.sec'};
6008: }
1.359 albertel 6009: $realm = $env{'course.'.$env{'request.course.id'}.'.description'};
1.378 raeburn 6010: } else {
6011: $role = &Apache::lonnet::plaintext($role);
1.54 www 6012: }
1.433 albertel 6013:
1.359 albertel 6014: if (!$realm) { $realm=' '; }
1.330 albertel 6015:
1.438 albertel 6016: my $extra_body_attr = &make_attr_string($forcereg,\%design);
1.329 albertel 6017:
1.101 www 6018: # construct main body tag
1.359 albertel 6019: my $bodytag = "<body $extra_body_attr>".
1.1235 raeburn 6020: &Apache::lontexconvert::init_math_support();
1.252 albertel 6021:
1.1131 raeburn 6022: &get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['inhibitmenu']);
6023:
1.1130 raeburn 6024: if (($bodyonly) || ($no_nav_bar) || ($env{'form.inhibitmenu'} eq 'yes')) {
1.60 matthew 6025: return $bodytag;
1.1130 raeburn 6026: }
1.359 albertel 6027:
1.954 raeburn 6028: if ($public) {
1.433 albertel 6029: undef($role);
6030: }
1.1318 raeburn 6031:
6032: if (($env{'request.course.id'}) && ($env{'request.lti.login'})) {
6033: if (ref($ltimenu) eq 'HASH') {
6034: unless ($ltimenu->{'role'}) {
6035: undef($role);
6036: }
6037: unless ($ltimenu->{'coursetitle'}) {
6038: $realm=' ';
6039: }
6040: }
6041: }
6042:
1.762 bisitz 6043: my $titleinfo = '<h1>'.$title.'</h1>';
1.359 albertel 6044: #
6045: # Extra info if you are the DC
6046: my $dc_info = '';
6047: if ($env{'user.adv'} && exists($env{'user.role.dc./'.
6048: $env{'course.'.$env{'request.course.id'}.
6049: '.domain'}.'/'})) {
6050: my $cid = $env{'request.course.id'};
1.917 raeburn 6051: $dc_info = $cid.' '.$env{'course.'.$cid.'.internal.coursecode'};
1.380 www 6052: $dc_info =~ s/\s+$//;
1.359 albertel 6053: }
6054:
1.1237 raeburn 6055: my $crstype;
6056: if ($env{'request.course.id'}) {
6057: $crstype = $env{'course.'.$env{'request.course.id'}.'.type'};
6058: } elsif ($args->{'crstype'}) {
6059: $crstype = $args->{'crstype'};
6060: }
6061: if (($crstype eq 'Placement') && (!$env{'request.role.adv'})) {
6062: undef($role);
6063: } else {
1.1242 raeburn 6064: $role = '<span class="LC_nobreak">('.$role.')</span>' if ($role && !$env{'browser.mobile'});
1.1237 raeburn 6065: }
1.853 droeschl 6066:
1.903 droeschl 6067: if ($env{'request.state'} eq 'construct') { $forcereg=1; }
6068:
6069: # if ($env{'request.state'} eq 'construct') {
6070: # $titleinfo = &CSTR_pageheader(); #FIXME: Will be removed once all scripts have their own calls
6071: # }
6072:
1.1130 raeburn 6073: $bodytag .= Apache::lonhtmlcommon::scripttag(
1.1154 raeburn 6074: Apache::lonmenu::utilityfunctions($httphost), 'start');
1.359 albertel 6075:
1.1318 raeburn 6076: unless ($args->{'no_primary_menu'}) {
6077: my ($left,$right) = Apache::lonmenu::primary_menu($crstype,$ltimenu);
1.359 albertel 6078:
1.1318 raeburn 6079: if ($env{'request.noversionuri'} =~ m{^/res/adm/pages/}) {
6080: if ($dc_info) {
6081: $dc_info = qq|<span class="LC_cusr_subheading">$dc_info</span>|;
6082: }
6083: $bodytag .= qq|<div id="LC_nav_bar">$left $role<br />
6084: <em>$realm</em> $dc_info</div>|;
6085: return $bodytag;
6086: }
1.894 droeschl 6087:
1.1318 raeburn 6088: unless ($env{'request.symb'} =~ m/\.page___\d+___/) {
6089: $bodytag .= qq|<div id="LC_nav_bar">$left $role</div>|;
6090: }
1.916 droeschl 6091:
1.1318 raeburn 6092: $bodytag .= $right;
1.852 droeschl 6093:
1.1318 raeburn 6094: if ($dc_info) {
6095: $dc_info = &dc_courseid_toggle($dc_info);
6096: }
6097: $bodytag .= qq|<div id="LC_realm">$realm $dc_info</div>|;
1.917 raeburn 6098: }
1.916 droeschl 6099:
1.1169 raeburn 6100: #if directed to not display the secondary menu, don't.
1.1168 raeburn 6101: if ($args->{'no_secondary_menu'}) {
6102: return $bodytag;
6103: }
1.1169 raeburn 6104: #don't show menus for public users
1.954 raeburn 6105: if (!$public){
1.1318 raeburn 6106: unless ($args->{'no_inline_menu'}) {
6107: $bodytag .= Apache::lonmenu::secondary_menu($httphost,$ltiscope,$ltimenu,
6108: $args->{'no_primary_menu'});
6109: }
1.903 droeschl 6110: $bodytag .= Apache::lonmenu::serverform();
1.920 raeburn 6111: $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end');
6112: if ($env{'request.state'} eq 'construct') {
1.962 droeschl 6113: $bodytag .= &Apache::lonmenu::innerregister($forcereg,
1.1316 raeburn 6114: $args->{'bread_crumbs'},'','',$hostname,$ltiscope,$ltiuri);
1.1096 raeburn 6115: } elsif ($forcereg) {
6116: $bodytag .= &Apache::lonmenu::innerregister($forcereg,undef,
1.1258 raeburn 6117: $args->{'group'},
1.1274 raeburn 6118: $args->{'hide_buttons'},
1.1316 raeburn 6119: $hostname,$ltiscope,$ltiuri);
1.1096 raeburn 6120: } else {
6121: $bodytag .=
6122: &Apache::lonmenu::prepare_functions($env{'request.noversionuri'},
6123: $forcereg,$args->{'group'},
6124: $args->{'bread_crumbs'},
1.1274 raeburn 6125: $advtoolsref,'',$hostname);
1.920 raeburn 6126: }
1.903 droeschl 6127: }else{
6128: # this is to seperate menu from content when there's no secondary
6129: # menu. Especially needed for public accessible ressources.
6130: $bodytag .= '<hr style="clear:both" />';
6131: $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end');
1.235 raeburn 6132: }
1.903 droeschl 6133:
1.235 raeburn 6134: return $bodytag;
1.182 matthew 6135: }
6136:
1.917 raeburn 6137: sub dc_courseid_toggle {
6138: my ($dc_info) = @_;
1.980 raeburn 6139: return ' <span id="dccidtext" class="LC_cusr_subheading LC_nobreak">'.
1.1069 raeburn 6140: '<a href="javascript:showCourseID();" class="LC_menubuttons_link">'.
1.917 raeburn 6141: &mt('(More ...)').'</a></span>'.
6142: '<div id="dccid" class="LC_dccid">'.$dc_info.'</div>';
6143: }
6144:
1.330 albertel 6145: sub make_attr_string {
6146: my ($register,$attr_ref) = @_;
6147:
6148: if ($attr_ref && !ref($attr_ref)) {
6149: die("addentries Must be a hash ref ".
6150: join(':',caller(1))." ".
6151: join(':',caller(0))." ");
6152: }
6153:
6154: if ($register) {
1.339 albertel 6155: my ($on_load,$on_unload);
6156: foreach my $key (keys(%{$attr_ref})) {
6157: if (lc($key) eq 'onload') {
6158: $on_load.=$attr_ref->{$key}.';';
6159: delete($attr_ref->{$key});
6160:
6161: } elsif (lc($key) eq 'onunload') {
6162: $on_unload.=$attr_ref->{$key}.';';
6163: delete($attr_ref->{$key});
6164: }
6165: }
1.953 droeschl 6166: $attr_ref->{'onload'} = $on_load;
6167: $attr_ref->{'onunload'}= $on_unload;
1.330 albertel 6168: }
1.339 albertel 6169:
1.330 albertel 6170: my $attr_string;
1.1159 raeburn 6171: foreach my $attr (sort(keys(%$attr_ref))) {
1.330 albertel 6172: $attr_string .= " $attr=\"".$attr_ref->{$attr}.'" ';
6173: }
6174: return $attr_string;
6175: }
6176:
6177:
1.182 matthew 6178: ###############################################
1.251 albertel 6179: ###############################################
6180:
6181: =pod
6182:
6183: =item * &endbodytag()
6184:
6185: Returns a uniform footer for LON-CAPA web pages.
6186:
1.635 raeburn 6187: Inputs: 1 - optional reference to an args hash
6188: If in the hash, key for noredirectlink has a value which evaluates to true,
6189: a 'Continue' link is not displayed if the page contains an
6190: internal redirect in the <head></head> section,
6191: i.e., $env{'internal.head.redirect'} exists
1.251 albertel 6192:
6193: =cut
6194:
6195: sub endbodytag {
1.635 raeburn 6196: my ($args) = @_;
1.1080 raeburn 6197: my $endbodytag;
6198: unless ((ref($args) eq 'HASH') && ($args->{'notbody'})) {
6199: $endbodytag='</body>';
6200: }
1.315 albertel 6201: if ( exists( $env{'internal.head.redirect'} ) ) {
1.635 raeburn 6202: if (!(ref($args) eq 'HASH' && $args->{'noredirectlink'})) {
6203: $endbodytag=
6204: "<br /><a href=\"$env{'internal.head.redirect'}\">".
6205: &mt('Continue').'</a>'.
6206: $endbodytag;
6207: }
1.315 albertel 6208: }
1.251 albertel 6209: return $endbodytag;
6210: }
6211:
1.352 albertel 6212: =pod
6213:
6214: =item * &standard_css()
6215:
6216: Returns a style sheet
6217:
6218: Inputs: (all optional)
6219: domain -> force to color decorate a page for a specific
6220: domain
6221: function -> force usage of a specific rolish color scheme
6222: bgcolor -> override the default page bgcolor
6223:
6224: =cut
6225:
1.343 albertel 6226: sub standard_css {
1.345 albertel 6227: my ($function,$domain,$bgcolor) = @_;
1.352 albertel 6228: $function = &get_users_function() if (!$function);
6229: my $img = &designparm($function.'.img', $domain);
6230: my $tabbg = &designparm($function.'.tabbg', $domain);
6231: my $font = &designparm($function.'.font', $domain);
1.801 tempelho 6232: my $fontmenu = &designparm($function.'.fontmenu', $domain);
1.791 tempelho 6233: #second colour for later usage
1.345 albertel 6234: my $sidebg = &designparm($function.'.sidebg',$domain);
1.382 albertel 6235: my $pgbg_or_bgcolor =
6236: $bgcolor ||
1.352 albertel 6237: &designparm($function.'.pgbg', $domain);
1.382 albertel 6238: my $pgbg = &designparm($function.'.pgbg', $domain);
1.352 albertel 6239: my $alink = &designparm($function.'.alink', $domain);
6240: my $vlink = &designparm($function.'.vlink', $domain);
6241: my $link = &designparm($function.'.link', $domain);
6242:
1.602 albertel 6243: my $sans = 'Verdana,Arial,Helvetica,sans-serif';
1.395 albertel 6244: my $mono = 'monospace';
1.850 bisitz 6245: my $data_table_head = $sidebg;
6246: my $data_table_light = '#FAFAFA';
1.1060 bisitz 6247: my $data_table_dark = '#E0E0E0';
1.470 banghart 6248: my $data_table_darker = '#CCCCCC';
1.349 albertel 6249: my $data_table_highlight = '#FFFF00';
1.352 albertel 6250: my $mail_new = '#FFBB77';
6251: my $mail_new_hover = '#DD9955';
6252: my $mail_read = '#BBBB77';
6253: my $mail_read_hover = '#999944';
6254: my $mail_replied = '#AAAA88';
6255: my $mail_replied_hover = '#888855';
6256: my $mail_other = '#99BBBB';
6257: my $mail_other_hover = '#669999';
1.391 albertel 6258: my $table_header = '#DDDDDD';
1.489 raeburn 6259: my $feedback_link_bg = '#BBBBBB';
1.911 bisitz 6260: my $lg_border_color = '#C8C8C8';
1.952 onken 6261: my $button_hover = '#BF2317';
1.392 albertel 6262:
1.608 albertel 6263: my $border = ($env{'browser.type'} eq 'explorer' ||
1.911 bisitz 6264: $env{'browser.type'} eq 'safari' ) ? '0 2px 0 2px'
6265: : '0 3px 0 4px';
1.448 albertel 6266:
1.523 albertel 6267:
1.343 albertel 6268: return <<END;
1.947 droeschl 6269:
6270: /* needed for iframe to allow 100% height in FF */
6271: body, html {
6272: margin: 0;
6273: padding: 0 0.5%;
6274: height: 99%; /* to avoid scrollbars */
6275: }
6276:
1.795 www 6277: body {
1.911 bisitz 6278: font-family: $sans;
6279: line-height:130%;
6280: font-size:0.83em;
6281: color:$font;
1.795 www 6282: }
6283:
1.959 onken 6284: a:focus,
6285: a:focus img {
1.795 www 6286: color: red;
6287: }
1.698 harmsja 6288:
1.911 bisitz 6289: form, .inline {
6290: display: inline;
1.795 www 6291: }
1.721 harmsja 6292:
1.795 www 6293: .LC_right {
1.911 bisitz 6294: text-align:right;
1.795 www 6295: }
6296:
6297: .LC_middle {
1.911 bisitz 6298: vertical-align:middle;
1.795 www 6299: }
1.721 harmsja 6300:
1.1130 raeburn 6301: .LC_floatleft {
6302: float: left;
6303: }
6304:
6305: .LC_floatright {
6306: float: right;
6307: }
6308:
1.911 bisitz 6309: .LC_400Box {
6310: width:400px;
6311: }
1.721 harmsja 6312:
1.947 droeschl 6313: .LC_iframecontainer {
6314: width: 98%;
6315: margin: 0;
6316: position: fixed;
6317: top: 8.5em;
6318: bottom: 0;
6319: }
6320:
6321: .LC_iframecontainer iframe{
6322: border: none;
6323: width: 100%;
6324: height: 100%;
6325: }
6326:
1.778 bisitz 6327: .LC_filename {
6328: font-family: $mono;
6329: white-space:pre;
1.921 bisitz 6330: font-size: 120%;
1.778 bisitz 6331: }
6332:
6333: .LC_fileicon {
6334: border: none;
6335: height: 1.3em;
6336: vertical-align: text-bottom;
6337: margin-right: 0.3em;
6338: text-decoration:none;
6339: }
6340:
1.1008 www 6341: .LC_setting {
6342: text-decoration:underline;
6343: }
6344:
1.350 albertel 6345: .LC_error {
6346: color: red;
6347: }
1.795 www 6348:
1.1097 bisitz 6349: .LC_warning {
6350: color: darkorange;
6351: }
6352:
1.457 albertel 6353: .LC_diff_removed {
1.733 bisitz 6354: color: red;
1.394 albertel 6355: }
1.532 albertel 6356:
6357: .LC_info,
1.457 albertel 6358: .LC_success,
6359: .LC_diff_added {
1.350 albertel 6360: color: green;
6361: }
1.795 www 6362:
1.802 bisitz 6363: div.LC_confirm_box {
6364: background-color: #FAFAFA;
6365: border: 1px solid $lg_border_color;
6366: margin-right: 0;
6367: padding: 5px;
6368: }
6369:
6370: div.LC_confirm_box .LC_error img,
6371: div.LC_confirm_box .LC_success img {
6372: vertical-align: middle;
6373: }
6374:
1.1242 raeburn 6375: .LC_maxwidth {
6376: max-width: 100%;
6377: height: auto;
6378: }
6379:
1.1243 raeburn 6380: .LC_textsize_mobile {
6381: \@media only screen and (max-device-width: 480px) {
6382: -webkit-text-size-adjust:100%; -moz-text-size-adjust:100%; -ms-text-size-adjust:100%;
6383: }
6384: }
6385:
1.440 albertel 6386: .LC_icon {
1.771 droeschl 6387: border: none;
1.790 droeschl 6388: vertical-align: middle;
1.771 droeschl 6389: }
6390:
1.543 albertel 6391: .LC_docs_spacer {
6392: width: 25px;
6393: height: 1px;
1.771 droeschl 6394: border: none;
1.543 albertel 6395: }
1.346 albertel 6396:
1.532 albertel 6397: .LC_internal_info {
1.735 bisitz 6398: color: #999999;
1.532 albertel 6399: }
6400:
1.794 www 6401: .LC_discussion {
1.1050 www 6402: background: $data_table_dark;
1.911 bisitz 6403: border: 1px solid black;
6404: margin: 2px;
1.794 www 6405: }
6406:
6407: .LC_disc_action_left {
1.1050 www 6408: background: $sidebg;
1.911 bisitz 6409: text-align: left;
1.1050 www 6410: padding: 4px;
6411: margin: 2px;
1.794 www 6412: }
6413:
6414: .LC_disc_action_right {
1.1050 www 6415: background: $sidebg;
1.911 bisitz 6416: text-align: right;
1.1050 www 6417: padding: 4px;
6418: margin: 2px;
1.794 www 6419: }
6420:
6421: .LC_disc_new_item {
1.911 bisitz 6422: background: white;
6423: border: 2px solid red;
1.1050 www 6424: margin: 4px;
6425: padding: 4px;
1.794 www 6426: }
6427:
6428: .LC_disc_old_item {
1.911 bisitz 6429: background: white;
1.1050 www 6430: margin: 4px;
6431: padding: 4px;
1.794 www 6432: }
6433:
1.458 albertel 6434: table.LC_pastsubmission {
6435: border: 1px solid black;
6436: margin: 2px;
6437: }
6438:
1.924 bisitz 6439: table#LC_menubuttons {
1.345 albertel 6440: width: 100%;
6441: background: $pgbg;
1.392 albertel 6442: border: 2px;
1.402 albertel 6443: border-collapse: separate;
1.803 bisitz 6444: padding: 0;
1.345 albertel 6445: }
1.392 albertel 6446:
1.801 tempelho 6447: table#LC_title_bar a {
6448: color: $fontmenu;
6449: }
1.836 bisitz 6450:
1.807 droeschl 6451: table#LC_title_bar {
1.819 tempelho 6452: clear: both;
1.836 bisitz 6453: display: none;
1.807 droeschl 6454: }
6455:
1.795 www 6456: table#LC_title_bar,
1.933 droeschl 6457: table.LC_breadcrumbs, /* obsolete? */
1.393 albertel 6458: table#LC_title_bar.LC_with_remote {
1.359 albertel 6459: width: 100%;
1.392 albertel 6460: border-color: $pgbg;
6461: border-style: solid;
6462: border-width: $border;
1.379 albertel 6463: background: $pgbg;
1.801 tempelho 6464: color: $fontmenu;
1.392 albertel 6465: border-collapse: collapse;
1.803 bisitz 6466: padding: 0;
1.819 tempelho 6467: margin: 0;
1.359 albertel 6468: }
1.795 www 6469:
1.933 droeschl 6470: ul.LC_breadcrumb_tools_outerlist {
1.913 droeschl 6471: margin: 0;
6472: padding: 0;
1.933 droeschl 6473: position: relative;
6474: list-style: none;
1.913 droeschl 6475: }
1.933 droeschl 6476: ul.LC_breadcrumb_tools_outerlist li {
1.913 droeschl 6477: display: inline;
6478: }
1.933 droeschl 6479:
6480: .LC_breadcrumb_tools_navigation {
1.913 droeschl 6481: padding: 0;
1.933 droeschl 6482: margin: 0;
6483: float: left;
1.913 droeschl 6484: }
1.933 droeschl 6485: .LC_breadcrumb_tools_tools {
6486: padding: 0;
6487: margin: 0;
1.913 droeschl 6488: float: right;
6489: }
6490:
1.1240 raeburn 6491: .LC_placement_prog {
6492: padding-right: 20px;
6493: font-weight: bold;
6494: font-size: 90%;
6495: }
6496:
1.359 albertel 6497: table#LC_title_bar td {
6498: background: $tabbg;
6499: }
1.795 www 6500:
1.911 bisitz 6501: table#LC_menubuttons img {
1.803 bisitz 6502: border: none;
1.346 albertel 6503: }
1.795 www 6504:
1.842 droeschl 6505: .LC_breadcrumbs_component {
1.911 bisitz 6506: float: right;
6507: margin: 0 1em;
1.357 albertel 6508: }
1.842 droeschl 6509: .LC_breadcrumbs_component img {
1.911 bisitz 6510: vertical-align: middle;
1.777 tempelho 6511: }
1.795 www 6512:
1.1243 raeburn 6513: .LC_breadcrumbs_hoverable {
6514: background: $sidebg;
6515: }
6516:
1.383 albertel 6517: td.LC_table_cell_checkbox {
6518: text-align: center;
6519: }
1.795 www 6520:
6521: .LC_fontsize_small {
1.911 bisitz 6522: font-size: 70%;
1.705 tempelho 6523: }
6524:
1.844 bisitz 6525: #LC_breadcrumbs {
1.911 bisitz 6526: clear:both;
6527: background: $sidebg;
6528: border-bottom: 1px solid $lg_border_color;
6529: line-height: 2.5em;
1.933 droeschl 6530: overflow: hidden;
1.911 bisitz 6531: margin: 0;
6532: padding: 0;
1.995 raeburn 6533: text-align: left;
1.819 tempelho 6534: }
1.862 bisitz 6535:
1.1098 bisitz 6536: .LC_head_subbox, .LC_actionbox {
1.911 bisitz 6537: clear:both;
6538: background: #F8F8F8; /* $sidebg; */
1.915 droeschl 6539: border: 1px solid $sidebg;
1.1098 bisitz 6540: margin: 0 0 10px 0;
1.966 bisitz 6541: padding: 3px;
1.995 raeburn 6542: text-align: left;
1.822 bisitz 6543: }
6544:
1.795 www 6545: .LC_fontsize_medium {
1.911 bisitz 6546: font-size: 85%;
1.705 tempelho 6547: }
6548:
1.795 www 6549: .LC_fontsize_large {
1.911 bisitz 6550: font-size: 120%;
1.705 tempelho 6551: }
6552:
1.346 albertel 6553: .LC_menubuttons_inline_text {
6554: color: $font;
1.698 harmsja 6555: font-size: 90%;
1.701 harmsja 6556: padding-left:3px;
1.346 albertel 6557: }
6558:
1.934 droeschl 6559: .LC_menubuttons_inline_text img{
6560: vertical-align: middle;
6561: }
6562:
1.1051 www 6563: li.LC_menubuttons_inline_text img {
1.951 onken 6564: cursor:pointer;
1.1002 droeschl 6565: text-decoration: none;
1.951 onken 6566: }
6567:
1.526 www 6568: .LC_menubuttons_link {
6569: text-decoration: none;
6570: }
1.795 www 6571:
1.522 albertel 6572: .LC_menubuttons_category {
1.521 www 6573: color: $font;
1.526 www 6574: background: $pgbg;
1.521 www 6575: font-size: larger;
6576: font-weight: bold;
6577: }
6578:
1.346 albertel 6579: td.LC_menubuttons_text {
1.911 bisitz 6580: color: $font;
1.346 albertel 6581: }
1.706 harmsja 6582:
1.346 albertel 6583: .LC_current_location {
6584: background: $tabbg;
6585: }
1.795 www 6586:
1.1286 raeburn 6587: td.LC_zero_height {
6588: line-height: 0;
6589: cellpadding: 0;
6590: }
6591:
1.938 bisitz 6592: table.LC_data_table {
1.347 albertel 6593: border: 1px solid #000000;
1.402 albertel 6594: border-collapse: separate;
1.426 albertel 6595: border-spacing: 1px;
1.610 albertel 6596: background: $pgbg;
1.347 albertel 6597: }
1.795 www 6598:
1.422 albertel 6599: .LC_data_table_dense {
6600: font-size: small;
6601: }
1.795 www 6602:
1.507 raeburn 6603: table.LC_nested_outer {
6604: border: 1px solid #000000;
1.589 raeburn 6605: border-collapse: collapse;
1.803 bisitz 6606: border-spacing: 0;
1.507 raeburn 6607: width: 100%;
6608: }
1.795 www 6609:
1.879 raeburn 6610: table.LC_innerpickbox,
1.507 raeburn 6611: table.LC_nested {
1.803 bisitz 6612: border: none;
1.589 raeburn 6613: border-collapse: collapse;
1.803 bisitz 6614: border-spacing: 0;
1.507 raeburn 6615: width: 100%;
6616: }
1.795 www 6617:
1.911 bisitz 6618: table.LC_data_table tr th,
6619: table.LC_calendar tr th,
1.879 raeburn 6620: table.LC_prior_tries tr th,
6621: table.LC_innerpickbox tr th {
1.349 albertel 6622: font-weight: bold;
6623: background-color: $data_table_head;
1.801 tempelho 6624: color:$fontmenu;
1.701 harmsja 6625: font-size:90%;
1.347 albertel 6626: }
1.795 www 6627:
1.879 raeburn 6628: table.LC_innerpickbox tr th,
6629: table.LC_innerpickbox tr td {
6630: vertical-align: top;
6631: }
6632:
1.711 raeburn 6633: table.LC_data_table tr.LC_info_row > td {
1.735 bisitz 6634: background-color: #CCCCCC;
1.711 raeburn 6635: font-weight: bold;
6636: text-align: left;
6637: }
1.795 www 6638:
1.912 bisitz 6639: table.LC_data_table tr.LC_odd_row > td {
6640: background-color: $data_table_light;
6641: padding: 2px;
6642: vertical-align: top;
6643: }
6644:
1.809 bisitz 6645: table.LC_pick_box tr > td.LC_odd_row {
1.349 albertel 6646: background-color: $data_table_light;
1.912 bisitz 6647: vertical-align: top;
6648: }
6649:
6650: table.LC_data_table tr.LC_even_row > td {
6651: background-color: $data_table_dark;
1.425 albertel 6652: padding: 2px;
1.900 bisitz 6653: vertical-align: top;
1.347 albertel 6654: }
1.795 www 6655:
1.809 bisitz 6656: table.LC_pick_box tr > td.LC_even_row {
1.349 albertel 6657: background-color: $data_table_dark;
1.900 bisitz 6658: vertical-align: top;
1.347 albertel 6659: }
1.795 www 6660:
1.425 albertel 6661: table.LC_data_table tr.LC_data_table_highlight td {
6662: background-color: $data_table_darker;
6663: }
1.795 www 6664:
1.639 raeburn 6665: table.LC_data_table tr td.LC_leftcol_header {
6666: background-color: $data_table_head;
6667: font-weight: bold;
6668: }
1.795 www 6669:
1.451 albertel 6670: table.LC_data_table tr.LC_empty_row td,
1.507 raeburn 6671: table.LC_nested tr.LC_empty_row td {
1.421 albertel 6672: font-weight: bold;
6673: font-style: italic;
6674: text-align: center;
6675: padding: 8px;
1.347 albertel 6676: }
1.795 www 6677:
1.1114 raeburn 6678: table.LC_data_table tr.LC_empty_row td,
6679: table.LC_data_table tr.LC_footer_row td {
1.940 bisitz 6680: background-color: $sidebg;
6681: }
6682:
6683: table.LC_nested tr.LC_empty_row td {
6684: background-color: #FFFFFF;
6685: }
6686:
1.890 droeschl 6687: table.LC_caption {
6688: }
6689:
1.507 raeburn 6690: table.LC_nested tr.LC_empty_row td {
1.465 albertel 6691: padding: 4ex
6692: }
1.795 www 6693:
1.507 raeburn 6694: table.LC_nested_outer tr th {
6695: font-weight: bold;
1.801 tempelho 6696: color:$fontmenu;
1.507 raeburn 6697: background-color: $data_table_head;
1.701 harmsja 6698: font-size: small;
1.507 raeburn 6699: border-bottom: 1px solid #000000;
6700: }
1.795 www 6701:
1.507 raeburn 6702: table.LC_nested_outer tr td.LC_subheader {
6703: background-color: $data_table_head;
6704: font-weight: bold;
6705: font-size: small;
6706: border-bottom: 1px solid #000000;
6707: text-align: right;
1.451 albertel 6708: }
1.795 www 6709:
1.507 raeburn 6710: table.LC_nested tr.LC_info_row td {
1.735 bisitz 6711: background-color: #CCCCCC;
1.451 albertel 6712: font-weight: bold;
6713: font-size: small;
1.507 raeburn 6714: text-align: center;
6715: }
1.795 www 6716:
1.589 raeburn 6717: table.LC_nested tr.LC_info_row td.LC_left_item,
6718: table.LC_nested_outer tr th.LC_left_item {
1.507 raeburn 6719: text-align: left;
1.451 albertel 6720: }
1.795 www 6721:
1.507 raeburn 6722: table.LC_nested td {
1.735 bisitz 6723: background-color: #FFFFFF;
1.451 albertel 6724: font-size: small;
1.507 raeburn 6725: }
1.795 www 6726:
1.507 raeburn 6727: table.LC_nested_outer tr th.LC_right_item,
6728: table.LC_nested tr.LC_info_row td.LC_right_item,
6729: table.LC_nested tr.LC_odd_row td.LC_right_item,
6730: table.LC_nested tr td.LC_right_item {
1.451 albertel 6731: text-align: right;
6732: }
6733:
1.507 raeburn 6734: table.LC_nested tr.LC_odd_row td {
1.735 bisitz 6735: background-color: #EEEEEE;
1.451 albertel 6736: }
6737:
1.473 raeburn 6738: table.LC_createuser {
6739: }
6740:
6741: table.LC_createuser tr.LC_section_row td {
1.701 harmsja 6742: font-size: small;
1.473 raeburn 6743: }
6744:
6745: table.LC_createuser tr.LC_info_row td {
1.735 bisitz 6746: background-color: #CCCCCC;
1.473 raeburn 6747: font-weight: bold;
6748: text-align: center;
6749: }
6750:
1.349 albertel 6751: table.LC_calendar {
6752: border: 1px solid #000000;
6753: border-collapse: collapse;
1.917 raeburn 6754: width: 98%;
1.349 albertel 6755: }
1.795 www 6756:
1.349 albertel 6757: table.LC_calendar_pickdate {
6758: font-size: xx-small;
6759: }
1.795 www 6760:
1.349 albertel 6761: table.LC_calendar tr td {
6762: border: 1px solid #000000;
6763: vertical-align: top;
1.917 raeburn 6764: width: 14%;
1.349 albertel 6765: }
1.795 www 6766:
1.349 albertel 6767: table.LC_calendar tr td.LC_calendar_day_empty {
6768: background-color: $data_table_dark;
6769: }
1.795 www 6770:
1.779 bisitz 6771: table.LC_calendar tr td.LC_calendar_day_current {
6772: background-color: $data_table_highlight;
1.777 tempelho 6773: }
1.795 www 6774:
1.938 bisitz 6775: table.LC_data_table tr td.LC_mail_new {
1.349 albertel 6776: background-color: $mail_new;
6777: }
1.795 www 6778:
1.938 bisitz 6779: table.LC_data_table tr.LC_mail_new:hover {
1.349 albertel 6780: background-color: $mail_new_hover;
6781: }
1.795 www 6782:
1.938 bisitz 6783: table.LC_data_table tr td.LC_mail_read {
1.349 albertel 6784: background-color: $mail_read;
6785: }
1.795 www 6786:
1.938 bisitz 6787: /*
6788: table.LC_data_table tr.LC_mail_read:hover {
1.349 albertel 6789: background-color: $mail_read_hover;
6790: }
1.938 bisitz 6791: */
1.795 www 6792:
1.938 bisitz 6793: table.LC_data_table tr td.LC_mail_replied {
1.349 albertel 6794: background-color: $mail_replied;
6795: }
1.795 www 6796:
1.938 bisitz 6797: /*
6798: table.LC_data_table tr.LC_mail_replied:hover {
1.349 albertel 6799: background-color: $mail_replied_hover;
6800: }
1.938 bisitz 6801: */
1.795 www 6802:
1.938 bisitz 6803: table.LC_data_table tr td.LC_mail_other {
1.349 albertel 6804: background-color: $mail_other;
6805: }
1.795 www 6806:
1.938 bisitz 6807: /*
6808: table.LC_data_table tr.LC_mail_other:hover {
1.349 albertel 6809: background-color: $mail_other_hover;
6810: }
1.938 bisitz 6811: */
1.494 raeburn 6812:
1.777 tempelho 6813: table.LC_data_table tr > td.LC_browser_file,
6814: table.LC_data_table tr > td.LC_browser_file_published {
1.899 bisitz 6815: background: #AAEE77;
1.389 albertel 6816: }
1.795 www 6817:
1.777 tempelho 6818: table.LC_data_table tr > td.LC_browser_file_locked,
6819: table.LC_data_table tr > td.LC_browser_file_unpublished {
1.389 albertel 6820: background: #FFAA99;
1.387 albertel 6821: }
1.795 www 6822:
1.777 tempelho 6823: table.LC_data_table tr > td.LC_browser_file_obsolete {
1.899 bisitz 6824: background: #888888;
1.779 bisitz 6825: }
1.795 www 6826:
1.777 tempelho 6827: table.LC_data_table tr > td.LC_browser_file_modified,
1.779 bisitz 6828: table.LC_data_table tr > td.LC_browser_file_metamodified {
1.899 bisitz 6829: background: #F8F866;
1.777 tempelho 6830: }
1.795 www 6831:
1.696 bisitz 6832: table.LC_data_table tr.LC_browser_folder > td {
1.899 bisitz 6833: background: #E0E8FF;
1.387 albertel 6834: }
1.696 bisitz 6835:
1.707 bisitz 6836: table.LC_data_table tr > td.LC_roles_is {
1.911 bisitz 6837: /* background: #77FF77; */
1.707 bisitz 6838: }
1.795 www 6839:
1.707 bisitz 6840: table.LC_data_table tr > td.LC_roles_future {
1.939 bisitz 6841: border-right: 8px solid #FFFF77;
1.707 bisitz 6842: }
1.795 www 6843:
1.707 bisitz 6844: table.LC_data_table tr > td.LC_roles_will {
1.939 bisitz 6845: border-right: 8px solid #FFAA77;
1.707 bisitz 6846: }
1.795 www 6847:
1.707 bisitz 6848: table.LC_data_table tr > td.LC_roles_expired {
1.939 bisitz 6849: border-right: 8px solid #FF7777;
1.707 bisitz 6850: }
1.795 www 6851:
1.707 bisitz 6852: table.LC_data_table tr > td.LC_roles_will_not {
1.939 bisitz 6853: border-right: 8px solid #AAFF77;
1.707 bisitz 6854: }
1.795 www 6855:
1.707 bisitz 6856: table.LC_data_table tr > td.LC_roles_selected {
1.939 bisitz 6857: border-right: 8px solid #11CC55;
1.707 bisitz 6858: }
6859:
1.388 albertel 6860: span.LC_current_location {
1.701 harmsja 6861: font-size:larger;
1.388 albertel 6862: background: $pgbg;
6863: }
1.387 albertel 6864:
1.1029 www 6865: span.LC_current_nav_location {
6866: font-weight:bold;
6867: background: $sidebg;
6868: }
6869:
1.395 albertel 6870: span.LC_parm_menu_item {
6871: font-size: larger;
6872: }
1.795 www 6873:
1.395 albertel 6874: span.LC_parm_scope_all {
6875: color: red;
6876: }
1.795 www 6877:
1.395 albertel 6878: span.LC_parm_scope_folder {
6879: color: green;
6880: }
1.795 www 6881:
1.395 albertel 6882: span.LC_parm_scope_resource {
6883: color: orange;
6884: }
1.795 www 6885:
1.395 albertel 6886: span.LC_parm_part {
6887: color: blue;
6888: }
1.795 www 6889:
1.911 bisitz 6890: span.LC_parm_folder,
6891: span.LC_parm_symb {
1.395 albertel 6892: font-size: x-small;
6893: font-family: $mono;
6894: color: #AAAAAA;
6895: }
6896:
1.977 bisitz 6897: ul.LC_parm_parmlist li {
6898: display: inline-block;
6899: padding: 0.3em 0.8em;
6900: vertical-align: top;
6901: width: 150px;
6902: border-top:1px solid $lg_border_color;
6903: }
6904:
1.795 www 6905: td.LC_parm_overview_level_menu,
6906: td.LC_parm_overview_map_menu,
6907: td.LC_parm_overview_parm_selectors,
6908: td.LC_parm_overview_restrictions {
1.396 albertel 6909: border: 1px solid black;
6910: border-collapse: collapse;
6911: }
1.795 www 6912:
1.1285 raeburn 6913: span.LC_parm_recursive,
6914: td.LC_parm_recursive {
6915: font-weight: bold;
6916: font-size: smaller;
6917: }
6918:
1.396 albertel 6919: table.LC_parm_overview_restrictions td {
6920: border-width: 1px 4px 1px 4px;
6921: border-style: solid;
6922: border-color: $pgbg;
6923: text-align: center;
6924: }
1.795 www 6925:
1.396 albertel 6926: table.LC_parm_overview_restrictions th {
6927: background: $tabbg;
6928: border-width: 1px 4px 1px 4px;
6929: border-style: solid;
6930: border-color: $pgbg;
6931: }
1.795 www 6932:
1.398 albertel 6933: table#LC_helpmenu {
1.803 bisitz 6934: border: none;
1.398 albertel 6935: height: 55px;
1.803 bisitz 6936: border-spacing: 0;
1.398 albertel 6937: }
6938:
6939: table#LC_helpmenu fieldset legend {
6940: font-size: larger;
6941: }
1.795 www 6942:
1.397 albertel 6943: table#LC_helpmenu_links {
6944: width: 100%;
6945: border: 1px solid black;
6946: background: $pgbg;
1.803 bisitz 6947: padding: 0;
1.397 albertel 6948: border-spacing: 1px;
6949: }
1.795 www 6950:
1.397 albertel 6951: table#LC_helpmenu_links tr td {
6952: padding: 1px;
6953: background: $tabbg;
1.399 albertel 6954: text-align: center;
6955: font-weight: bold;
1.397 albertel 6956: }
1.396 albertel 6957:
1.795 www 6958: table#LC_helpmenu_links a:link,
6959: table#LC_helpmenu_links a:visited,
1.397 albertel 6960: table#LC_helpmenu_links a:active {
6961: text-decoration: none;
6962: color: $font;
6963: }
1.795 www 6964:
1.397 albertel 6965: table#LC_helpmenu_links a:hover {
6966: text-decoration: underline;
6967: color: $vlink;
6968: }
1.396 albertel 6969:
1.417 albertel 6970: .LC_chrt_popup_exists {
6971: border: 1px solid #339933;
6972: margin: -1px;
6973: }
1.795 www 6974:
1.417 albertel 6975: .LC_chrt_popup_up {
6976: border: 1px solid yellow;
6977: margin: -1px;
6978: }
1.795 www 6979:
1.417 albertel 6980: .LC_chrt_popup {
6981: border: 1px solid #8888FF;
6982: background: #CCCCFF;
6983: }
1.795 www 6984:
1.421 albertel 6985: table.LC_pick_box {
6986: border-collapse: separate;
6987: background: white;
6988: border: 1px solid black;
6989: border-spacing: 1px;
6990: }
1.795 www 6991:
1.421 albertel 6992: table.LC_pick_box td.LC_pick_box_title {
1.850 bisitz 6993: background: $sidebg;
1.421 albertel 6994: font-weight: bold;
1.900 bisitz 6995: text-align: left;
1.740 bisitz 6996: vertical-align: top;
1.421 albertel 6997: width: 184px;
6998: padding: 8px;
6999: }
1.795 www 7000:
1.579 raeburn 7001: table.LC_pick_box td.LC_pick_box_value {
7002: text-align: left;
7003: padding: 8px;
7004: }
1.795 www 7005:
1.579 raeburn 7006: table.LC_pick_box td.LC_pick_box_select {
7007: text-align: left;
7008: padding: 8px;
7009: }
1.795 www 7010:
1.424 albertel 7011: table.LC_pick_box td.LC_pick_box_separator {
1.803 bisitz 7012: padding: 0;
1.421 albertel 7013: height: 1px;
7014: background: black;
7015: }
1.795 www 7016:
1.421 albertel 7017: table.LC_pick_box td.LC_pick_box_submit {
7018: text-align: right;
7019: }
1.795 www 7020:
1.579 raeburn 7021: table.LC_pick_box td.LC_evenrow_value {
7022: text-align: left;
7023: padding: 8px;
7024: background-color: $data_table_light;
7025: }
1.795 www 7026:
1.579 raeburn 7027: table.LC_pick_box td.LC_oddrow_value {
7028: text-align: left;
7029: padding: 8px;
7030: background-color: $data_table_light;
7031: }
1.795 www 7032:
1.579 raeburn 7033: span.LC_helpform_receipt_cat {
7034: font-weight: bold;
7035: }
1.795 www 7036:
1.424 albertel 7037: table.LC_group_priv_box {
7038: background: white;
7039: border: 1px solid black;
7040: border-spacing: 1px;
7041: }
1.795 www 7042:
1.424 albertel 7043: table.LC_group_priv_box td.LC_pick_box_title {
7044: background: $tabbg;
7045: font-weight: bold;
7046: text-align: right;
7047: width: 184px;
7048: }
1.795 www 7049:
1.424 albertel 7050: table.LC_group_priv_box td.LC_groups_fixed {
7051: background: $data_table_light;
7052: text-align: center;
7053: }
1.795 www 7054:
1.424 albertel 7055: table.LC_group_priv_box td.LC_groups_optional {
7056: background: $data_table_dark;
7057: text-align: center;
7058: }
1.795 www 7059:
1.424 albertel 7060: table.LC_group_priv_box td.LC_groups_functionality {
7061: background: $data_table_darker;
7062: text-align: center;
7063: font-weight: bold;
7064: }
1.795 www 7065:
1.424 albertel 7066: table.LC_group_priv td {
7067: text-align: left;
1.803 bisitz 7068: padding: 0;
1.424 albertel 7069: }
7070:
7071: .LC_navbuttons {
7072: margin: 2ex 0ex 2ex 0ex;
7073: }
1.795 www 7074:
1.423 albertel 7075: .LC_topic_bar {
7076: font-weight: bold;
7077: background: $tabbg;
1.918 wenzelju 7078: margin: 1em 0em 1em 2em;
1.805 bisitz 7079: padding: 3px;
1.918 wenzelju 7080: font-size: 1.2em;
1.423 albertel 7081: }
1.795 www 7082:
1.423 albertel 7083: .LC_topic_bar span {
1.918 wenzelju 7084: left: 0.5em;
7085: position: absolute;
1.423 albertel 7086: vertical-align: middle;
1.918 wenzelju 7087: font-size: 1.2em;
1.423 albertel 7088: }
1.795 www 7089:
1.423 albertel 7090: table.LC_course_group_status {
7091: margin: 20px;
7092: }
1.795 www 7093:
1.423 albertel 7094: table.LC_status_selector td {
7095: vertical-align: top;
7096: text-align: center;
1.424 albertel 7097: padding: 4px;
7098: }
1.795 www 7099:
1.599 albertel 7100: div.LC_feedback_link {
1.616 albertel 7101: clear: both;
1.829 kalberla 7102: background: $sidebg;
1.779 bisitz 7103: width: 100%;
1.829 kalberla 7104: padding-bottom: 10px;
7105: border: 1px $tabbg solid;
1.833 kalberla 7106: height: 22px;
7107: line-height: 22px;
7108: padding-top: 5px;
7109: }
7110:
7111: div.LC_feedback_link img {
7112: height: 22px;
1.867 kalberla 7113: vertical-align:middle;
1.829 kalberla 7114: }
7115:
1.911 bisitz 7116: div.LC_feedback_link a {
1.829 kalberla 7117: text-decoration: none;
1.489 raeburn 7118: }
1.795 www 7119:
1.867 kalberla 7120: div.LC_comblock {
1.911 bisitz 7121: display:inline;
1.867 kalberla 7122: color:$font;
7123: font-size:90%;
7124: }
7125:
7126: div.LC_feedback_link div.LC_comblock {
7127: padding-left:5px;
7128: }
7129:
7130: div.LC_feedback_link div.LC_comblock a {
7131: color:$font;
7132: }
7133:
1.489 raeburn 7134: span.LC_feedback_link {
1.858 bisitz 7135: /* background: $feedback_link_bg; */
1.599 albertel 7136: font-size: larger;
7137: }
1.795 www 7138:
1.599 albertel 7139: span.LC_message_link {
1.858 bisitz 7140: /* background: $feedback_link_bg; */
1.599 albertel 7141: font-size: larger;
7142: position: absolute;
7143: right: 1em;
1.489 raeburn 7144: }
1.421 albertel 7145:
1.515 albertel 7146: table.LC_prior_tries {
1.524 albertel 7147: border: 1px solid #000000;
7148: border-collapse: separate;
7149: border-spacing: 1px;
1.515 albertel 7150: }
1.523 albertel 7151:
1.515 albertel 7152: table.LC_prior_tries td {
1.524 albertel 7153: padding: 2px;
1.515 albertel 7154: }
1.523 albertel 7155:
7156: .LC_answer_correct {
1.795 www 7157: background: lightgreen;
7158: color: darkgreen;
7159: padding: 6px;
1.523 albertel 7160: }
1.795 www 7161:
1.523 albertel 7162: .LC_answer_charged_try {
1.797 www 7163: background: #FFAAAA;
1.795 www 7164: color: darkred;
7165: padding: 6px;
1.523 albertel 7166: }
1.795 www 7167:
1.779 bisitz 7168: .LC_answer_not_charged_try,
1.523 albertel 7169: .LC_answer_no_grade,
7170: .LC_answer_late {
1.795 www 7171: background: lightyellow;
1.523 albertel 7172: color: black;
1.795 www 7173: padding: 6px;
1.523 albertel 7174: }
1.795 www 7175:
1.523 albertel 7176: .LC_answer_previous {
1.795 www 7177: background: lightblue;
7178: color: darkblue;
7179: padding: 6px;
1.523 albertel 7180: }
1.795 www 7181:
1.779 bisitz 7182: .LC_answer_no_message {
1.777 tempelho 7183: background: #FFFFFF;
7184: color: black;
1.795 www 7185: padding: 6px;
1.779 bisitz 7186: }
1.795 www 7187:
1.779 bisitz 7188: .LC_answer_unknown {
7189: background: orange;
7190: color: black;
1.795 www 7191: padding: 6px;
1.777 tempelho 7192: }
1.795 www 7193:
1.529 albertel 7194: span.LC_prior_numerical,
7195: span.LC_prior_string,
7196: span.LC_prior_custom,
7197: span.LC_prior_reaction,
7198: span.LC_prior_math {
1.925 bisitz 7199: font-family: $mono;
1.523 albertel 7200: white-space: pre;
7201: }
7202:
1.525 albertel 7203: span.LC_prior_string {
1.925 bisitz 7204: font-family: $mono;
1.525 albertel 7205: white-space: pre;
7206: }
7207:
1.523 albertel 7208: table.LC_prior_option {
7209: width: 100%;
7210: border-collapse: collapse;
7211: }
1.795 www 7212:
1.911 bisitz 7213: table.LC_prior_rank,
1.795 www 7214: table.LC_prior_match {
1.528 albertel 7215: border-collapse: collapse;
7216: }
1.795 www 7217:
1.528 albertel 7218: table.LC_prior_option tr td,
7219: table.LC_prior_rank tr td,
7220: table.LC_prior_match tr td {
1.524 albertel 7221: border: 1px solid #000000;
1.515 albertel 7222: }
7223:
1.855 bisitz 7224: .LC_nobreak {
1.544 albertel 7225: white-space: nowrap;
1.519 raeburn 7226: }
7227:
1.576 raeburn 7228: span.LC_cusr_emph {
7229: font-style: italic;
7230: }
7231:
1.633 raeburn 7232: span.LC_cusr_subheading {
7233: font-weight: normal;
7234: font-size: 85%;
7235: }
7236:
1.861 bisitz 7237: div.LC_docs_entry_move {
1.859 bisitz 7238: border: 1px solid #BBBBBB;
1.545 albertel 7239: background: #DDDDDD;
1.861 bisitz 7240: width: 22px;
1.859 bisitz 7241: padding: 1px;
7242: margin: 0;
1.545 albertel 7243: }
7244:
1.861 bisitz 7245: table.LC_data_table tr > td.LC_docs_entry_commands,
7246: table.LC_data_table tr > td.LC_docs_entry_parameter {
1.545 albertel 7247: font-size: x-small;
7248: }
1.795 www 7249:
1.861 bisitz 7250: .LC_docs_entry_parameter {
7251: white-space: nowrap;
7252: }
7253:
1.544 albertel 7254: .LC_docs_copy {
1.545 albertel 7255: color: #000099;
1.544 albertel 7256: }
1.795 www 7257:
1.544 albertel 7258: .LC_docs_cut {
1.545 albertel 7259: color: #550044;
1.544 albertel 7260: }
1.795 www 7261:
1.544 albertel 7262: .LC_docs_rename {
1.545 albertel 7263: color: #009900;
1.544 albertel 7264: }
1.795 www 7265:
1.544 albertel 7266: .LC_docs_remove {
1.545 albertel 7267: color: #990000;
7268: }
7269:
1.1284 raeburn 7270: .LC_docs_alias {
7271: color: #440055;
7272: }
7273:
1.1286 raeburn 7274: .LC_domprefs_email,
1.1284 raeburn 7275: .LC_docs_alias_name,
1.547 albertel 7276: .LC_docs_reinit_warn,
7277: .LC_docs_ext_edit {
7278: font-size: x-small;
7279: }
7280:
1.545 albertel 7281: table.LC_docs_adddocs td,
7282: table.LC_docs_adddocs th {
7283: border: 1px solid #BBBBBB;
7284: padding: 4px;
7285: background: #DDDDDD;
1.543 albertel 7286: }
7287:
1.584 albertel 7288: table.LC_sty_begin {
7289: background: #BBFFBB;
7290: }
1.795 www 7291:
1.584 albertel 7292: table.LC_sty_end {
7293: background: #FFBBBB;
7294: }
7295:
1.589 raeburn 7296: table.LC_double_column {
1.803 bisitz 7297: border-width: 0;
1.589 raeburn 7298: border-collapse: collapse;
7299: width: 100%;
7300: padding: 2px;
7301: }
7302:
7303: table.LC_double_column tr td.LC_left_col {
1.590 raeburn 7304: top: 2px;
1.589 raeburn 7305: left: 2px;
7306: width: 47%;
7307: vertical-align: top;
7308: }
7309:
7310: table.LC_double_column tr td.LC_right_col {
7311: top: 2px;
1.779 bisitz 7312: right: 2px;
1.589 raeburn 7313: width: 47%;
7314: vertical-align: top;
7315: }
7316:
1.591 raeburn 7317: div.LC_left_float {
7318: float: left;
7319: padding-right: 5%;
1.597 albertel 7320: padding-bottom: 4px;
1.591 raeburn 7321: }
7322:
7323: div.LC_clear_float_header {
1.597 albertel 7324: padding-bottom: 2px;
1.591 raeburn 7325: }
7326:
7327: div.LC_clear_float_footer {
1.597 albertel 7328: padding-top: 10px;
1.591 raeburn 7329: clear: both;
7330: }
7331:
1.597 albertel 7332: div.LC_grade_show_user {
1.941 bisitz 7333: /* border-left: 5px solid $sidebg; */
7334: border-top: 5px solid #000000;
7335: margin: 50px 0 0 0;
1.936 bisitz 7336: padding: 15px 0 5px 10px;
1.597 albertel 7337: }
1.795 www 7338:
1.936 bisitz 7339: div.LC_grade_show_user_odd_row {
1.941 bisitz 7340: /* border-left: 5px solid #000000; */
7341: }
7342:
7343: div.LC_grade_show_user div.LC_Box {
7344: margin-right: 50px;
1.597 albertel 7345: }
7346:
7347: div.LC_grade_submissions,
7348: div.LC_grade_message_center,
1.936 bisitz 7349: div.LC_grade_info_links {
1.597 albertel 7350: margin: 5px;
7351: width: 99%;
7352: background: #FFFFFF;
7353: }
1.795 www 7354:
1.597 albertel 7355: div.LC_grade_submissions_header,
1.936 bisitz 7356: div.LC_grade_message_center_header {
1.705 tempelho 7357: font-weight: bold;
7358: font-size: large;
1.597 albertel 7359: }
1.795 www 7360:
1.597 albertel 7361: div.LC_grade_submissions_body,
1.936 bisitz 7362: div.LC_grade_message_center_body {
1.597 albertel 7363: border: 1px solid black;
7364: width: 99%;
7365: background: #FFFFFF;
7366: }
1.795 www 7367:
1.613 albertel 7368: table.LC_scantron_action {
7369: width: 100%;
7370: }
1.795 www 7371:
1.613 albertel 7372: table.LC_scantron_action tr th {
1.698 harmsja 7373: font-weight:bold;
7374: font-style:normal;
1.613 albertel 7375: }
1.795 www 7376:
1.779 bisitz 7377: .LC_edit_problem_header,
1.614 albertel 7378: div.LC_edit_problem_footer {
1.705 tempelho 7379: font-weight: normal;
7380: font-size: medium;
1.602 albertel 7381: margin: 2px;
1.1060 bisitz 7382: background-color: $sidebg;
1.600 albertel 7383: }
1.795 www 7384:
1.600 albertel 7385: div.LC_edit_problem_header,
1.602 albertel 7386: div.LC_edit_problem_header div,
1.614 albertel 7387: div.LC_edit_problem_footer,
7388: div.LC_edit_problem_footer div,
1.602 albertel 7389: div.LC_edit_problem_editxml_header,
7390: div.LC_edit_problem_editxml_header div {
1.1205 golterma 7391: z-index: 100;
1.600 albertel 7392: }
1.795 www 7393:
1.600 albertel 7394: div.LC_edit_problem_header_title {
1.705 tempelho 7395: font-weight: bold;
7396: font-size: larger;
1.602 albertel 7397: background: $tabbg;
7398: padding: 3px;
1.1060 bisitz 7399: margin: 0 0 5px 0;
1.602 albertel 7400: }
1.795 www 7401:
1.602 albertel 7402: table.LC_edit_problem_header_title {
7403: width: 100%;
1.600 albertel 7404: background: $tabbg;
1.602 albertel 7405: }
7406:
1.1205 golterma 7407: div.LC_edit_actionbar {
7408: background-color: $sidebg;
1.1218 droeschl 7409: margin: 0;
7410: padding: 0;
7411: line-height: 200%;
1.602 albertel 7412: }
1.795 www 7413:
1.1218 droeschl 7414: div.LC_edit_actionbar div{
7415: padding: 0;
7416: margin: 0;
7417: display: inline-block;
1.600 albertel 7418: }
1.795 www 7419:
1.1124 bisitz 7420: .LC_edit_opt {
7421: padding-left: 1em;
7422: white-space: nowrap;
7423: }
7424:
1.1152 golterma 7425: .LC_edit_problem_latexhelper{
7426: text-align: right;
7427: }
7428:
7429: #LC_edit_problem_colorful div{
7430: margin-left: 40px;
7431: }
7432:
1.1205 golterma 7433: #LC_edit_problem_codemirror div{
7434: margin-left: 0px;
7435: }
7436:
1.911 bisitz 7437: img.stift {
1.803 bisitz 7438: border-width: 0;
7439: vertical-align: middle;
1.677 riegler 7440: }
1.680 riegler 7441:
1.923 bisitz 7442: table td.LC_mainmenu_col_fieldset {
1.680 riegler 7443: vertical-align: top;
1.777 tempelho 7444: }
1.795 www 7445:
1.716 raeburn 7446: div.LC_createcourse {
1.911 bisitz 7447: margin: 10px 10px 10px 10px;
1.716 raeburn 7448: }
7449:
1.917 raeburn 7450: .LC_dccid {
1.1130 raeburn 7451: float: right;
1.917 raeburn 7452: margin: 0.2em 0 0 0;
7453: padding: 0;
7454: font-size: 90%;
7455: display:none;
7456: }
7457:
1.897 wenzelju 7458: ol.LC_primary_menu a:hover,
1.721 harmsja 7459: ol#LC_MenuBreadcrumbs a:hover,
7460: ol#LC_PathBreadcrumbs a:hover,
1.897 wenzelju 7461: ul#LC_secondary_menu a:hover,
1.721 harmsja 7462: .LC_FormSectionClearButton input:hover
1.795 www 7463: ul.LC_TabContent li:hover a {
1.952 onken 7464: color:$button_hover;
1.911 bisitz 7465: text-decoration:none;
1.693 droeschl 7466: }
7467:
1.779 bisitz 7468: h1 {
1.911 bisitz 7469: padding: 0;
7470: line-height:130%;
1.693 droeschl 7471: }
1.698 harmsja 7472:
1.911 bisitz 7473: h2,
7474: h3,
7475: h4,
7476: h5,
7477: h6 {
7478: margin: 5px 0 5px 0;
7479: padding: 0;
7480: line-height:130%;
1.693 droeschl 7481: }
1.795 www 7482:
7483: .LC_hcell {
1.911 bisitz 7484: padding:3px 15px 3px 15px;
7485: margin: 0;
7486: background-color:$tabbg;
7487: color:$fontmenu;
7488: border-bottom:solid 1px $lg_border_color;
1.693 droeschl 7489: }
1.795 www 7490:
1.840 bisitz 7491: .LC_Box > .LC_hcell {
1.911 bisitz 7492: margin: 0 -10px 10px -10px;
1.835 bisitz 7493: }
7494:
1.721 harmsja 7495: .LC_noBorder {
1.911 bisitz 7496: border: 0;
1.698 harmsja 7497: }
1.693 droeschl 7498:
1.721 harmsja 7499: .LC_FormSectionClearButton input {
1.911 bisitz 7500: background-color:transparent;
7501: border: none;
7502: cursor:pointer;
7503: text-decoration:underline;
1.693 droeschl 7504: }
1.763 bisitz 7505:
7506: .LC_help_open_topic {
1.911 bisitz 7507: color: #FFFFFF;
7508: background-color: #EEEEFF;
7509: margin: 1px;
7510: padding: 4px;
7511: border: 1px solid #000033;
7512: white-space: nowrap;
7513: /* vertical-align: middle; */
1.759 neumanie 7514: }
1.693 droeschl 7515:
1.911 bisitz 7516: dl,
7517: ul,
7518: div,
7519: fieldset {
7520: margin: 10px 10px 10px 0;
7521: /* overflow: hidden; */
1.693 droeschl 7522: }
1.795 www 7523:
1.1211 raeburn 7524: article.geogebraweb div {
7525: margin: 0;
7526: }
7527:
1.838 bisitz 7528: fieldset > legend {
1.911 bisitz 7529: font-weight: bold;
7530: padding: 0 5px 0 5px;
1.838 bisitz 7531: }
7532:
1.813 bisitz 7533: #LC_nav_bar {
1.911 bisitz 7534: float: left;
1.995 raeburn 7535: background-color: $pgbg_or_bgcolor;
1.966 bisitz 7536: margin: 0 0 2px 0;
1.807 droeschl 7537: }
7538:
1.916 droeschl 7539: #LC_realm {
7540: margin: 0.2em 0 0 0;
7541: padding: 0;
7542: font-weight: bold;
7543: text-align: center;
1.995 raeburn 7544: background-color: $pgbg_or_bgcolor;
1.916 droeschl 7545: }
7546:
1.911 bisitz 7547: #LC_nav_bar em {
7548: font-weight: bold;
7549: font-style: normal;
1.807 droeschl 7550: }
7551:
1.897 wenzelju 7552: ol.LC_primary_menu {
1.934 droeschl 7553: margin: 0;
1.1076 raeburn 7554: padding: 0;
1.807 droeschl 7555: }
7556:
1.852 droeschl 7557: ol#LC_PathBreadcrumbs {
1.911 bisitz 7558: margin: 0;
1.693 droeschl 7559: }
7560:
1.897 wenzelju 7561: ol.LC_primary_menu li {
1.1076 raeburn 7562: color: RGB(80, 80, 80);
7563: vertical-align: middle;
7564: text-align: left;
7565: list-style: none;
1.1205 golterma 7566: position: relative;
1.1076 raeburn 7567: float: left;
1.1205 golterma 7568: z-index: 100; /* will be displayed above codemirror and underneath the help-layer */
7569: line-height: 1.5em;
1.1076 raeburn 7570: }
7571:
1.1205 golterma 7572: ol.LC_primary_menu li a,
7573: ol.LC_primary_menu li p {
1.1076 raeburn 7574: display: block;
7575: margin: 0;
7576: padding: 0 5px 0 10px;
7577: text-decoration: none;
7578: }
7579:
1.1205 golterma 7580: ol.LC_primary_menu li p span.LC_primary_menu_innertitle {
7581: display: inline-block;
7582: width: 95%;
7583: text-align: left;
7584: }
7585:
7586: ol.LC_primary_menu li p span.LC_primary_menu_innerarrow {
7587: display: inline-block;
7588: width: 5%;
7589: float: right;
7590: text-align: right;
7591: font-size: 70%;
7592: }
7593:
7594: ol.LC_primary_menu ul {
1.1076 raeburn 7595: display: none;
1.1205 golterma 7596: width: 15em;
1.1076 raeburn 7597: background-color: $data_table_light;
1.1205 golterma 7598: position: absolute;
7599: top: 100%;
1.1076 raeburn 7600: }
7601:
1.1205 golterma 7602: ol.LC_primary_menu ul ul {
7603: left: 100%;
7604: top: 0;
7605: }
7606:
7607: ol.LC_primary_menu li:hover > ul, ol.LC_primary_menu li.hover > ul {
1.1076 raeburn 7608: display: block;
7609: position: absolute;
7610: margin: 0;
7611: padding: 0;
1.1078 raeburn 7612: z-index: 2;
1.1076 raeburn 7613: }
7614:
7615: ol.LC_primary_menu li:hover li, ol.LC_primary_menu li.hover li {
1.1205 golterma 7616: /* First Submenu -> size should be smaller than the menu title of the whole menu */
1.1076 raeburn 7617: font-size: 90%;
1.911 bisitz 7618: vertical-align: top;
1.1076 raeburn 7619: float: none;
1.1079 raeburn 7620: border-left: 1px solid black;
7621: border-right: 1px solid black;
1.1205 golterma 7622: /* A dark bottom border to visualize different menu options;
7623: overwritten in the create_submenu routine for the last border-bottom of the menu */
7624: border-bottom: 1px solid $data_table_dark;
1.1076 raeburn 7625: }
7626:
1.1205 golterma 7627: ol.LC_primary_menu li li p:hover {
7628: color:$button_hover;
7629: text-decoration:none;
7630: background-color:$data_table_dark;
1.1076 raeburn 7631: }
7632:
7633: ol.LC_primary_menu li li a:hover {
7634: color:$button_hover;
7635: background-color:$data_table_dark;
1.693 droeschl 7636: }
7637:
1.1205 golterma 7638: /* Font-size equal to the size of the predecessors*/
7639: ol.LC_primary_menu li:hover li li {
7640: font-size: 100%;
7641: }
7642:
1.897 wenzelju 7643: ol.LC_primary_menu li img {
1.911 bisitz 7644: vertical-align: bottom;
1.934 droeschl 7645: height: 1.1em;
1.1077 raeburn 7646: margin: 0.2em 0 0 0;
1.693 droeschl 7647: }
7648:
1.897 wenzelju 7649: ol.LC_primary_menu a {
1.911 bisitz 7650: color: RGB(80, 80, 80);
7651: text-decoration: none;
1.693 droeschl 7652: }
1.795 www 7653:
1.949 droeschl 7654: ol.LC_primary_menu a.LC_new_message {
7655: font-weight:bold;
7656: color: darkred;
7657: }
7658:
1.975 raeburn 7659: ol.LC_docs_parameters {
7660: margin-left: 0;
7661: padding: 0;
7662: list-style: none;
7663: }
7664:
7665: ol.LC_docs_parameters li {
7666: margin: 0;
7667: padding-right: 20px;
7668: display: inline;
7669: }
7670:
1.976 raeburn 7671: ol.LC_docs_parameters li:before {
7672: content: "\\002022 \\0020";
7673: }
7674:
7675: li.LC_docs_parameters_title {
7676: font-weight: bold;
7677: }
7678:
7679: ol.LC_docs_parameters li.LC_docs_parameters_title:before {
7680: content: "";
7681: }
7682:
1.897 wenzelju 7683: ul#LC_secondary_menu {
1.1107 raeburn 7684: clear: right;
1.911 bisitz 7685: color: $fontmenu;
7686: background: $tabbg;
7687: list-style: none;
7688: padding: 0;
7689: margin: 0;
7690: width: 100%;
1.995 raeburn 7691: text-align: left;
1.1107 raeburn 7692: float: left;
1.808 droeschl 7693: }
7694:
1.897 wenzelju 7695: ul#LC_secondary_menu li {
1.911 bisitz 7696: font-weight: bold;
7697: line-height: 1.8em;
1.1107 raeburn 7698: border-right: 1px solid black;
7699: float: left;
7700: }
7701:
7702: ul#LC_secondary_menu li.LC_hoverable:hover, ul#LC_secondary_menu li.hover {
7703: background-color: $data_table_light;
7704: }
7705:
7706: ul#LC_secondary_menu li a {
1.911 bisitz 7707: padding: 0 0.8em;
1.1107 raeburn 7708: }
7709:
7710: ul#LC_secondary_menu li ul {
7711: display: none;
7712: }
7713:
7714: ul#LC_secondary_menu li:hover ul, ul#LC_secondary_menu li.hover ul {
7715: display: block;
7716: position: absolute;
7717: margin: 0;
7718: padding: 0;
7719: list-style:none;
7720: float: none;
7721: background-color: $data_table_light;
7722: z-index: 2;
7723: margin-left: -1px;
7724: }
7725:
7726: ul#LC_secondary_menu li ul li {
7727: font-size: 90%;
7728: vertical-align: top;
7729: border-left: 1px solid black;
1.911 bisitz 7730: border-right: 1px solid black;
1.1119 raeburn 7731: background-color: $data_table_light;
1.1107 raeburn 7732: list-style:none;
7733: float: none;
7734: }
7735:
7736: ul#LC_secondary_menu li ul li:hover, ul#LC_secondary_menu li ul li.hover {
7737: background-color: $data_table_dark;
1.807 droeschl 7738: }
7739:
1.847 tempelho 7740: ul.LC_TabContent {
1.911 bisitz 7741: display:block;
7742: background: $sidebg;
7743: border-bottom: solid 1px $lg_border_color;
7744: list-style:none;
1.1020 raeburn 7745: margin: -1px -10px 0 -10px;
1.911 bisitz 7746: padding: 0;
1.693 droeschl 7747: }
7748:
1.795 www 7749: ul.LC_TabContent li,
7750: ul.LC_TabContentBigger li {
1.911 bisitz 7751: float:left;
1.741 harmsja 7752: }
1.795 www 7753:
1.897 wenzelju 7754: ul#LC_secondary_menu li a {
1.911 bisitz 7755: color: $fontmenu;
7756: text-decoration: none;
1.693 droeschl 7757: }
1.795 www 7758:
1.721 harmsja 7759: ul.LC_TabContent {
1.952 onken 7760: min-height:20px;
1.721 harmsja 7761: }
1.795 www 7762:
7763: ul.LC_TabContent li {
1.911 bisitz 7764: vertical-align:middle;
1.959 onken 7765: padding: 0 16px 0 10px;
1.911 bisitz 7766: background-color:$tabbg;
7767: border-bottom:solid 1px $lg_border_color;
1.1020 raeburn 7768: border-left: solid 1px $font;
1.721 harmsja 7769: }
1.795 www 7770:
1.847 tempelho 7771: ul.LC_TabContent .right {
1.911 bisitz 7772: float:right;
1.847 tempelho 7773: }
7774:
1.911 bisitz 7775: ul.LC_TabContent li a,
7776: ul.LC_TabContent li {
7777: color:rgb(47,47,47);
7778: text-decoration:none;
7779: font-size:95%;
7780: font-weight:bold;
1.952 onken 7781: min-height:20px;
7782: }
7783:
1.959 onken 7784: ul.LC_TabContent li a:hover,
7785: ul.LC_TabContent li a:focus {
1.952 onken 7786: color: $button_hover;
1.959 onken 7787: background:none;
7788: outline:none;
1.952 onken 7789: }
7790:
7791: ul.LC_TabContent li:hover {
7792: color: $button_hover;
7793: cursor:pointer;
1.721 harmsja 7794: }
1.795 www 7795:
1.911 bisitz 7796: ul.LC_TabContent li.active {
1.952 onken 7797: color: $font;
1.911 bisitz 7798: background:#FFFFFF url(/adm/lonIcons/open.gif) no-repeat scroll right center;
1.952 onken 7799: border-bottom:solid 1px #FFFFFF;
7800: cursor: default;
1.744 ehlerst 7801: }
1.795 www 7802:
1.959 onken 7803: ul.LC_TabContent li.active a {
7804: color:$font;
7805: background:#FFFFFF;
7806: outline: none;
7807: }
1.1047 raeburn 7808:
7809: ul.LC_TabContent li.goback {
7810: float: left;
7811: border-left: none;
7812: }
7813:
1.870 tempelho 7814: #maincoursedoc {
1.911 bisitz 7815: clear:both;
1.870 tempelho 7816: }
7817:
7818: ul.LC_TabContentBigger {
1.911 bisitz 7819: display:block;
7820: list-style:none;
7821: padding: 0;
1.870 tempelho 7822: }
7823:
1.795 www 7824: ul.LC_TabContentBigger li {
1.911 bisitz 7825: vertical-align:bottom;
7826: height: 30px;
7827: font-size:110%;
7828: font-weight:bold;
7829: color: #737373;
1.841 tempelho 7830: }
7831:
1.957 onken 7832: ul.LC_TabContentBigger li.active {
7833: position: relative;
7834: top: 1px;
7835: }
7836:
1.870 tempelho 7837: ul.LC_TabContentBigger li a {
1.911 bisitz 7838: background:url('/adm/lonIcons/tabbgleft.gif') left bottom no-repeat;
7839: height: 30px;
7840: line-height: 30px;
7841: text-align: center;
7842: display: block;
7843: text-decoration: none;
1.958 onken 7844: outline: none;
1.741 harmsja 7845: }
1.795 www 7846:
1.870 tempelho 7847: ul.LC_TabContentBigger li.active a {
1.911 bisitz 7848: background:url('/adm/lonIcons/tabbgleft.gif') left top no-repeat;
7849: color:$font;
1.744 ehlerst 7850: }
1.795 www 7851:
1.870 tempelho 7852: ul.LC_TabContentBigger li b {
1.911 bisitz 7853: background: url('/adm/lonIcons/tabbgright.gif') no-repeat right bottom;
7854: display: block;
7855: float: left;
7856: padding: 0 30px;
1.957 onken 7857: border-bottom: 1px solid $lg_border_color;
1.870 tempelho 7858: }
7859:
1.956 onken 7860: ul.LC_TabContentBigger li:hover b {
7861: color:$button_hover;
7862: }
7863:
1.870 tempelho 7864: ul.LC_TabContentBigger li.active b {
1.911 bisitz 7865: background:url('/adm/lonIcons/tabbgright.gif') right top no-repeat;
7866: color:$font;
1.957 onken 7867: border: 0;
1.741 harmsja 7868: }
1.693 droeschl 7869:
1.870 tempelho 7870:
1.862 bisitz 7871: ul.LC_CourseBreadcrumbs {
7872: background: $sidebg;
1.1020 raeburn 7873: height: 2em;
1.862 bisitz 7874: padding-left: 10px;
1.1020 raeburn 7875: margin: 0;
1.862 bisitz 7876: list-style-position: inside;
7877: }
7878:
1.911 bisitz 7879: ol#LC_MenuBreadcrumbs,
1.862 bisitz 7880: ol#LC_PathBreadcrumbs {
1.911 bisitz 7881: padding-left: 10px;
7882: margin: 0;
1.933 droeschl 7883: height: 2.5em; /* equal to #LC_breadcrumbs line-height */
1.693 droeschl 7884: }
7885:
1.911 bisitz 7886: ol#LC_MenuBreadcrumbs li,
7887: ol#LC_PathBreadcrumbs li,
1.862 bisitz 7888: ul.LC_CourseBreadcrumbs li {
1.911 bisitz 7889: display: inline;
1.933 droeschl 7890: white-space: normal;
1.693 droeschl 7891: }
7892:
1.823 bisitz 7893: ol#LC_MenuBreadcrumbs li a,
1.862 bisitz 7894: ul.LC_CourseBreadcrumbs li a {
1.911 bisitz 7895: text-decoration: none;
7896: font-size:90%;
1.693 droeschl 7897: }
1.795 www 7898:
1.969 droeschl 7899: ol#LC_MenuBreadcrumbs h1 {
7900: display: inline;
7901: font-size: 90%;
7902: line-height: 2.5em;
7903: margin: 0;
7904: padding: 0;
7905: }
7906:
1.795 www 7907: ol#LC_PathBreadcrumbs li a {
1.911 bisitz 7908: text-decoration:none;
7909: font-size:100%;
7910: font-weight:bold;
1.693 droeschl 7911: }
1.795 www 7912:
1.840 bisitz 7913: .LC_Box {
1.911 bisitz 7914: border: solid 1px $lg_border_color;
7915: padding: 0 10px 10px 10px;
1.746 neumanie 7916: }
1.795 www 7917:
1.1020 raeburn 7918: .LC_DocsBox {
7919: border: solid 1px $lg_border_color;
7920: padding: 0 0 10px 10px;
7921: }
7922:
1.795 www 7923: .LC_AboutMe_Image {
1.911 bisitz 7924: float:left;
7925: margin-right:10px;
1.747 neumanie 7926: }
1.795 www 7927:
7928: .LC_Clear_AboutMe_Image {
1.911 bisitz 7929: clear:left;
1.747 neumanie 7930: }
1.795 www 7931:
1.721 harmsja 7932: dl.LC_ListStyleClean dt {
1.911 bisitz 7933: padding-right: 5px;
7934: display: table-header-group;
1.693 droeschl 7935: }
7936:
1.721 harmsja 7937: dl.LC_ListStyleClean dd {
1.911 bisitz 7938: display: table-row;
1.693 droeschl 7939: }
7940:
1.721 harmsja 7941: .LC_ListStyleClean,
7942: .LC_ListStyleSimple,
7943: .LC_ListStyleNormal,
1.795 www 7944: .LC_ListStyleSpecial {
1.911 bisitz 7945: /* display:block; */
7946: list-style-position: inside;
7947: list-style-type: none;
7948: overflow: hidden;
7949: padding: 0;
1.693 droeschl 7950: }
7951:
1.721 harmsja 7952: .LC_ListStyleSimple li,
7953: .LC_ListStyleSimple dd,
7954: .LC_ListStyleNormal li,
7955: .LC_ListStyleNormal dd,
7956: .LC_ListStyleSpecial li,
1.795 www 7957: .LC_ListStyleSpecial dd {
1.911 bisitz 7958: margin: 0;
7959: padding: 5px 5px 5px 10px;
7960: clear: both;
1.693 droeschl 7961: }
7962:
1.721 harmsja 7963: .LC_ListStyleClean li,
7964: .LC_ListStyleClean dd {
1.911 bisitz 7965: padding-top: 0;
7966: padding-bottom: 0;
1.693 droeschl 7967: }
7968:
1.721 harmsja 7969: .LC_ListStyleSimple dd,
1.795 www 7970: .LC_ListStyleSimple li {
1.911 bisitz 7971: border-bottom: solid 1px $lg_border_color;
1.693 droeschl 7972: }
7973:
1.721 harmsja 7974: .LC_ListStyleSpecial li,
7975: .LC_ListStyleSpecial dd {
1.911 bisitz 7976: list-style-type: none;
7977: background-color: RGB(220, 220, 220);
7978: margin-bottom: 4px;
1.693 droeschl 7979: }
7980:
1.721 harmsja 7981: table.LC_SimpleTable {
1.911 bisitz 7982: margin:5px;
7983: border:solid 1px $lg_border_color;
1.795 www 7984: }
1.693 droeschl 7985:
1.721 harmsja 7986: table.LC_SimpleTable tr {
1.911 bisitz 7987: padding: 0;
7988: border:solid 1px $lg_border_color;
1.693 droeschl 7989: }
1.795 www 7990:
7991: table.LC_SimpleTable thead {
1.911 bisitz 7992: background:rgb(220,220,220);
1.693 droeschl 7993: }
7994:
1.721 harmsja 7995: div.LC_columnSection {
1.911 bisitz 7996: display: block;
7997: clear: both;
7998: overflow: hidden;
7999: margin: 0;
1.693 droeschl 8000: }
8001:
1.721 harmsja 8002: div.LC_columnSection>* {
1.911 bisitz 8003: float: left;
8004: margin: 10px 20px 10px 0;
8005: overflow:hidden;
1.693 droeschl 8006: }
1.721 harmsja 8007:
1.795 www 8008: table em {
1.911 bisitz 8009: font-weight: bold;
8010: font-style: normal;
1.748 schulted 8011: }
1.795 www 8012:
1.779 bisitz 8013: table.LC_tableBrowseRes,
1.795 www 8014: table.LC_tableOfContent {
1.911 bisitz 8015: border:none;
8016: border-spacing: 1px;
8017: padding: 3px;
8018: background-color: #FFFFFF;
8019: font-size: 90%;
1.753 droeschl 8020: }
1.789 droeschl 8021:
1.911 bisitz 8022: table.LC_tableOfContent {
8023: border-collapse: collapse;
1.789 droeschl 8024: }
8025:
1.771 droeschl 8026: table.LC_tableBrowseRes a,
1.768 schulted 8027: table.LC_tableOfContent a {
1.911 bisitz 8028: background-color: transparent;
8029: text-decoration: none;
1.753 droeschl 8030: }
8031:
1.795 www 8032: table.LC_tableOfContent img {
1.911 bisitz 8033: border: none;
8034: height: 1.3em;
8035: vertical-align: text-bottom;
8036: margin-right: 0.3em;
1.753 droeschl 8037: }
1.757 schulted 8038:
1.795 www 8039: a#LC_content_toolbar_firsthomework {
1.911 bisitz 8040: background-image:url(/res/adm/pages/open-first-problem.gif);
1.774 ehlerst 8041: }
8042:
1.795 www 8043: a#LC_content_toolbar_everything {
1.911 bisitz 8044: background-image:url(/res/adm/pages/show-all.gif);
1.774 ehlerst 8045: }
8046:
1.795 www 8047: a#LC_content_toolbar_uncompleted {
1.911 bisitz 8048: background-image:url(/res/adm/pages/show-incomplete-problems.gif);
1.774 ehlerst 8049: }
8050:
1.795 www 8051: #LC_content_toolbar_clearbubbles {
1.911 bisitz 8052: background-image:url(/res/adm/pages/mark-discussionentries-read.gif);
1.774 ehlerst 8053: }
8054:
1.795 www 8055: a#LC_content_toolbar_changefolder {
1.911 bisitz 8056: background : url(/res/adm/pages/close-all-folders.gif) top center ;
1.757 schulted 8057: }
8058:
1.795 www 8059: a#LC_content_toolbar_changefolder_toggled {
1.911 bisitz 8060: background-image:url(/res/adm/pages/open-all-folders.gif);
1.757 schulted 8061: }
8062:
1.1043 raeburn 8063: a#LC_content_toolbar_edittoplevel {
8064: background-image:url(/res/adm/pages/edittoplevel.gif);
8065: }
8066:
1.795 www 8067: ul#LC_toolbar li a:hover {
1.911 bisitz 8068: background-position: bottom center;
1.757 schulted 8069: }
8070:
1.795 www 8071: ul#LC_toolbar {
1.911 bisitz 8072: padding: 0;
8073: margin: 2px;
8074: list-style:none;
8075: position:relative;
8076: background-color:white;
1.1082 raeburn 8077: overflow: auto;
1.757 schulted 8078: }
8079:
1.795 www 8080: ul#LC_toolbar li {
1.911 bisitz 8081: border:1px solid white;
8082: padding: 0;
8083: margin: 0;
8084: float: left;
8085: display:inline;
8086: vertical-align:middle;
1.1082 raeburn 8087: white-space: nowrap;
1.911 bisitz 8088: }
1.757 schulted 8089:
1.783 amueller 8090:
1.795 www 8091: a.LC_toolbarItem {
1.911 bisitz 8092: display:block;
8093: padding: 0;
8094: margin: 0;
8095: height: 32px;
8096: width: 32px;
8097: color:white;
8098: border: none;
8099: background-repeat:no-repeat;
8100: background-color:transparent;
1.757 schulted 8101: }
8102:
1.915 droeschl 8103: ul.LC_funclist {
8104: margin: 0;
8105: padding: 0.5em 1em 0.5em 0;
8106: }
8107:
1.933 droeschl 8108: ul.LC_funclist > li:first-child {
8109: font-weight:bold;
8110: margin-left:0.8em;
8111: }
8112:
1.915 droeschl 8113: ul.LC_funclist + ul.LC_funclist {
8114: /*
8115: left border as a seperator if we have more than
8116: one list
8117: */
8118: border-left: 1px solid $sidebg;
8119: /*
8120: this hides the left border behind the border of the
8121: outer box if element is wrapped to the next 'line'
8122: */
8123: margin-left: -1px;
8124: }
8125:
1.843 bisitz 8126: ul.LC_funclist li {
1.915 droeschl 8127: display: inline;
1.782 bisitz 8128: white-space: nowrap;
1.915 droeschl 8129: margin: 0 0 0 25px;
8130: line-height: 150%;
1.782 bisitz 8131: }
8132:
1.974 wenzelju 8133: .LC_hidden {
8134: display: none;
8135: }
8136:
1.1030 www 8137: .LCmodal-overlay {
8138: position:fixed;
8139: top:0;
8140: right:0;
8141: bottom:0;
8142: left:0;
8143: height:100%;
8144: width:100%;
8145: margin:0;
8146: padding:0;
8147: background:#999;
8148: opacity:.75;
8149: filter: alpha(opacity=75);
8150: -moz-opacity: 0.75;
8151: z-index:101;
8152: }
8153:
8154: * html .LCmodal-overlay {
8155: position: absolute;
8156: height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px');
8157: }
8158:
8159: .LCmodal-window {
8160: position:fixed;
8161: top:50%;
8162: left:50%;
8163: margin:0;
8164: padding:0;
8165: z-index:102;
8166: }
8167:
8168: * html .LCmodal-window {
8169: position:absolute;
8170: }
8171:
8172: .LCclose-window {
8173: position:absolute;
8174: width:32px;
8175: height:32px;
8176: right:8px;
8177: top:8px;
8178: background:transparent url('/res/adm/pages/process-stop.png') no-repeat scroll right top;
8179: text-indent:-99999px;
8180: overflow:hidden;
8181: cursor:pointer;
8182: }
8183:
1.1100 raeburn 8184: /*
1.1231 damieng 8185: styles used for response display
8186: */
8187: div.LC_radiofoil, div.LC_rankfoil {
8188: margin: .5em 0em .5em 0em;
8189: }
8190: table.LC_itemgroup {
8191: margin-top: 1em;
8192: }
8193:
8194: /*
1.1100 raeburn 8195: styles used by TTH when "Default set of options to pass to tth/m
8196: when converting TeX" in course settings has been set
8197:
8198: option passed: -t
8199:
8200: */
8201:
8202: td div.comp { margin-top: -0.6ex; margin-bottom: -1ex;}
8203: td div.comb { margin-top: -0.6ex; margin-bottom: -.6ex;}
8204: td div.hrcomp { line-height: 0.9; margin-top: -0.8ex; margin-bottom: -1ex;}
8205: td div.norm {line-height:normal;}
8206:
8207: /*
8208: option passed -y3
8209: */
8210:
8211: span.roman {font-family: serif; font-style: normal; font-weight: normal;}
8212: span.overacc2 {position: relative; left: .8em; top: -1.2ex;}
8213: span.overacc1 {position: relative; left: .6em; top: -1.2ex;}
8214:
1.1230 damieng 8215: /*
8216: sections with roles, for content only
8217: */
8218: section[class^="role-"] {
8219: padding-left: 10px;
8220: padding-right: 5px;
8221: margin-top: 8px;
8222: margin-bottom: 8px;
8223: border: 1px solid #2A4;
8224: border-radius: 5px;
8225: box-shadow: 0px 1px 1px #BBB;
8226: }
8227: section[class^="role-"]>h1 {
8228: position: relative;
8229: margin: 0px;
8230: padding-top: 10px;
8231: padding-left: 40px;
8232: }
8233: section[class^="role-"]>h1:before {
8234: position: absolute;
8235: left: -5px;
8236: top: 5px;
8237: }
8238: section.role-activity>h1:before {
8239: content:url('/adm/daxe/images/section_icons/activity.png');
8240: }
8241: section.role-advice>h1:before {
8242: content:url('/adm/daxe/images/section_icons/advice.png');
8243: }
8244: section.role-bibliography>h1:before {
8245: content:url('/adm/daxe/images/section_icons/bibliography.png');
8246: }
8247: section.role-citation>h1:before {
8248: content:url('/adm/daxe/images/section_icons/citation.png');
8249: }
8250: section.role-conclusion>h1:before {
8251: content:url('/adm/daxe/images/section_icons/conclusion.png');
8252: }
8253: section.role-definition>h1:before {
8254: content:url('/adm/daxe/images/section_icons/definition.png');
8255: }
8256: section.role-demonstration>h1:before {
8257: content:url('/adm/daxe/images/section_icons/demonstration.png');
8258: }
8259: section.role-example>h1:before {
8260: content:url('/adm/daxe/images/section_icons/example.png');
8261: }
8262: section.role-explanation>h1:before {
8263: content:url('/adm/daxe/images/section_icons/explanation.png');
8264: }
8265: section.role-introduction>h1:before {
8266: content:url('/adm/daxe/images/section_icons/introduction.png');
8267: }
8268: section.role-method>h1:before {
8269: content:url('/adm/daxe/images/section_icons/method.png');
8270: }
8271: section.role-more_information>h1:before {
8272: content:url('/adm/daxe/images/section_icons/more_information.png');
8273: }
8274: section.role-objectives>h1:before {
8275: content:url('/adm/daxe/images/section_icons/objectives.png');
8276: }
8277: section.role-prerequisites>h1:before {
8278: content:url('/adm/daxe/images/section_icons/prerequisites.png');
8279: }
8280: section.role-remark>h1:before {
8281: content:url('/adm/daxe/images/section_icons/remark.png');
8282: }
8283: section.role-reminder>h1:before {
8284: content:url('/adm/daxe/images/section_icons/reminder.png');
8285: }
8286: section.role-summary>h1:before {
8287: content:url('/adm/daxe/images/section_icons/summary.png');
8288: }
8289: section.role-syntax>h1:before {
8290: content:url('/adm/daxe/images/section_icons/syntax.png');
8291: }
8292: section.role-warning>h1:before {
8293: content:url('/adm/daxe/images/section_icons/warning.png');
8294: }
8295:
1.1269 raeburn 8296: #LC_minitab_header {
8297: float:left;
8298: width:100%;
8299: background:#DAE0D2 url("/res/adm/pages/minitabmenu_bg.gif") repeat-x bottom;
8300: font-size:93%;
8301: line-height:normal;
8302: margin: 0.5em 0 0.5em 0;
8303: }
8304: #LC_minitab_header ul {
8305: margin:0;
8306: padding:10px 10px 0;
8307: list-style:none;
8308: }
8309: #LC_minitab_header li {
8310: float:left;
8311: background:url("/res/adm/pages/minitabmenu_left.gif") no-repeat left top;
8312: margin:0;
8313: padding:0 0 0 9px;
8314: }
8315: #LC_minitab_header a {
8316: display:block;
8317: background:url("/res/adm/pages/minitabmenu_right.gif") no-repeat right top;
8318: padding:5px 15px 4px 6px;
8319: }
8320: #LC_minitab_header #LC_current_minitab {
8321: background-image:url("/res/adm/pages/minitabmenu_left_on.gif");
8322: }
8323: #LC_minitab_header #LC_current_minitab a {
8324: background-image:url("/res/adm/pages/minitabmenu_right_on.gif");
8325: padding-bottom:5px;
8326: }
8327:
8328:
1.343 albertel 8329: END
8330: }
8331:
1.306 albertel 8332: =pod
8333:
8334: =item * &headtag()
8335:
8336: Returns a uniform footer for LON-CAPA web pages.
8337:
1.307 albertel 8338: Inputs: $title - optional title for the head
8339: $head_extra - optional extra HTML to put inside the <head>
1.315 albertel 8340: $args - optional arguments
1.319 albertel 8341: force_register - if is true call registerurl so the remote is
8342: informed
1.415 albertel 8343: redirect -> array ref of
8344: 1- seconds before redirect occurs
8345: 2- url to redirect to
8346: 3- whether the side effect should occur
1.315 albertel 8347: (side effect of setting
8348: $env{'internal.head.redirect'} to the url
8349: redirected too)
1.352 albertel 8350: domain -> force to color decorate a page for a specific
8351: domain
8352: function -> force usage of a specific rolish color scheme
8353: bgcolor -> override the default page bgcolor
1.460 albertel 8354: no_auto_mt_title
8355: -> prevent &mt()ing the title arg
1.464 albertel 8356:
1.306 albertel 8357: =cut
8358:
8359: sub headtag {
1.313 albertel 8360: my ($title,$head_extra,$args) = @_;
1.306 albertel 8361:
1.363 albertel 8362: my $function = $args->{'function'} || &get_users_function();
8363: my $domain = $args->{'domain'} || &determinedomain();
8364: my $bgcolor = $args->{'bgcolor'} || &designparm($function.'.pgbg',$domain);
1.1154 raeburn 8365: my $httphost = $args->{'use_absolute'};
1.418 albertel 8366: my $url = join(':',$env{'user.name'},$env{'user.domain'},
1.458 albertel 8367: $Apache::lonnet::perlvar{'lonVersion'},
1.531 albertel 8368: #time(),
1.418 albertel 8369: $env{'environment.color.timestamp'},
1.363 albertel 8370: $function,$domain,$bgcolor);
8371:
1.369 www 8372: $url = '/adm/css/'.&escape($url).'.css';
1.363 albertel 8373:
1.308 albertel 8374: my $result =
8375: '<head>'.
1.1160 raeburn 8376: &font_settings($args);
1.319 albertel 8377:
1.1188 raeburn 8378: my $inhibitprint;
8379: if ($args->{'print_suppress'}) {
8380: $inhibitprint = &print_suppression();
8381: }
1.1064 raeburn 8382:
1.461 albertel 8383: if (!$args->{'frameset'}) {
8384: $result .= &Apache::lonhtmlcommon::htmlareaheaders();
8385: }
1.962 droeschl 8386: if ($args->{'force_register'} && $env{'request.noversionuri'} !~ m{^/res/adm/pages/}) {
8387: $result .= Apache::lonxml::display_title();
1.319 albertel 8388: }
1.436 albertel 8389: if (!$args->{'no_nav_bar'}
8390: && !$args->{'only_body'}
8391: && !$args->{'frameset'}) {
1.1154 raeburn 8392: $result .= &help_menu_js($httphost);
1.1032 www 8393: $result.=&modal_window();
1.1038 www 8394: $result.=&togglebox_script();
1.1034 www 8395: $result.=&wishlist_window();
1.1041 www 8396: $result.=&LCprogressbarUpdate_script();
1.1034 www 8397: } else {
8398: if ($args->{'add_modal'}) {
8399: $result.=&modal_window();
8400: }
8401: if ($args->{'add_wishlist'}) {
8402: $result.=&wishlist_window();
8403: }
1.1038 www 8404: if ($args->{'add_togglebox'}) {
8405: $result.=&togglebox_script();
8406: }
1.1041 www 8407: if ($args->{'add_progressbar'}) {
8408: $result.=&LCprogressbarUpdate_script();
8409: }
1.436 albertel 8410: }
1.314 albertel 8411: if (ref($args->{'redirect'})) {
1.414 albertel 8412: my ($time,$url,$inhibit_continue) = @{$args->{'redirect'}};
1.315 albertel 8413: $url = &Apache::lonenc::check_encrypt($url);
1.414 albertel 8414: if (!$inhibit_continue) {
8415: $env{'internal.head.redirect'} = $url;
8416: }
1.313 albertel 8417: $result.=<<ADDMETA
8418: <meta http-equiv="pragma" content="no-cache" />
1.344 albertel 8419: <meta http-equiv="Refresh" content="$time; url=$url" />
1.313 albertel 8420: ADDMETA
1.1210 raeburn 8421: } else {
8422: unless (($args->{'frameset'}) || ($args->{'js_ready'}) || ($args->{'only_body'}) || ($args->{'no_nav_bar'})) {
8423: my $requrl = $env{'request.uri'};
8424: if ($requrl eq '') {
8425: $requrl = $ENV{'REQUEST_URI'};
8426: $requrl =~ s/\?.+$//;
8427: }
8428: unless (($requrl =~ m{^/adm/(?:switchserver|login|authenticate|logout|groupsort|cleanup|helper|slotrequest|grades)(\?|$)}) ||
8429: (($requrl =~ m{^/res/}) && (($env{'form.submitted'} eq 'scantron') ||
8430: ($env{'form.grade_symb'}) || ($Apache::lonhomework::scantronmode)))) {
8431: my $dom_in_use = $Apache::lonnet::perlvar{'lonDefDomain'};
8432: unless (&Apache::lonnet::allowed('mau',$dom_in_use)) {
8433: my %domdefs = &Apache::lonnet::get_domain_defaults($dom_in_use);
8434: if (ref($domdefs{'offloadnow'}) eq 'HASH') {
8435: my $lonhost = $Apache::lonnet::perlvar{'lonHostID'};
8436: if ($domdefs{'offloadnow'}{$lonhost}) {
8437: my $newserver = &Apache::lonnet::spareserver(30000,undef,1,$dom_in_use);
8438: if (($newserver) && ($newserver ne $lonhost)) {
8439: my $numsec = 5;
8440: my $timeout = $numsec * 1000;
8441: my ($newurl,$locknum,%locks,$msg);
8442: if ($env{'request.role.adv'}) {
8443: ($locknum,%locks) = &Apache::lonnet::get_locks();
8444: }
8445: my $disable_submit = 0;
8446: if ($requrl =~ /$LONCAPA::assess_re/) {
8447: $disable_submit = 1;
8448: }
8449: if ($locknum) {
8450: my @lockinfo = sort(values(%locks));
8451: $msg = &mt('Once the following tasks are complete: ')."\\n".
8452: join(", ",sort(values(%locks)))."\\n".
8453: &mt('your session will be transferred to a different server, after you click "Roles".');
8454: } else {
8455: if (($requrl =~ m{^/res/}) && ($env{'form.submitted'} =~ /^part_/)) {
8456: $msg = &mt('Your LON-CAPA submission has been recorded')."\\n";
8457: }
8458: $msg .= &mt('Your current LON-CAPA session will be transferred to a different server in [quant,_1,second].',$numsec);
8459: $newurl = '/adm/switchserver?otherserver='.$newserver;
8460: if (($env{'request.role'}) && ($env{'request.role'} ne 'cm')) {
8461: $newurl .= '&role='.$env{'request.role'};
8462: }
8463: if ($env{'request.symb'}) {
8464: $newurl .= '&symb='.$env{'request.symb'};
8465: } else {
8466: $newurl .= '&origurl='.$requrl;
8467: }
8468: }
1.1222 damieng 8469: &js_escape(\$msg);
1.1210 raeburn 8470: $result.=<<OFFLOAD
8471: <meta http-equiv="pragma" content="no-cache" />
8472: <script type="text/javascript">
1.1215 raeburn 8473: // <![CDATA[
1.1210 raeburn 8474: function LC_Offload_Now() {
8475: var dest = "$newurl";
8476: if (dest != '') {
8477: window.location.href="$newurl";
8478: }
8479: }
1.1214 raeburn 8480: \$(document).ready(function () {
8481: window.alert('$msg');
8482: if ($disable_submit) {
1.1210 raeburn 8483: \$(".LC_hwk_submit").prop("disabled", true);
8484: \$( ".LC_textline" ).prop( "readonly", "readonly");
1.1214 raeburn 8485: }
8486: setTimeout('LC_Offload_Now()', $timeout);
8487: });
1.1215 raeburn 8488: // ]]>
1.1210 raeburn 8489: </script>
8490: OFFLOAD
8491: }
8492: }
8493: }
8494: }
8495: }
8496: }
1.313 albertel 8497: }
1.306 albertel 8498: if (!defined($title)) {
8499: $title = 'The LearningOnline Network with CAPA';
8500: }
1.460 albertel 8501: if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
8502: $result .= '<title> LON-CAPA '.$title.'</title>'
1.1168 raeburn 8503: .'<link rel="stylesheet" type="text/css" href="'.$url.'"';
8504: if (!$args->{'frameset'}) {
8505: $result .= ' /';
8506: }
8507: $result .= '>'
1.1064 raeburn 8508: .$inhibitprint
1.414 albertel 8509: .$head_extra;
1.1242 raeburn 8510: my $clientmobile;
8511: if (($env{'user.name'} eq '') && ($env{'user.domain'} eq '')) {
8512: (undef,undef,undef,undef,undef,undef,$clientmobile) = &decode_user_agent();
8513: } else {
8514: $clientmobile = $env{'browser.mobile'};
8515: }
8516: if ($clientmobile) {
1.1137 raeburn 8517: $result .= '
8518: <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0">
8519: <meta name="apple-mobile-web-app-capable" content="yes" />';
8520: }
1.1278 raeburn 8521: $result .= '<meta name="google" content="notranslate" />'."\n";
1.962 droeschl 8522: return $result.'</head>';
1.306 albertel 8523: }
8524:
8525: =pod
8526:
1.340 albertel 8527: =item * &font_settings()
8528:
8529: Returns neccessary <meta> to set the proper encoding
8530:
1.1160 raeburn 8531: Inputs: optional reference to HASH -- $args passed to &headtag()
1.340 albertel 8532:
8533: =cut
8534:
8535: sub font_settings {
1.1160 raeburn 8536: my ($args) = @_;
1.340 albertel 8537: my $headerstring='';
1.1160 raeburn 8538: if ((!$env{'browser.mathml'} && $env{'browser.unicode'}) ||
8539: ((ref($args) eq 'HASH') && ($args->{'browser.unicode'}))) {
1.1168 raeburn 8540: $headerstring.=
8541: '<meta http-equiv="Content-Type" content="text/html; charset=utf-8"';
8542: if (!$args->{'frameset'}) {
8543: $headerstring.= ' /';
8544: }
8545: $headerstring .= '>'."\n";
1.340 albertel 8546: }
8547: return $headerstring;
8548: }
8549:
1.341 albertel 8550: =pod
8551:
1.1064 raeburn 8552: =item * &print_suppression()
8553:
8554: In course context returns css which causes the body to be blank when media="print",
8555: if printout generation is unavailable for the current resource.
8556:
8557: This could be because:
8558:
8559: (a) printstartdate is in the future
8560:
8561: (b) printenddate is in the past
8562:
8563: (c) there is an active exam block with "printout"
8564: functionality blocked
8565:
8566: Users with pav, pfo or evb privileges are exempt.
8567:
8568: Inputs: none
8569:
8570: =cut
8571:
8572:
8573: sub print_suppression {
8574: my $noprint;
8575: if ($env{'request.course.id'}) {
8576: my $scope = $env{'request.course.id'};
8577: if ((&Apache::lonnet::allowed('pav',$scope)) ||
8578: (&Apache::lonnet::allowed('pfo',$scope))) {
8579: return;
8580: }
8581: if ($env{'request.course.sec'} ne '') {
8582: $scope .= "/$env{'request.course.sec'}";
8583: if ((&Apache::lonnet::allowed('pav',$scope)) ||
8584: (&Apache::lonnet::allowed('pfo',$scope))) {
1.1065 raeburn 8585: return;
1.1064 raeburn 8586: }
8587: }
8588: my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
8589: my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
1.1189 raeburn 8590: my $blocked = &blocking_status('printout',$cnum,$cdom,undef,1);
1.1064 raeburn 8591: if ($blocked) {
8592: my $checkrole = "cm./$cdom/$cnum";
8593: if ($env{'request.course.sec'} ne '') {
8594: $checkrole .= "/$env{'request.course.sec'}";
8595: }
8596: unless ((&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) &&
8597: ($env{'request.role'} !~ m{^st\./$cdom/$cnum})) {
8598: $noprint = 1;
8599: }
8600: }
8601: unless ($noprint) {
8602: my $symb = &Apache::lonnet::symbread();
8603: if ($symb ne '') {
8604: my $navmap = Apache::lonnavmaps::navmap->new();
8605: if (ref($navmap)) {
8606: my $res = $navmap->getBySymb($symb);
8607: if (ref($res)) {
8608: if (!$res->resprintable()) {
8609: $noprint = 1;
8610: }
8611: }
8612: }
8613: }
8614: }
8615: if ($noprint) {
8616: return <<"ENDSTYLE";
8617: <style type="text/css" media="print">
8618: body { display:none }
8619: </style>
8620: ENDSTYLE
8621: }
8622: }
8623: return;
8624: }
8625:
8626: =pod
8627:
1.341 albertel 8628: =item * &xml_begin()
8629:
8630: Returns the needed doctype and <html>
8631:
8632: Inputs: none
8633:
8634: =cut
8635:
8636: sub xml_begin {
1.1168 raeburn 8637: my ($is_frameset) = @_;
1.341 albertel 8638: my $output='';
8639:
8640: if ($env{'browser.mathml'}) {
8641: $output='<?xml version="1.0"?>'
8642: #.'<?xml-stylesheet type="text/css" href="/adm/MathML/mathml.css"?>'."\n"
8643: # .'<!DOCTYPE html SYSTEM "/adm/MathML/mathml.dtd" '
8644:
8645: # .'<!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">] >'
8646: .'<!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">'
8647: .'<html xmlns:math="http://www.w3.org/1998/Math/MathML" '
8648: .'xmlns="http://www.w3.org/1999/xhtml">';
1.1168 raeburn 8649: } elsif ($is_frameset) {
8650: $output='<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">'."\n".
8651: '<html>'."\n";
1.341 albertel 8652: } else {
1.1168 raeburn 8653: $output='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'."\n".
8654: '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">'."\n";
1.341 albertel 8655: }
8656: return $output;
8657: }
1.340 albertel 8658:
8659: =pod
8660:
1.306 albertel 8661: =item * &start_page()
8662:
8663: Returns a complete <html> .. <body> section for LON-CAPA web pages.
8664:
1.648 raeburn 8665: Inputs:
8666:
8667: =over 4
8668:
8669: $title - optional title for the page
8670:
8671: $head_extra - optional extra HTML to incude inside the <head>
8672:
8673: $args - additional optional args supported are:
8674:
8675: =over 8
8676:
8677: only_body -> is true will set &bodytag() onlybodytag
1.317 albertel 8678: arg on
1.814 bisitz 8679: no_nav_bar -> is true will set &bodytag() no_nav_bar arg on
1.648 raeburn 8680: add_entries -> additional attributes to add to the <body>
8681: domain -> force to color decorate a page for a
1.317 albertel 8682: specific domain
1.648 raeburn 8683: function -> force usage of a specific rolish color
1.317 albertel 8684: scheme
1.648 raeburn 8685: redirect -> see &headtag()
8686: bgcolor -> override the default page bg color
8687: js_ready -> return a string ready for being used in
1.317 albertel 8688: a javascript writeln
1.648 raeburn 8689: html_encode -> return a string ready for being used in
1.320 albertel 8690: a html attribute
1.648 raeburn 8691: force_register -> if is true will turn on the &bodytag()
1.317 albertel 8692: $forcereg arg
1.648 raeburn 8693: frameset -> if true will start with a <frameset>
1.330 albertel 8694: rather than <body>
1.648 raeburn 8695: skip_phases -> hash ref of
1.338 albertel 8696: head -> skip the <html><head> generation
8697: body -> skip all <body> generation
1.648 raeburn 8698: no_auto_mt_title -> prevent &mt()ing the title arg
1.867 kalberla 8699: bread_crumbs -> Array containing breadcrumbs
1.983 raeburn 8700: bread_crumbs_component -> if exists show it as headline else show only the breadcrumbs
1.1272 raeburn 8701: bread_crumbs_nomenu -> if true will pass false as the value of $menulink
8702: to lonhtmlcommon::breadcrumbs
1.1096 raeburn 8703: group -> includes the current group, if page is for a
1.1274 raeburn 8704: specific group
8705: use_absolute -> for request for external resource or syllabus, this
8706: will contain https://<hostname> if server uses
8707: https (as per hosts.tab), but request is for http
8708: hostname -> hostname, originally from $r->hostname(), (optional).
1.361 albertel 8709:
1.648 raeburn 8710: =back
1.460 albertel 8711:
1.648 raeburn 8712: =back
1.562 albertel 8713:
1.306 albertel 8714: =cut
8715:
8716: sub start_page {
1.309 albertel 8717: my ($title,$head_extra,$args) = @_;
1.318 albertel 8718: #&Apache::lonnet::logthis("start_page ".join(':',caller(0)));
1.319 albertel 8719:
1.315 albertel 8720: $env{'internal.start_page'}++;
1.1318 raeburn 8721: my ($result,@advtools,$ltiscope,$ltiuri,%ltimenu);
1.964 droeschl 8722:
1.338 albertel 8723: if (! exists($args->{'skip_phases'}{'head'}) ) {
1.1168 raeburn 8724: $result .= &xml_begin($args->{'frameset'}) . &headtag($title, $head_extra, $args);
1.338 albertel 8725: }
1.1316 raeburn 8726:
8727: if (($env{'request.course.id'}) && ($env{'request.lti.login'})) {
1.1318 raeburn 8728: if ($env{'course.'.$env{'request.course.id'}.'.lti.override'}) {
8729: unless ($env{'course.'.$env{'request.course.id'}.'.lti.topmenu'}) {
8730: $args->{'no_primary_menu'} = 1;
8731: }
8732: unless ($env{'course.'.$env{'request.course.id'}.'.lti.inlinemenu'}) {
8733: $args->{'no_inline_menu'} = 1;
8734: }
8735: if ($env{'course.'.$env{'request.course.id'}.'.lti.lcmenu'}) {
8736: map { $ltimenu{$_} = 1; } split(/,/,$env{'course.'.$env{'request.course.id'}.'.lti.lcmenu'});
8737: }
8738: } else {
8739: my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
8740: my %lti = &Apache::lonnet::get_domain_lti($cdom,'provider');
8741: if (ref($lti{$env{'request.lti.login'}}) eq 'HASH') {
8742: unless ($lti{$env{'request.lti.login'}}{'topmenu'}) {
8743: $args->{'no_primary_menu'} = 1;
8744: }
8745: unless ($lti{$env{'request.lti.login'}}{'inlinemenu'}) {
8746: $args->{'no_inline_menu'} = 1;
8747: }
8748: if (ref($lti{$env{'request.lti.login'}}{'lcmenu'}) eq 'ARRAY') {
8749: map { $ltimenu{$_} = 1; } @{$lti{$env{'request.lti.login'}}{'lcmenu'}};
8750: }
8751: }
8752: }
1.1316 raeburn 8753: ($ltiscope,$ltiuri) = &LONCAPA::ltiutils::lti_provider_scope($env{'request.lti.uri'},
8754: $env{'course.'.$env{'request.course.id'}.'.domain'},
8755: $env{'course.'.$env{'request.course.id'}.'.num'});
8756: }
1.338 albertel 8757:
8758: if (! exists($args->{'skip_phases'}{'body'}) ) {
8759: if ($args->{'frameset'}) {
8760: my $attr_string = &make_attr_string($args->{'force_register'},
8761: $args->{'add_entries'});
8762: $result .= "\n<frameset $attr_string>\n";
1.831 bisitz 8763: } else {
8764: $result .=
8765: &bodytag($title,
8766: $args->{'function'}, $args->{'add_entries'},
8767: $args->{'only_body'}, $args->{'domain'},
8768: $args->{'force_register'}, $args->{'no_nav_bar'},
1.1096 raeburn 8769: $args->{'bgcolor'}, $args,
1.1318 raeburn 8770: \@advtools,$ltiscope,$ltiuri,\%ltimenu);
1.831 bisitz 8771: }
1.330 albertel 8772: }
1.338 albertel 8773:
1.315 albertel 8774: if ($args->{'js_ready'}) {
1.713 kaisler 8775: $result = &js_ready($result);
1.315 albertel 8776: }
1.320 albertel 8777: if ($args->{'html_encode'}) {
1.713 kaisler 8778: $result = &html_encode($result);
8779: }
8780:
1.813 bisitz 8781: # Preparation for new and consistent functionlist at top of screen
8782: # if ($args->{'functionlist'}) {
8783: # $result .= &build_functionlist();
8784: #}
8785:
1.964 droeschl 8786: # Don't add anything more if only_body wanted or in const space
8787: return $result if $args->{'only_body'}
8788: || $env{'request.state'} eq 'construct';
1.813 bisitz 8789:
8790: #Breadcrumbs
1.758 kaisler 8791: if (exists($args->{'bread_crumbs'}) or exists($args->{'bread_crumbs_component'})) {
8792: &Apache::lonhtmlcommon::clear_breadcrumbs();
8793: #if any br links exists, add them to the breadcrumbs
8794: if (exists($args->{'bread_crumbs'}) and ref($args->{'bread_crumbs'}) eq 'ARRAY') {
8795: foreach my $crumb (@{$args->{'bread_crumbs'}}){
8796: &Apache::lonhtmlcommon::add_breadcrumb($crumb);
8797: }
8798: }
1.1096 raeburn 8799: # if @advtools array contains items add then to the breadcrumbs
8800: if (@advtools > 0) {
8801: &Apache::lonmenu::advtools_crumbs(@advtools);
8802: }
1.1272 raeburn 8803: my $menulink;
8804: # if arg: bread_crumbs_nomenu is true pass 0 as $menulink item.
8805: if ((exists($args->{'bread_crumbs_nomenu'})) ||
1.1312 raeburn 8806: ($ltiscope eq 'map') || ($ltiscope eq 'resource') ||
1.1272 raeburn 8807: ((($args->{'crstype'} eq 'Placement') || (($env{'request.course.id'}) &&
8808: ($env{'course.'.$env{'request.course.id'}.'.type'} eq 'Placement'))) &&
8809: (!$env{'request.role.adv'}))) {
8810: $menulink = 0;
8811: } else {
8812: undef($menulink);
8813: }
1.758 kaisler 8814: #if bread_crumbs_component exists show it as headline else show only the breadcrumbs
8815: if(exists($args->{'bread_crumbs_component'})){
1.1272 raeburn 8816: $result .= &Apache::lonhtmlcommon::breadcrumbs($args->{'bread_crumbs_component'},'',$menulink);
1.1237 raeburn 8817: } else {
1.1272 raeburn 8818: $result .= &Apache::lonhtmlcommon::breadcrumbs('','',$menulink);
1.758 kaisler 8819: }
1.320 albertel 8820: }
1.315 albertel 8821: return $result;
1.306 albertel 8822: }
8823:
8824: sub end_page {
1.315 albertel 8825: my ($args) = @_;
8826: $env{'internal.end_page'}++;
1.330 albertel 8827: my $result;
1.335 albertel 8828: if ($args->{'discussion'}) {
8829: my ($target,$parser);
8830: if (ref($args->{'discussion'})) {
8831: ($target,$parser) =($args->{'discussion'}{'target'},
8832: $args->{'discussion'}{'parser'});
8833: }
8834: $result .= &Apache::lonxml::xmlend($target,$parser);
8835: }
1.330 albertel 8836: if ($args->{'frameset'}) {
8837: $result .= '</frameset>';
8838: } else {
1.635 raeburn 8839: $result .= &endbodytag($args);
1.330 albertel 8840: }
1.1080 raeburn 8841: unless ($args->{'notbody'}) {
8842: $result .= "\n</html>";
8843: }
1.330 albertel 8844:
1.315 albertel 8845: if ($args->{'js_ready'}) {
1.317 albertel 8846: $result = &js_ready($result);
1.315 albertel 8847: }
1.335 albertel 8848:
1.320 albertel 8849: if ($args->{'html_encode'}) {
8850: $result = &html_encode($result);
8851: }
1.335 albertel 8852:
1.315 albertel 8853: return $result;
8854: }
8855:
1.1034 www 8856: sub wishlist_window {
8857: return(<<'ENDWISHLIST');
1.1046 raeburn 8858: <script type="text/javascript">
1.1034 www 8859: // <![CDATA[
8860: // <!-- BEGIN LON-CAPA Internal
8861: function set_wishlistlink(title, path) {
8862: if (!title) {
8863: title = document.title;
8864: title = title.replace(/^LON-CAPA /,'');
8865: }
1.1175 raeburn 8866: title = encodeURIComponent(title);
1.1203 raeburn 8867: title = title.replace("'","\\\'");
1.1034 www 8868: if (!path) {
8869: path = location.pathname;
8870: }
1.1175 raeburn 8871: path = encodeURIComponent(path);
1.1203 raeburn 8872: path = path.replace("'","\\\'");
1.1034 www 8873: Win = window.open('/adm/wishlist?mode=newLink&setTitle='+title+'&setPath='+path,
8874: 'wishlistNewLink','width=560,height=350,scrollbars=0');
8875: }
8876: // END LON-CAPA Internal -->
8877: // ]]>
8878: </script>
8879: ENDWISHLIST
8880: }
8881:
1.1030 www 8882: sub modal_window {
8883: return(<<'ENDMODAL');
1.1046 raeburn 8884: <script type="text/javascript">
1.1030 www 8885: // <![CDATA[
8886: // <!-- BEGIN LON-CAPA Internal
8887: var modalWindow = {
8888: parent:"body",
8889: windowId:null,
8890: content:null,
8891: width:null,
8892: height:null,
8893: close:function()
8894: {
8895: $(".LCmodal-window").remove();
8896: $(".LCmodal-overlay").remove();
8897: },
8898: open:function()
8899: {
8900: var modal = "";
8901: modal += "<div class=\"LCmodal-overlay\"></div>";
8902: 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;\">";
8903: modal += this.content;
8904: modal += "</div>";
8905:
8906: $(this.parent).append(modal);
8907:
8908: $(".LCmodal-window").append("<a class=\"LCclose-window\"></a>");
8909: $(".LCclose-window").click(function(){modalWindow.close();});
8910: $(".LCmodal-overlay").click(function(){modalWindow.close();});
8911: }
8912: };
1.1140 raeburn 8913: var openMyModal = function(source,width,height,scrolling,transparency,style)
1.1030 www 8914: {
1.1266 raeburn 8915: source = source.replace(/'/g,"'");
1.1030 www 8916: modalWindow.windowId = "myModal";
8917: modalWindow.width = width;
8918: modalWindow.height = height;
1.1196 raeburn 8919: modalWindow.content = "<iframe width='"+width+"' height='"+height+"' frameborder='0' scrolling='"+scrolling+"' allowtransparency='"+transparency+"' src='" + source + "' style='"+style+"'></iframe>";
1.1030 www 8920: modalWindow.open();
1.1208 raeburn 8921: };
1.1030 www 8922: // END LON-CAPA Internal -->
8923: // ]]>
8924: </script>
8925: ENDMODAL
8926: }
8927:
8928: sub modal_link {
1.1140 raeburn 8929: my ($link,$linktext,$width,$height,$target,$scrolling,$title,$transparency,$style)=@_;
1.1030 www 8930: unless ($width) { $width=480; }
8931: unless ($height) { $height=400; }
1.1031 www 8932: unless ($scrolling) { $scrolling='yes'; }
1.1140 raeburn 8933: unless ($transparency) { $transparency='true'; }
8934:
1.1074 raeburn 8935: my $target_attr;
8936: if (defined($target)) {
8937: $target_attr = 'target="'.$target.'"';
8938: }
8939: return <<"ENDLINK";
1.1140 raeburn 8940: <a href="$link" $target_attr title="$title" onclick="javascript:openMyModal('$link',$width,$height,'$scrolling','$transparency','$style'); return false;">
1.1074 raeburn 8941: $linktext</a>
8942: ENDLINK
1.1030 www 8943: }
8944:
1.1032 www 8945: sub modal_adhoc_script {
8946: my ($funcname,$width,$height,$content)=@_;
8947: return (<<ENDADHOC);
1.1046 raeburn 8948: <script type="text/javascript">
1.1032 www 8949: // <![CDATA[
8950: var $funcname = function()
8951: {
8952: modalWindow.windowId = "myModal";
8953: modalWindow.width = $width;
8954: modalWindow.height = $height;
8955: modalWindow.content = '$content';
8956: modalWindow.open();
8957: };
8958: // ]]>
8959: </script>
8960: ENDADHOC
8961: }
8962:
1.1041 www 8963: sub modal_adhoc_inner {
8964: my ($funcname,$width,$height,$content)=@_;
8965: my $innerwidth=$width-20;
8966: $content=&js_ready(
1.1140 raeburn 8967: &start_page('Dialog',undef,{'only_body'=>1,'bgcolor'=>'#FFFFFF'}).
8968: &start_scrollbox($width.'px',$innerwidth.'px',$height.'px','myModal','#FFFFFF',undef,1).
8969: $content.
1.1041 www 8970: &end_scrollbox().
1.1140 raeburn 8971: &end_page()
1.1041 www 8972: );
8973: return &modal_adhoc_script($funcname,$width,$height,$content);
8974: }
8975:
8976: sub modal_adhoc_window {
8977: my ($funcname,$width,$height,$content,$linktext)=@_;
8978: return &modal_adhoc_inner($funcname,$width,$height,$content).
8979: "<a href=\"javascript:$funcname();void(0);\">".$linktext."</a>";
8980: }
8981:
8982: sub modal_adhoc_launch {
8983: my ($funcname,$width,$height,$content)=@_;
8984: return &modal_adhoc_inner($funcname,$width,$height,$content).(<<ENDLAUNCH);
8985: <script type="text/javascript">
8986: // <![CDATA[
8987: $funcname();
8988: // ]]>
8989: </script>
8990: ENDLAUNCH
8991: }
8992:
8993: sub modal_adhoc_close {
8994: return (<<ENDCLOSE);
8995: <script type="text/javascript">
8996: // <![CDATA[
8997: modalWindow.close();
8998: // ]]>
8999: </script>
9000: ENDCLOSE
9001: }
9002:
1.1038 www 9003: sub togglebox_script {
9004: return(<<ENDTOGGLE);
9005: <script type="text/javascript">
9006: // <![CDATA[
9007: function LCtoggleDisplay(id,hidetext,showtext) {
9008: link = document.getElementById(id + "link").childNodes[0];
9009: with (document.getElementById(id).style) {
9010: if (display == "none" ) {
9011: display = "inline";
9012: link.nodeValue = hidetext;
9013: } else {
9014: display = "none";
9015: link.nodeValue = showtext;
9016: }
9017: }
9018: }
9019: // ]]>
9020: </script>
9021: ENDTOGGLE
9022: }
9023:
1.1039 www 9024: sub start_togglebox {
9025: my ($id,$heading,$headerbg,$hidetext,$showtext)=@_;
9026: unless ($heading) { $heading=''; } else { $heading.=' '; }
9027: unless ($showtext) { $showtext=&mt('show'); }
9028: unless ($hidetext) { $hidetext=&mt('hide'); }
9029: unless ($headerbg) { $headerbg='#FFFFFF'; }
9030: return &start_data_table().
9031: &start_data_table_header_row().
9032: '<td bgcolor="'.$headerbg.'">'.$heading.
9033: '[<a id="'.$id.'link" href="javascript:LCtoggleDisplay(\''.$id.'\',\''.$hidetext.'\',\''.
9034: $showtext.'\')">'.$showtext.'</a>]</td>'.
9035: &end_data_table_header_row().
9036: '<tr id="'.$id.'" style="display:none""><td>';
9037: }
9038:
9039: sub end_togglebox {
9040: return '</td></tr>'.&end_data_table();
9041: }
9042:
1.1041 www 9043: sub LCprogressbar_script {
1.1302 raeburn 9044: my ($id,$number_to_do)=@_;
9045: if ($number_to_do) {
9046: return(<<ENDPROGRESS);
1.1041 www 9047: <script type="text/javascript">
9048: // <![CDATA[
1.1045 www 9049: \$('#progressbar$id').progressbar({
1.1041 www 9050: value: 0,
9051: change: function(event, ui) {
9052: var newVal = \$(this).progressbar('option', 'value');
9053: \$('.pblabel', this).text(LCprogressTxt);
9054: }
9055: });
9056: // ]]>
9057: </script>
9058: ENDPROGRESS
1.1302 raeburn 9059: } else {
9060: return(<<ENDPROGRESS);
9061: <script type="text/javascript">
9062: // <![CDATA[
9063: \$('#progressbar$id').progressbar({
9064: value: false,
9065: create: function(event, ui) {
9066: \$('.ui-widget-header', this).css({'background':'#F0F0F0'});
9067: \$('.ui-progressbar-overlay', this).css({'margin':'0'});
9068: }
9069: });
9070: // ]]>
9071: </script>
9072: ENDPROGRESS
9073: }
1.1041 www 9074: }
9075:
9076: sub LCprogressbarUpdate_script {
9077: return(<<ENDPROGRESSUPDATE);
9078: <style type="text/css">
9079: .ui-progressbar { position:relative; }
1.1302 raeburn 9080: .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 9081: .pblabel { position: absolute; width: 100%; text-align: center; line-height: 1.9em; }
9082: </style>
9083: <script type="text/javascript">
9084: // <![CDATA[
1.1045 www 9085: var LCprogressTxt='---';
9086:
1.1302 raeburn 9087: function LCupdateProgress(percent,progresstext,id,maxnum) {
1.1041 www 9088: LCprogressTxt=progresstext;
1.1302 raeburn 9089: if ((maxnum == '') || (maxnum == undefined) || (maxnum == null)) {
9090: \$('#progressbar'+id).find('.progress-label').text(LCprogressTxt);
9091: } else if (percent === \$('#progressbar'+id).progressbar( "value" )) {
1.1301 raeburn 9092: \$('#progressbar'+id).find('.pblabel').text(LCprogressTxt);
9093: } else {
9094: \$('#progressbar'+id).progressbar('value',percent);
9095: }
1.1041 www 9096: }
9097: // ]]>
9098: </script>
9099: ENDPROGRESSUPDATE
9100: }
9101:
1.1042 www 9102: my $LClastpercent;
1.1045 www 9103: my $LCidcnt;
9104: my $LCcurrentid;
1.1042 www 9105:
1.1041 www 9106: sub LCprogressbar {
1.1302 raeburn 9107: my ($r,$number_to_do,$preamble)=@_;
1.1042 www 9108: $LClastpercent=0;
1.1045 www 9109: $LCidcnt++;
9110: $LCcurrentid=$$.'_'.$LCidcnt;
1.1302 raeburn 9111: my ($starting,$content);
9112: if ($number_to_do) {
9113: $starting=&mt('Starting');
9114: $content=(<<ENDPROGBAR);
9115: $preamble
1.1045 www 9116: <div id="progressbar$LCcurrentid">
1.1041 www 9117: <span class="pblabel">$starting</span>
9118: </div>
9119: ENDPROGBAR
1.1302 raeburn 9120: } else {
9121: $starting=&mt('Loading...');
9122: $LClastpercent='false';
9123: $content=(<<ENDPROGBAR);
9124: $preamble
9125: <div id="progressbar$LCcurrentid">
9126: <div class="progress-label">$starting</div>
9127: </div>
9128: ENDPROGBAR
9129: }
9130: &r_print($r,$content.&LCprogressbar_script($LCcurrentid,$number_to_do));
1.1041 www 9131: }
9132:
9133: sub LCprogressbarUpdate {
1.1302 raeburn 9134: my ($r,$val,$text,$number_to_do)=@_;
9135: if ($number_to_do) {
9136: unless ($val) {
9137: if ($LClastpercent) {
9138: $val=$LClastpercent;
9139: } else {
9140: $val=0;
9141: }
9142: }
9143: if ($val<0) { $val=0; }
9144: if ($val>100) { $val=0; }
9145: $LClastpercent=$val;
9146: unless ($text) { $text=$val.'%'; }
9147: } else {
9148: $val = 'false';
1.1042 www 9149: }
1.1041 www 9150: $text=&js_ready($text);
1.1044 www 9151: &r_print($r,<<ENDUPDATE);
1.1041 www 9152: <script type="text/javascript">
9153: // <![CDATA[
1.1302 raeburn 9154: LCupdateProgress($val,'$text','$LCcurrentid','$number_to_do');
1.1041 www 9155: // ]]>
9156: </script>
9157: ENDUPDATE
1.1035 www 9158: }
9159:
1.1042 www 9160: sub LCprogressbarClose {
9161: my ($r)=@_;
9162: $LClastpercent=0;
1.1044 www 9163: &r_print($r,<<ENDCLOSE);
1.1042 www 9164: <script type="text/javascript">
9165: // <![CDATA[
1.1045 www 9166: \$("#progressbar$LCcurrentid").hide('slow');
1.1042 www 9167: // ]]>
9168: </script>
9169: ENDCLOSE
1.1044 www 9170: }
9171:
9172: sub r_print {
9173: my ($r,$to_print)=@_;
9174: if ($r) {
9175: $r->print($to_print);
9176: $r->rflush();
9177: } else {
9178: print($to_print);
9179: }
1.1042 www 9180: }
9181:
1.320 albertel 9182: sub html_encode {
9183: my ($result) = @_;
9184:
1.322 albertel 9185: $result = &HTML::Entities::encode($result,'<>&"');
1.320 albertel 9186:
9187: return $result;
9188: }
1.1044 www 9189:
1.317 albertel 9190: sub js_ready {
9191: my ($result) = @_;
9192:
1.323 albertel 9193: $result =~ s/[\n\r]/ /xmsg;
9194: $result =~ s/\\/\\\\/xmsg;
9195: $result =~ s/'/\\'/xmsg;
1.372 albertel 9196: $result =~ s{</}{<\\/}xmsg;
1.317 albertel 9197:
9198: return $result;
9199: }
9200:
1.315 albertel 9201: sub validate_page {
9202: if ( exists($env{'internal.start_page'})
1.316 albertel 9203: && $env{'internal.start_page'} > 1) {
9204: &Apache::lonnet::logthis('start_page called multiple times '.
1.318 albertel 9205: $env{'internal.start_page'}.' '.
1.316 albertel 9206: $ENV{'request.filename'});
1.315 albertel 9207: }
9208: if ( exists($env{'internal.end_page'})
1.316 albertel 9209: && $env{'internal.end_page'} > 1) {
9210: &Apache::lonnet::logthis('end_page called multiple times '.
1.318 albertel 9211: $env{'internal.end_page'}.' '.
1.316 albertel 9212: $env{'request.filename'});
1.315 albertel 9213: }
9214: if ( exists($env{'internal.start_page'})
9215: && ! exists($env{'internal.end_page'})) {
1.316 albertel 9216: &Apache::lonnet::logthis('start_page called without end_page '.
9217: $env{'request.filename'});
1.315 albertel 9218: }
9219: if ( ! exists($env{'internal.start_page'})
9220: && exists($env{'internal.end_page'})) {
1.316 albertel 9221: &Apache::lonnet::logthis('end_page called without start_page'.
9222: $env{'request.filename'});
1.315 albertel 9223: }
1.306 albertel 9224: }
1.315 albertel 9225:
1.996 www 9226:
9227: sub start_scrollbox {
1.1140 raeburn 9228: my ($outerwidth,$width,$height,$id,$bgcolor,$cursor,$needjsready) = @_;
1.998 raeburn 9229: unless ($outerwidth) { $outerwidth='520px'; }
9230: unless ($width) { $width='500px'; }
9231: unless ($height) { $height='200px'; }
1.1075 raeburn 9232: my ($table_id,$div_id,$tdcol);
1.1018 raeburn 9233: if ($id ne '') {
1.1140 raeburn 9234: $table_id = ' id="table_'.$id.'"';
1.1137 raeburn 9235: $div_id = ' id="div_'.$id.'"';
1.1018 raeburn 9236: }
1.1075 raeburn 9237: if ($bgcolor ne '') {
9238: $tdcol = "background-color: $bgcolor;";
9239: }
1.1137 raeburn 9240: my $nicescroll_js;
9241: if ($env{'browser.mobile'}) {
1.1140 raeburn 9242: $nicescroll_js = &nicescroll_javascript('div_'.$id,$cursor,$needjsready);
9243: }
9244: return <<"END";
9245: $nicescroll_js
9246:
9247: <table style="width: $outerwidth; border: 1px solid none;"$table_id><tr><td style="width: $width;$tdcol">
9248: <div style="overflow:auto; width:$width; height:$height;"$div_id>
9249: END
9250: }
9251:
9252: sub end_scrollbox {
9253: return '</div></td></tr></table>';
9254: }
9255:
9256: sub nicescroll_javascript {
9257: my ($id,$cursor,$needjsready,$framecheck,$location) = @_;
9258: my %options;
9259: if (ref($cursor) eq 'HASH') {
9260: %options = %{$cursor};
9261: }
9262: unless ($options{'railalign'} =~ /^left|right$/) {
9263: $options{'railalign'} = 'left';
9264: }
9265: unless ($options{'cursorcolor'} =~ /^\#\w+$/) {
9266: my $function = &get_users_function();
9267: $options{'cursorcolor'} = &designparm($function.'.sidebg',$env{'request.role.domain'});
1.1138 raeburn 9268: unless ($options{'cursorcolor'} =~ /^\#\w+$/) {
1.1140 raeburn 9269: $options{'cursorcolor'} = '#00F';
1.1138 raeburn 9270: }
1.1140 raeburn 9271: }
9272: if ($options{'cursoropacity'} =~ /^[\d.]+$/) {
9273: unless ($options{'cursoropacity'} >= 0.0 && $options{'cursoropacity'} <=1.0) {
1.1138 raeburn 9274: $options{'cursoropacity'}='1.0';
9275: }
1.1140 raeburn 9276: } else {
9277: $options{'cursoropacity'}='1.0';
9278: }
9279: if ($options{'cursorfixedheight'} eq 'none') {
9280: delete($options{'cursorfixedheight'});
9281: } else {
9282: unless ($options{'cursorfixedheight'} =~ /^\d+$/) { $options{'cursorfixedheight'}='50'; }
9283: }
9284: unless ($options{'railoffset'} =~ /^{[\w\:\d\-,]+}$/) {
9285: delete($options{'railoffset'});
9286: }
9287: my @niceoptions;
9288: while (my($key,$value) = each(%options)) {
9289: if ($value =~ /^\{.+\}$/) {
9290: push(@niceoptions,$key.':'.$value);
1.1138 raeburn 9291: } else {
1.1140 raeburn 9292: push(@niceoptions,$key.':"'.$value.'"');
1.1138 raeburn 9293: }
1.1140 raeburn 9294: }
9295: my $nicescroll_js = '
1.1137 raeburn 9296: $(document).ready(
1.1140 raeburn 9297: function() {
9298: $("#'.$id.'").niceScroll({'.join(',',@niceoptions).'});
9299: }
1.1137 raeburn 9300: );
9301: ';
1.1140 raeburn 9302: if ($framecheck) {
9303: $nicescroll_js .= '
9304: function expand_div(caller) {
9305: if (top === self) {
9306: document.getElementById("'.$id.'").style.width = "auto";
9307: document.getElementById("'.$id.'").style.height = "auto";
9308: } else {
9309: try {
9310: if (parent.frames) {
9311: if (parent.frames.length > 1) {
9312: var framesrc = parent.frames[1].location.href;
9313: var currsrc = framesrc.replace(/\#.*$/,"");
9314: if ((caller == "search") || (currsrc == "'.$location.'")) {
9315: document.getElementById("'.$id.'").style.width = "auto";
9316: document.getElementById("'.$id.'").style.height = "auto";
9317: }
9318: }
9319: }
9320: } catch (e) {
9321: return;
9322: }
1.1137 raeburn 9323: }
1.1140 raeburn 9324: return;
1.996 www 9325: }
1.1140 raeburn 9326: ';
9327: }
9328: if ($needjsready) {
9329: $nicescroll_js = '
9330: <script type="text/javascript">'."\n".$nicescroll_js."\n</script>\n";
9331: } else {
9332: $nicescroll_js = &Apache::lonhtmlcommon::scripttag($nicescroll_js);
9333: }
9334: return $nicescroll_js;
1.996 www 9335: }
9336:
1.318 albertel 9337: sub simple_error_page {
1.1150 bisitz 9338: my ($r,$title,$msg,$args) = @_;
1.1304 raeburn 9339: my %displayargs;
1.1151 raeburn 9340: if (ref($args) eq 'HASH') {
9341: if (!$args->{'no_auto_mt_msg'}) { $msg = &mt($msg); }
1.1304 raeburn 9342: if ($args->{'only_body'}) {
9343: $displayargs{'only_body'} = 1;
9344: }
9345: if ($args->{'no_nav_bar'}) {
9346: $displayargs{'no_nav_bar'} = 1;
9347: }
1.1151 raeburn 9348: } else {
9349: $msg = &mt($msg);
9350: }
1.1150 bisitz 9351:
1.318 albertel 9352: my $page =
1.1304 raeburn 9353: &Apache::loncommon::start_page($title,'',\%displayargs).
1.1150 bisitz 9354: '<p class="LC_error">'.$msg.'</p>'.
1.318 albertel 9355: &Apache::loncommon::end_page();
9356: if (ref($r)) {
9357: $r->print($page);
1.327 albertel 9358: return;
1.318 albertel 9359: }
9360: return $page;
9361: }
1.347 albertel 9362:
9363: {
1.610 albertel 9364: my @row_count;
1.961 onken 9365:
9366: sub start_data_table_count {
9367: unshift(@row_count, 0);
9368: return;
9369: }
9370:
9371: sub end_data_table_count {
9372: shift(@row_count);
9373: return;
9374: }
9375:
1.347 albertel 9376: sub start_data_table {
1.1018 raeburn 9377: my ($add_class,$id) = @_;
1.422 albertel 9378: my $css_class = (join(' ','LC_data_table',$add_class));
1.1018 raeburn 9379: my $table_id;
9380: if (defined($id)) {
9381: $table_id = ' id="'.$id.'"';
9382: }
1.961 onken 9383: &start_data_table_count();
1.1018 raeburn 9384: return '<table class="'.$css_class.'"'.$table_id.'>'."\n";
1.347 albertel 9385: }
9386:
9387: sub end_data_table {
1.961 onken 9388: &end_data_table_count();
1.389 albertel 9389: return '</table>'."\n";;
1.347 albertel 9390: }
9391:
9392: sub start_data_table_row {
1.974 wenzelju 9393: my ($add_class, $id) = @_;
1.610 albertel 9394: $row_count[0]++;
9395: my $css_class = ($row_count[0] % 2)?'LC_odd_row':'LC_even_row';
1.900 bisitz 9396: $css_class = (join(' ',$css_class,$add_class)) unless ($add_class eq '');
1.974 wenzelju 9397: $id = (' id="'.$id.'"') unless ($id eq '');
9398: return '<tr class="'.$css_class.'"'.$id.'>'."\n";
1.347 albertel 9399: }
1.471 banghart 9400:
9401: sub continue_data_table_row {
1.974 wenzelju 9402: my ($add_class, $id) = @_;
1.610 albertel 9403: my $css_class = ($row_count[0] % 2)?'LC_odd_row':'LC_even_row';
1.974 wenzelju 9404: $css_class = (join(' ',$css_class,$add_class)) unless ($add_class eq '');
9405: $id = (' id="'.$id.'"') unless ($id eq '');
9406: return '<tr class="'.$css_class.'"'.$id.'>'."\n";
1.471 banghart 9407: }
1.347 albertel 9408:
9409: sub end_data_table_row {
1.389 albertel 9410: return '</tr>'."\n";;
1.347 albertel 9411: }
1.367 www 9412:
1.421 albertel 9413: sub start_data_table_empty_row {
1.707 bisitz 9414: # $row_count[0]++;
1.421 albertel 9415: return '<tr class="LC_empty_row" >'."\n";;
9416: }
9417:
9418: sub end_data_table_empty_row {
9419: return '</tr>'."\n";;
9420: }
9421:
1.367 www 9422: sub start_data_table_header_row {
1.389 albertel 9423: return '<tr class="LC_header_row">'."\n";;
1.367 www 9424: }
9425:
9426: sub end_data_table_header_row {
1.389 albertel 9427: return '</tr>'."\n";;
1.367 www 9428: }
1.890 droeschl 9429:
9430: sub data_table_caption {
9431: my $caption = shift;
9432: return "<caption class=\"LC_caption\">$caption</caption>";
9433: }
1.347 albertel 9434: }
9435:
1.548 albertel 9436: =pod
9437:
9438: =item * &inhibit_menu_check($arg)
9439:
9440: Checks for a inhibitmenu state and generates output to preserve it
9441:
9442: Inputs: $arg - can be any of
9443: - undef - in which case the return value is a string
9444: to add into arguments list of a uri
9445: - 'input' - in which case the return value is a HTML
9446: <form> <input> field of type hidden to
9447: preserve the value
9448: - a url - in which case the return value is the url with
9449: the neccesary cgi args added to preserve the
9450: inhibitmenu state
9451: - a ref to a url - no return value, but the string is
9452: updated to include the neccessary cgi
9453: args to preserve the inhibitmenu state
9454:
9455: =cut
9456:
9457: sub inhibit_menu_check {
9458: my ($arg) = @_;
9459: &get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['inhibitmenu']);
9460: if ($arg eq 'input') {
9461: if ($env{'form.inhibitmenu'}) {
9462: return '<input type="hidden" name="inhibitmenu" value="'.$env{'form.inhibitmenu'}.'" />';
9463: } else {
9464: return
9465: }
9466: }
9467: if ($env{'form.inhibitmenu'}) {
9468: if (ref($arg)) {
9469: $$arg .= '?inhibitmenu='.$env{'form.inhibitmenu'};
9470: } elsif ($arg eq '') {
9471: $arg .= 'inhibitmenu='.$env{'form.inhibitmenu'};
9472: } else {
9473: $arg .= '?inhibitmenu='.$env{'form.inhibitmenu'};
9474: }
9475: }
9476: if (!ref($arg)) {
9477: return $arg;
9478: }
9479: }
9480:
1.251 albertel 9481: ###############################################
1.182 matthew 9482:
9483: =pod
9484:
1.549 albertel 9485: =back
9486:
9487: =head1 User Information Routines
9488:
9489: =over 4
9490:
1.405 albertel 9491: =item * &get_users_function()
1.182 matthew 9492:
9493: Used by &bodytag to determine the current users primary role.
9494: Returns either 'student','coordinator','admin', or 'author'.
9495:
9496: =cut
9497:
9498: ###############################################
9499: sub get_users_function {
1.815 tempelho 9500: my $function = 'norole';
1.818 tempelho 9501: if ($env{'request.role'}=~/^(st)/) {
9502: $function='student';
9503: }
1.907 raeburn 9504: if ($env{'request.role'}=~/^(cc|co|in|ta|ep)/) {
1.182 matthew 9505: $function='coordinator';
9506: }
1.258 albertel 9507: if ($env{'request.role'}=~/^(su|dc|ad|li)/) {
1.182 matthew 9508: $function='admin';
9509: }
1.826 bisitz 9510: if (($env{'request.role'}=~/^(au|ca|aa)/) ||
1.1025 raeburn 9511: ($ENV{'REQUEST_URI'}=~ m{/^(/priv)})) {
1.182 matthew 9512: $function='author';
9513: }
9514: return $function;
1.54 www 9515: }
1.99 www 9516:
9517: ###############################################
9518:
1.233 raeburn 9519: =pod
9520:
1.821 raeburn 9521: =item * &show_course()
9522:
9523: Used by lonmenu.pm and lonroles.pm to determine whether to use the word
9524: 'Courses' or 'Roles' in inline navigation and on screen displaying user's roles.
9525:
9526: Inputs:
9527: None
9528:
9529: Outputs:
9530: Scalar: 1 if 'Course' to be used, 0 otherwise.
9531:
9532: =cut
9533:
9534: ###############################################
9535: sub show_course {
9536: my $course = !$env{'user.adv'};
9537: if (!$env{'user.adv'}) {
9538: foreach my $env (keys(%env)) {
9539: next if ($env !~ m/^user\.priv\./);
9540: if ($env !~ m/^user\.priv\.(?:st|cm)/) {
9541: $course = 0;
9542: last;
9543: }
9544: }
9545: }
9546: return $course;
9547: }
9548:
9549: ###############################################
9550:
9551: =pod
9552:
1.542 raeburn 9553: =item * &check_user_status()
1.274 raeburn 9554:
9555: Determines current status of supplied role for a
9556: specific user. Roles can be active, previous or future.
9557:
9558: Inputs:
9559: user's domain, user's username, course's domain,
1.375 raeburn 9560: course's number, optional section ID.
1.274 raeburn 9561:
9562: Outputs:
9563: role status: active, previous or future.
9564:
9565: =cut
9566:
9567: sub check_user_status {
1.412 raeburn 9568: my ($udom,$uname,$cdom,$crs,$role,$sec) = @_;
1.1073 raeburn 9569: my %userinfo = &Apache::lonnet::dump('roles',$udom,$uname);
1.1202 raeburn 9570: my @uroles = keys(%userinfo);
1.274 raeburn 9571: my $srchstr;
9572: my $active_chk = 'none';
1.412 raeburn 9573: my $now = time;
1.274 raeburn 9574: if (@uroles > 0) {
1.908 raeburn 9575: if (($role eq 'cc') || ($role eq 'co') || ($sec eq '') || (!defined($sec))) {
1.274 raeburn 9576: $srchstr = '/'.$cdom.'/'.$crs.'_'.$role;
9577: } else {
1.412 raeburn 9578: $srchstr = '/'.$cdom.'/'.$crs.'/'.$sec.'_'.$role;
9579: }
9580: if (grep/^\Q$srchstr\E$/,@uroles) {
1.274 raeburn 9581: my $role_end = 0;
9582: my $role_start = 0;
9583: $active_chk = 'active';
1.412 raeburn 9584: if ($userinfo{$srchstr} =~ m/^\Q$role\E_(\d+)/) {
9585: $role_end = $1;
9586: if ($userinfo{$srchstr} =~ m/^\Q$role\E_\Q$role_end\E_(\d+)$/) {
9587: $role_start = $1;
1.274 raeburn 9588: }
9589: }
9590: if ($role_start > 0) {
1.412 raeburn 9591: if ($now < $role_start) {
1.274 raeburn 9592: $active_chk = 'future';
9593: }
9594: }
9595: if ($role_end > 0) {
1.412 raeburn 9596: if ($now > $role_end) {
1.274 raeburn 9597: $active_chk = 'previous';
9598: }
9599: }
9600: }
9601: }
9602: return $active_chk;
9603: }
9604:
9605: ###############################################
9606:
9607: =pod
9608:
1.405 albertel 9609: =item * &get_sections()
1.233 raeburn 9610:
9611: Determines all the sections for a course including
9612: sections with students and sections containing other roles.
1.419 raeburn 9613: Incoming parameters:
9614:
9615: 1. domain
9616: 2. course number
9617: 3. reference to array containing roles for which sections should
9618: be gathered (optional).
9619: 4. reference to array containing status types for which sections
9620: should be gathered (optional).
9621:
9622: If the third argument is undefined, sections are gathered for any role.
9623: If the fourth argument is undefined, sections are gathered for any status.
9624: Permissible values are 'active' or 'future' or 'previous'.
1.233 raeburn 9625:
1.374 raeburn 9626: Returns section hash (keys are section IDs, values are
9627: number of users in each section), subject to the
1.419 raeburn 9628: optional roles filter, optional status filter
1.233 raeburn 9629:
9630: =cut
9631:
9632: ###############################################
9633: sub get_sections {
1.419 raeburn 9634: my ($cdom,$cnum,$possible_roles,$possible_status) = @_;
1.366 albertel 9635: if (!defined($cdom) || !defined($cnum)) {
9636: my $cid = $env{'request.course.id'};
9637:
9638: return if (!defined($cid));
9639:
9640: $cdom = $env{'course.'.$cid.'.domain'};
9641: $cnum = $env{'course.'.$cid.'.num'};
9642: }
9643:
9644: my %sectioncount;
1.419 raeburn 9645: my $now = time;
1.240 albertel 9646:
1.1118 raeburn 9647: my $check_students = 1;
9648: my $only_students = 0;
9649: if (ref($possible_roles) eq 'ARRAY') {
9650: if (grep(/^st$/,@{$possible_roles})) {
9651: if (@{$possible_roles} == 1) {
9652: $only_students = 1;
9653: }
9654: } else {
9655: $check_students = 0;
9656: }
9657: }
9658:
9659: if ($check_students) {
1.276 albertel 9660: my ($classlist) = &Apache::loncoursedata::get_classlist($cdom,$cnum);
1.240 albertel 9661: my $sec_index = &Apache::loncoursedata::CL_SECTION();
9662: my $status_index = &Apache::loncoursedata::CL_STATUS();
1.419 raeburn 9663: my $start_index = &Apache::loncoursedata::CL_START();
9664: my $end_index = &Apache::loncoursedata::CL_END();
9665: my $status;
1.366 albertel 9666: while (my ($student,$data) = each(%$classlist)) {
1.419 raeburn 9667: my ($section,$stu_status,$start,$end) = ($data->[$sec_index],
9668: $data->[$status_index],
9669: $data->[$start_index],
9670: $data->[$end_index]);
9671: if ($stu_status eq 'Active') {
9672: $status = 'active';
9673: } elsif ($end < $now) {
9674: $status = 'previous';
9675: } elsif ($start > $now) {
9676: $status = 'future';
9677: }
9678: if ($section ne '-1' && $section !~ /^\s*$/) {
9679: if ((!defined($possible_status)) || (($status ne '') &&
9680: (grep/^\Q$status\E$/,@{$possible_status}))) {
9681: $sectioncount{$section}++;
9682: }
1.240 albertel 9683: }
9684: }
9685: }
1.1118 raeburn 9686: if ($only_students) {
9687: return %sectioncount;
9688: }
1.240 albertel 9689: my %courseroles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
9690: foreach my $user (sort(keys(%courseroles))) {
9691: if ($user !~ /^(\w{2})/) { next; }
9692: my ($role) = ($user =~ /^(\w{2})/);
9693: if ($possible_roles && !(grep(/^$role$/,@$possible_roles))) { next; }
1.419 raeburn 9694: my ($section,$status);
1.240 albertel 9695: if ($role eq 'cr' &&
9696: $user =~ m-^$role/[^/]*/[^/]*/[^/]*:[^:]*:[^:]*:(\w+)-) {
9697: $section=$1;
9698: }
9699: if ($user =~ /^$role:[^:]*:[^:]*:(\w+)/) { $section=$1; }
9700: if (!defined($section) || $section eq '-1') { next; }
1.419 raeburn 9701: my ($end,$start) = ($courseroles{$user} =~ /^([^:]*):([^:]*)$/);
9702: if ($end == -1 && $start == -1) {
9703: next; #deleted role
9704: }
9705: if (!defined($possible_status)) {
9706: $sectioncount{$section}++;
9707: } else {
9708: if ((!$end || $end >= $now) && (!$start || $start <= $now)) {
9709: $status = 'active';
9710: } elsif ($end < $now) {
9711: $status = 'future';
9712: } elsif ($start > $now) {
9713: $status = 'previous';
9714: }
9715: if (($status ne '') && (grep/^\Q$status\E$/,@{$possible_status})) {
9716: $sectioncount{$section}++;
9717: }
9718: }
1.233 raeburn 9719: }
1.366 albertel 9720: return %sectioncount;
1.233 raeburn 9721: }
9722:
1.274 raeburn 9723: ###############################################
1.294 raeburn 9724:
9725: =pod
1.405 albertel 9726:
9727: =item * &get_course_users()
9728:
1.275 raeburn 9729: Retrieves usernames:domains for users in the specified course
9730: with specific role(s), and access status.
9731:
9732: Incoming parameters:
1.277 albertel 9733: 1. course domain
9734: 2. course number
9735: 3. access status: users must have - either active,
1.275 raeburn 9736: previous, future, or all.
1.277 albertel 9737: 4. reference to array of permissible roles
1.288 raeburn 9738: 5. reference to array of section restrictions (optional)
9739: 6. reference to results object (hash of hashes).
9740: 7. reference to optional userdata hash
1.609 raeburn 9741: 8. reference to optional statushash
1.630 raeburn 9742: 9. flag if privileged users (except those set to unhide in
9743: course settings) should be excluded
1.609 raeburn 9744: Keys of top level results hash are roles.
1.275 raeburn 9745: Keys of inner hashes are username:domain, with
9746: values set to access type.
1.288 raeburn 9747: Optional userdata hash returns an array with arguments in the
9748: same order as loncoursedata::get_classlist() for student data.
9749:
1.609 raeburn 9750: Optional statushash returns
9751:
1.288 raeburn 9752: Entries for end, start, section and status are blank because
9753: of the possibility of multiple values for non-student roles.
9754:
1.275 raeburn 9755: =cut
1.405 albertel 9756:
1.275 raeburn 9757: ###############################################
1.405 albertel 9758:
1.275 raeburn 9759: sub get_course_users {
1.630 raeburn 9760: my ($cdom,$cnum,$types,$roles,$sections,$users,$userdata,$statushash,$hidepriv) = @_;
1.288 raeburn 9761: my %idx = ();
1.419 raeburn 9762: my %seclists;
1.288 raeburn 9763:
9764: $idx{udom} = &Apache::loncoursedata::CL_SDOM();
9765: $idx{uname} = &Apache::loncoursedata::CL_SNAME();
9766: $idx{end} = &Apache::loncoursedata::CL_END();
9767: $idx{start} = &Apache::loncoursedata::CL_START();
9768: $idx{id} = &Apache::loncoursedata::CL_ID();
9769: $idx{section} = &Apache::loncoursedata::CL_SECTION();
9770: $idx{fullname} = &Apache::loncoursedata::CL_FULLNAME();
9771: $idx{status} = &Apache::loncoursedata::CL_STATUS();
9772:
1.290 albertel 9773: if (grep(/^st$/,@{$roles})) {
1.276 albertel 9774: my ($classlist,$keylist)=&Apache::loncoursedata::get_classlist($cdom,$cnum);
1.278 raeburn 9775: my $now = time;
1.277 albertel 9776: foreach my $student (keys(%{$classlist})) {
1.288 raeburn 9777: my $match = 0;
1.412 raeburn 9778: my $secmatch = 0;
1.419 raeburn 9779: my $section = $$classlist{$student}[$idx{section}];
1.609 raeburn 9780: my $status = $$classlist{$student}[$idx{status}];
1.419 raeburn 9781: if ($section eq '') {
9782: $section = 'none';
9783: }
1.291 albertel 9784: if ((ref($sections) eq 'ARRAY') && (@{$sections} > 0)) {
1.420 albertel 9785: if (grep(/^all$/,@{$sections})) {
1.412 raeburn 9786: $secmatch = 1;
9787: } elsif ($$classlist{$student}[$idx{section}] eq '') {
1.420 albertel 9788: if (grep(/^none$/,@{$sections})) {
1.412 raeburn 9789: $secmatch = 1;
9790: }
9791: } else {
1.419 raeburn 9792: if (grep(/^\Q$section\E$/,@{$sections})) {
1.412 raeburn 9793: $secmatch = 1;
9794: }
1.290 albertel 9795: }
1.412 raeburn 9796: if (!$secmatch) {
9797: next;
9798: }
1.419 raeburn 9799: }
1.275 raeburn 9800: if (defined($$types{'active'})) {
1.288 raeburn 9801: if ($$classlist{$student}[$idx{status}] eq 'Active') {
1.275 raeburn 9802: push(@{$$users{st}{$student}},'active');
1.288 raeburn 9803: $match = 1;
1.275 raeburn 9804: }
9805: }
9806: if (defined($$types{'previous'})) {
1.609 raeburn 9807: if ($$classlist{$student}[$idx{status}] eq 'Expired') {
1.275 raeburn 9808: push(@{$$users{st}{$student}},'previous');
1.288 raeburn 9809: $match = 1;
1.275 raeburn 9810: }
9811: }
9812: if (defined($$types{'future'})) {
1.609 raeburn 9813: if ($$classlist{$student}[$idx{status}] eq 'Future') {
1.275 raeburn 9814: push(@{$$users{st}{$student}},'future');
1.288 raeburn 9815: $match = 1;
1.275 raeburn 9816: }
9817: }
1.609 raeburn 9818: if ($match) {
9819: push(@{$seclists{$student}},$section);
9820: if (ref($userdata) eq 'HASH') {
9821: $$userdata{$student} = $$classlist{$student};
9822: }
9823: if (ref($statushash) eq 'HASH') {
9824: $statushash->{$student}{'st'}{$section} = $status;
9825: }
1.288 raeburn 9826: }
1.275 raeburn 9827: }
9828: }
1.412 raeburn 9829: if ((@{$roles} > 1) || ((@{$roles} == 1) && ($$roles[0] ne "st"))) {
1.439 raeburn 9830: my %coursepersonnel = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
9831: my $now = time;
1.609 raeburn 9832: my %displaystatus = ( previous => 'Expired',
9833: active => 'Active',
9834: future => 'Future',
9835: );
1.1121 raeburn 9836: my (%nothide,@possdoms);
1.630 raeburn 9837: if ($hidepriv) {
9838: my %coursehash=&Apache::lonnet::coursedescription($cdom.'_'.$cnum);
9839: foreach my $user (split(/\s*\,\s*/,$coursehash{'nothideprivileged'})) {
9840: if ($user !~ /:/) {
9841: $nothide{join(':',split(/[\@]/,$user))}=1;
9842: } else {
9843: $nothide{$user} = 1;
9844: }
9845: }
1.1121 raeburn 9846: my @possdoms = ($cdom);
9847: if ($coursehash{'checkforpriv'}) {
9848: push(@possdoms,split(/,/,$coursehash{'checkforpriv'}));
9849: }
1.630 raeburn 9850: }
1.439 raeburn 9851: foreach my $person (sort(keys(%coursepersonnel))) {
1.288 raeburn 9852: my $match = 0;
1.412 raeburn 9853: my $secmatch = 0;
1.439 raeburn 9854: my $status;
1.412 raeburn 9855: my ($role,$user,$usec) = ($person =~ /^([^:]*):([^:]+:[^:]+):([^:]*)/);
1.275 raeburn 9856: $user =~ s/:$//;
1.439 raeburn 9857: my ($end,$start) = split(/:/,$coursepersonnel{$person});
9858: if ($end == -1 || $start == -1) {
9859: next;
9860: }
9861: if (($role) && ((grep(/^\Q$role\E$/,@{$roles})) ||
9862: (grep(/^cr$/,@{$roles}) && $role =~ /^cr\//))) {
1.412 raeburn 9863: my ($uname,$udom) = split(/:/,$user);
9864: if ((ref($sections) eq 'ARRAY') && (@{$sections} > 0)) {
1.420 albertel 9865: if (grep(/^all$/,@{$sections})) {
1.412 raeburn 9866: $secmatch = 1;
9867: } elsif ($usec eq '') {
1.420 albertel 9868: if (grep(/^none$/,@{$sections})) {
1.412 raeburn 9869: $secmatch = 1;
9870: }
9871: } else {
9872: if (grep(/^\Q$usec\E$/,@{$sections})) {
9873: $secmatch = 1;
9874: }
9875: }
9876: if (!$secmatch) {
9877: next;
9878: }
1.288 raeburn 9879: }
1.419 raeburn 9880: if ($usec eq '') {
9881: $usec = 'none';
9882: }
1.275 raeburn 9883: if ($uname ne '' && $udom ne '') {
1.630 raeburn 9884: if ($hidepriv) {
1.1121 raeburn 9885: if ((&Apache::lonnet::privileged($uname,$udom,\@possdoms)) &&
1.630 raeburn 9886: (!$nothide{$uname.':'.$udom})) {
9887: next;
9888: }
9889: }
1.503 raeburn 9890: if ($end > 0 && $end < $now) {
1.439 raeburn 9891: $status = 'previous';
9892: } elsif ($start > $now) {
9893: $status = 'future';
9894: } else {
9895: $status = 'active';
9896: }
1.277 albertel 9897: foreach my $type (keys(%{$types})) {
1.275 raeburn 9898: if ($status eq $type) {
1.420 albertel 9899: if (!grep(/^\Q$type\E$/,@{$$users{$role}{$user}})) {
1.419 raeburn 9900: push(@{$$users{$role}{$user}},$type);
9901: }
1.288 raeburn 9902: $match = 1;
9903: }
9904: }
1.419 raeburn 9905: if (($match) && (ref($userdata) eq 'HASH')) {
9906: if (!exists($$userdata{$uname.':'.$udom})) {
9907: &get_user_info($udom,$uname,\%idx,$userdata);
9908: }
1.420 albertel 9909: if (!grep(/^\Q$usec\E$/,@{$seclists{$uname.':'.$udom}})) {
1.419 raeburn 9910: push(@{$seclists{$uname.':'.$udom}},$usec);
9911: }
1.609 raeburn 9912: if (ref($statushash) eq 'HASH') {
9913: $statushash->{$uname.':'.$udom}{$role}{$usec} = $displaystatus{$status};
9914: }
1.275 raeburn 9915: }
9916: }
9917: }
9918: }
1.290 albertel 9919: if (grep(/^ow$/,@{$roles})) {
1.279 raeburn 9920: if ((defined($cdom)) && (defined($cnum))) {
9921: my %csettings = &Apache::lonnet::get('environment',['internal.courseowner'],$cdom,$cnum);
9922: if ( defined($csettings{'internal.courseowner'}) ) {
9923: my $owner = $csettings{'internal.courseowner'};
1.609 raeburn 9924: next if ($owner eq '');
9925: my ($ownername,$ownerdom);
9926: if ($owner =~ /^([^:]+):([^:]+)$/) {
9927: $ownername = $1;
9928: $ownerdom = $2;
9929: } else {
9930: $ownername = $owner;
9931: $ownerdom = $cdom;
9932: $owner = $ownername.':'.$ownerdom;
1.439 raeburn 9933: }
9934: @{$$users{'ow'}{$owner}} = 'any';
1.290 albertel 9935: if (defined($userdata) &&
1.609 raeburn 9936: !exists($$userdata{$owner})) {
9937: &get_user_info($ownerdom,$ownername,\%idx,$userdata);
9938: if (!grep(/^none$/,@{$seclists{$owner}})) {
9939: push(@{$seclists{$owner}},'none');
9940: }
9941: if (ref($statushash) eq 'HASH') {
9942: $statushash->{$owner}{'ow'}{'none'} = 'Any';
1.419 raeburn 9943: }
1.290 albertel 9944: }
1.279 raeburn 9945: }
9946: }
9947: }
1.419 raeburn 9948: foreach my $user (keys(%seclists)) {
9949: @{$seclists{$user}} = (sort {$a <=> $b} @{$seclists{$user}});
9950: $$userdata{$user}[$idx{section}] = join(',',@{$seclists{$user}});
9951: }
1.275 raeburn 9952: }
9953: return;
9954: }
9955:
1.288 raeburn 9956: sub get_user_info {
9957: my ($udom,$uname,$idx,$userdata) = @_;
1.289 albertel 9958: $$userdata{$uname.':'.$udom}[$$idx{fullname}] =
9959: &plainname($uname,$udom,'lastname');
1.291 albertel 9960: $$userdata{$uname.':'.$udom}[$$idx{uname}] = $uname;
1.297 raeburn 9961: $$userdata{$uname.':'.$udom}[$$idx{udom}] = $udom;
1.609 raeburn 9962: my %idhash = &Apache::lonnet::idrget($udom,($uname));
9963: $$userdata{$uname.':'.$udom}[$$idx{id}] = $idhash{$uname};
1.288 raeburn 9964: return;
9965: }
1.275 raeburn 9966:
1.472 raeburn 9967: ###############################################
9968:
9969: =pod
9970:
9971: =item * &get_user_quota()
9972:
1.1134 raeburn 9973: Retrieves quota assigned for storage of user files.
9974: Default is to report quota for portfolio files.
1.472 raeburn 9975:
9976: Incoming parameters:
9977: 1. user's username
9978: 2. user's domain
1.1134 raeburn 9979: 3. quota name - portfolio, author, or course
1.1136 raeburn 9980: (if no quota name provided, defaults to portfolio).
1.1237 raeburn 9981: 4. crstype - official, unofficial, textbook, placement or community,
9982: if quota name is course
1.472 raeburn 9983:
9984: Returns:
1.1163 raeburn 9985: 1. Disk quota (in MB) assigned to student.
1.536 raeburn 9986: 2. (Optional) Type of setting: custom or default
9987: (individually assigned or default for user's
9988: institutional status).
9989: 3. (Optional) - User's institutional status (e.g., faculty, staff
9990: or student - types as defined in localenroll::inst_usertypes
9991: for user's domain, which determines default quota for user.
9992: 4. (Optional) - Default quota which would apply to the user.
1.472 raeburn 9993:
9994: If a value has been stored in the user's environment,
1.536 raeburn 9995: it will return that, otherwise it returns the maximal default
1.1134 raeburn 9996: defined for the user's institutional status(es) in the domain.
1.472 raeburn 9997:
9998: =cut
9999:
10000: ###############################################
10001:
10002:
10003: sub get_user_quota {
1.1136 raeburn 10004: my ($uname,$udom,$quotaname,$crstype) = @_;
1.536 raeburn 10005: my ($quota,$quotatype,$settingstatus,$defquota);
1.472 raeburn 10006: if (!defined($udom)) {
10007: $udom = $env{'user.domain'};
10008: }
10009: if (!defined($uname)) {
10010: $uname = $env{'user.name'};
10011: }
10012: if (($udom eq '' || $uname eq '') ||
10013: ($udom eq 'public') && ($uname eq 'public')) {
10014: $quota = 0;
1.536 raeburn 10015: $quotatype = 'default';
10016: $defquota = 0;
1.472 raeburn 10017: } else {
1.536 raeburn 10018: my $inststatus;
1.1134 raeburn 10019: if ($quotaname eq 'course') {
10020: if (($env{'course.'.$udom.'_'.$uname.'.num'} eq $uname) &&
10021: ($env{'course.'.$udom.'_'.$uname.'.domain'} eq $udom)) {
10022: $quota = $env{'course.'.$udom.'_'.$uname.'.internal.uploadquota'};
10023: } else {
10024: my %cenv = &Apache::lonnet::coursedescription("$udom/$uname");
10025: $quota = $cenv{'internal.uploadquota'};
10026: }
1.536 raeburn 10027: } else {
1.1134 raeburn 10028: if ($udom eq $env{'user.domain'} && $uname eq $env{'user.name'}) {
10029: if ($quotaname eq 'author') {
10030: $quota = $env{'environment.authorquota'};
10031: } else {
10032: $quota = $env{'environment.portfolioquota'};
10033: }
10034: $inststatus = $env{'environment.inststatus'};
10035: } else {
10036: my %userenv =
10037: &Apache::lonnet::get('environment',['portfolioquota',
10038: 'authorquota','inststatus'],$udom,$uname);
10039: my ($tmp) = keys(%userenv);
10040: if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
10041: if ($quotaname eq 'author') {
10042: $quota = $userenv{'authorquota'};
10043: } else {
10044: $quota = $userenv{'portfolioquota'};
10045: }
10046: $inststatus = $userenv{'inststatus'};
10047: } else {
10048: undef(%userenv);
10049: }
10050: }
10051: }
10052: if ($quota eq '' || wantarray) {
10053: if ($quotaname eq 'course') {
10054: my %domdefs = &Apache::lonnet::get_domain_defaults($udom);
1.1165 raeburn 10055: if (($crstype eq 'official') || ($crstype eq 'unofficial') ||
1.1237 raeburn 10056: ($crstype eq 'community') || ($crstype eq 'textbook') ||
10057: ($crstype eq 'placement')) {
1.1136 raeburn 10058: $defquota = $domdefs{$crstype.'quota'};
10059: }
10060: if ($defquota eq '') {
10061: $defquota = 500;
10062: }
1.1134 raeburn 10063: } else {
10064: ($defquota,$settingstatus) = &default_quota($udom,$inststatus,$quotaname);
10065: }
10066: if ($quota eq '') {
10067: $quota = $defquota;
10068: $quotatype = 'default';
10069: } else {
10070: $quotatype = 'custom';
10071: }
1.472 raeburn 10072: }
10073: }
1.536 raeburn 10074: if (wantarray) {
10075: return ($quota,$quotatype,$settingstatus,$defquota);
10076: } else {
10077: return $quota;
10078: }
1.472 raeburn 10079: }
10080:
10081: ###############################################
10082:
10083: =pod
10084:
10085: =item * &default_quota()
10086:
1.536 raeburn 10087: Retrieves default quota assigned for storage of user portfolio files,
10088: given an (optional) user's institutional status.
1.472 raeburn 10089:
10090: Incoming parameters:
1.1142 raeburn 10091:
1.472 raeburn 10092: 1. domain
1.536 raeburn 10093: 2. (Optional) institutional status(es). This is a : separated list of
10094: status types (e.g., faculty, staff, student etc.)
10095: which apply to the user for whom the default is being retrieved.
10096: If the institutional status string in undefined, the domain
1.1134 raeburn 10097: default quota will be returned.
10098: 3. quota name - portfolio, author, or course
10099: (if no quota name provided, defaults to portfolio).
1.472 raeburn 10100:
10101: Returns:
1.1142 raeburn 10102:
1.1163 raeburn 10103: 1. Default disk quota (in MB) for user portfolios in the domain.
1.536 raeburn 10104: 2. (Optional) institutional type which determined the value of the
10105: default quota.
1.472 raeburn 10106:
10107: If a value has been stored in the domain's configuration db,
10108: it will return that, otherwise it returns 20 (for backwards
10109: compatibility with domains which have not set up a configuration
1.1163 raeburn 10110: db file; the original statically defined portfolio quota was 20 MB).
1.472 raeburn 10111:
1.536 raeburn 10112: If the user's status includes multiple types (e.g., staff and student),
10113: the largest default quota which applies to the user determines the
10114: default quota returned.
10115:
1.472 raeburn 10116: =cut
10117:
10118: ###############################################
10119:
10120:
10121: sub default_quota {
1.1134 raeburn 10122: my ($udom,$inststatus,$quotaname) = @_;
1.536 raeburn 10123: my ($defquota,$settingstatus);
10124: my %quotahash = &Apache::lonnet::get_dom('configuration',
1.622 raeburn 10125: ['quotas'],$udom);
1.1134 raeburn 10126: my $key = 'defaultquota';
10127: if ($quotaname eq 'author') {
10128: $key = 'authorquota';
10129: }
1.622 raeburn 10130: if (ref($quotahash{'quotas'}) eq 'HASH') {
1.536 raeburn 10131: if ($inststatus ne '') {
1.765 raeburn 10132: my @statuses = map { &unescape($_); } split(/:/,$inststatus);
1.536 raeburn 10133: foreach my $item (@statuses) {
1.1134 raeburn 10134: if (ref($quotahash{'quotas'}{$key}) eq 'HASH') {
10135: if ($quotahash{'quotas'}{$key}{$item} ne '') {
1.711 raeburn 10136: if ($defquota eq '') {
1.1134 raeburn 10137: $defquota = $quotahash{'quotas'}{$key}{$item};
1.711 raeburn 10138: $settingstatus = $item;
1.1134 raeburn 10139: } elsif ($quotahash{'quotas'}{$key}{$item} > $defquota) {
10140: $defquota = $quotahash{'quotas'}{$key}{$item};
1.711 raeburn 10141: $settingstatus = $item;
10142: }
10143: }
1.1134 raeburn 10144: } elsif ($key eq 'defaultquota') {
1.711 raeburn 10145: if ($quotahash{'quotas'}{$item} ne '') {
10146: if ($defquota eq '') {
10147: $defquota = $quotahash{'quotas'}{$item};
10148: $settingstatus = $item;
10149: } elsif ($quotahash{'quotas'}{$item} > $defquota) {
10150: $defquota = $quotahash{'quotas'}{$item};
10151: $settingstatus = $item;
10152: }
1.536 raeburn 10153: }
10154: }
10155: }
10156: }
10157: if ($defquota eq '') {
1.1134 raeburn 10158: if (ref($quotahash{'quotas'}{$key}) eq 'HASH') {
10159: $defquota = $quotahash{'quotas'}{$key}{'default'};
10160: } elsif ($key eq 'defaultquota') {
1.711 raeburn 10161: $defquota = $quotahash{'quotas'}{'default'};
10162: }
1.536 raeburn 10163: $settingstatus = 'default';
1.1139 raeburn 10164: if ($defquota eq '') {
10165: if ($quotaname eq 'author') {
10166: $defquota = 500;
10167: }
10168: }
1.536 raeburn 10169: }
10170: } else {
10171: $settingstatus = 'default';
1.1134 raeburn 10172: if ($quotaname eq 'author') {
10173: $defquota = 500;
10174: } else {
10175: $defquota = 20;
10176: }
1.536 raeburn 10177: }
10178: if (wantarray) {
10179: return ($defquota,$settingstatus);
1.472 raeburn 10180: } else {
1.536 raeburn 10181: return $defquota;
1.472 raeburn 10182: }
10183: }
10184:
1.1135 raeburn 10185: ###############################################
10186:
10187: =pod
10188:
1.1136 raeburn 10189: =item * &excess_filesize_warning()
1.1135 raeburn 10190:
10191: Returns warning message if upload of file to authoring space, or copying
1.1136 raeburn 10192: of existing file within authoring space will cause quota for the authoring
1.1146 raeburn 10193: space to be exceeded.
1.1136 raeburn 10194:
10195: Same, if upload of a file directly to a course/community via Course Editor
1.1137 raeburn 10196: will cause quota for uploaded content for the course to be exceeded.
1.1135 raeburn 10197:
1.1165 raeburn 10198: Inputs: 7
1.1136 raeburn 10199: 1. username or coursenum
1.1135 raeburn 10200: 2. domain
1.1136 raeburn 10201: 3. context ('author' or 'course')
1.1135 raeburn 10202: 4. filename of file for which action is being requested
10203: 5. filesize (kB) of file
10204: 6. action being taken: copy or upload.
1.1237 raeburn 10205: 7. quotatype (in course context -- official, unofficial, textbook, placement or community).
1.1135 raeburn 10206:
10207: Returns: 1 scalar: HTML to display containing warning if quota would be exceeded,
1.1142 raeburn 10208: otherwise return null.
10209:
10210: =back
1.1135 raeburn 10211:
10212: =cut
10213:
1.1136 raeburn 10214: sub excess_filesize_warning {
1.1165 raeburn 10215: my ($uname,$udom,$context,$filename,$filesize,$action,$quotatype) = @_;
1.1136 raeburn 10216: my $current_disk_usage = 0;
1.1165 raeburn 10217: my $disk_quota = &get_user_quota($uname,$udom,$context,$quotatype); #expressed in MB
1.1136 raeburn 10218: if ($context eq 'author') {
10219: my $authorspace = $Apache::lonnet::perlvar{'lonDocRoot'}."/priv/$udom/$uname";
10220: $current_disk_usage = &Apache::lonnet::diskusage($udom,$uname,$authorspace);
10221: } else {
10222: foreach my $subdir ('docs','supplemental') {
10223: $current_disk_usage += &Apache::lonnet::diskusage($udom,$uname,"userfiles/$subdir",1);
10224: }
10225: }
1.1135 raeburn 10226: $disk_quota = int($disk_quota * 1000);
10227: if (($current_disk_usage + $filesize) > $disk_quota) {
1.1179 bisitz 10228: return '<p class="LC_warning">'.
1.1135 raeburn 10229: &mt("Unable to $action [_1]. (size = [_2] kilobytes). Disk quota will be exceeded.",
1.1179 bisitz 10230: '<span class="LC_filename">'.$filename.'</span>',$filesize).'</p>'.
10231: '<p>'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',
1.1135 raeburn 10232: $disk_quota,$current_disk_usage).
10233: '</p>';
10234: }
10235: return;
10236: }
10237:
10238: ###############################################
10239:
10240:
1.1136 raeburn 10241:
10242:
1.384 raeburn 10243: sub get_secgrprole_info {
10244: my ($cdom,$cnum,$needroles,$type) = @_;
10245: my %sections_count = &get_sections($cdom,$cnum);
10246: my @sections = (sort {$a <=> $b} keys(%sections_count));
10247: my %curr_groups = &Apache::longroup::coursegroups($cdom,$cnum);
10248: my @groups = sort(keys(%curr_groups));
10249: my $allroles = [];
10250: my $rolehash;
10251: my $accesshash = {
10252: active => 'Currently has access',
10253: future => 'Will have future access',
10254: previous => 'Previously had access',
10255: };
10256: if ($needroles) {
10257: $rolehash = {'all' => 'all'};
1.385 albertel 10258: my %user_roles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
10259: if (&Apache::lonnet::error(%user_roles)) {
10260: undef(%user_roles);
10261: }
10262: foreach my $item (keys(%user_roles)) {
1.384 raeburn 10263: my ($role)=split(/\:/,$item,2);
10264: if ($role eq 'cr') { next; }
10265: if ($role =~ /^cr/) {
10266: $$rolehash{$role} = (split('/',$role))[3];
10267: } else {
10268: $$rolehash{$role} = &Apache::lonnet::plaintext($role,$type);
10269: }
10270: }
10271: foreach my $key (sort(keys(%{$rolehash}))) {
10272: push(@{$allroles},$key);
10273: }
10274: push (@{$allroles},'st');
10275: $$rolehash{'st'} = &Apache::lonnet::plaintext('st',$type);
10276: }
10277: return (\@sections,\@groups,$allroles,$rolehash,$accesshash);
10278: }
10279:
1.555 raeburn 10280: sub user_picker {
1.1279 raeburn 10281: my ($dom,$srch,$forcenewuser,$caller,$cancreate,$usertype,$context,$fixeddom,$noinstd) = @_;
1.555 raeburn 10282: my $currdom = $dom;
1.1253 raeburn 10283: my @alldoms = &Apache::lonnet::all_domains();
10284: if (@alldoms == 1) {
10285: my %domsrch = &Apache::lonnet::get_dom('configuration',
10286: ['directorysrch'],$alldoms[0]);
10287: my $domdesc = &Apache::lonnet::domain($alldoms[0],'description');
10288: my $showdom = $domdesc;
10289: if ($showdom eq '') {
10290: $showdom = $dom;
10291: }
10292: if (ref($domsrch{'directorysrch'}) eq 'HASH') {
10293: if ((!$domsrch{'directorysrch'}{'available'}) &&
10294: ($domsrch{'directorysrch'}{'lcavailable'} eq '0')) {
10295: return (&mt('LON-CAPA directory search is not available in domain: [_1]',$showdom),0);
10296: }
10297: }
10298: }
1.555 raeburn 10299: my %curr_selected = (
10300: srchin => 'dom',
1.580 raeburn 10301: srchby => 'lastname',
1.555 raeburn 10302: );
10303: my $srchterm;
1.625 raeburn 10304: if ((ref($srch) eq 'HASH') && ($env{'form.origform'} ne 'crtusername')) {
1.555 raeburn 10305: if ($srch->{'srchby'} ne '') {
10306: $curr_selected{'srchby'} = $srch->{'srchby'};
10307: }
10308: if ($srch->{'srchin'} ne '') {
10309: $curr_selected{'srchin'} = $srch->{'srchin'};
10310: }
10311: if ($srch->{'srchtype'} ne '') {
10312: $curr_selected{'srchtype'} = $srch->{'srchtype'};
10313: }
10314: if ($srch->{'srchdomain'} ne '') {
10315: $currdom = $srch->{'srchdomain'};
10316: }
10317: $srchterm = $srch->{'srchterm'};
10318: }
1.1222 damieng 10319: my %html_lt=&Apache::lonlocal::texthash(
1.573 raeburn 10320: 'usr' => 'Search criteria',
1.563 raeburn 10321: 'doma' => 'Domain/institution to search',
1.558 albertel 10322: 'uname' => 'username',
10323: 'lastname' => 'last name',
1.555 raeburn 10324: 'lastfirst' => 'last name, first name',
1.558 albertel 10325: 'crs' => 'in this course',
1.576 raeburn 10326: 'dom' => 'in selected LON-CAPA domain',
1.558 albertel 10327: 'alc' => 'all LON-CAPA',
1.573 raeburn 10328: 'instd' => 'in institutional directory for selected domain',
1.558 albertel 10329: 'exact' => 'is',
10330: 'contains' => 'contains',
1.569 raeburn 10331: 'begins' => 'begins with',
1.1222 damieng 10332: );
10333: my %js_lt=&Apache::lonlocal::texthash(
1.571 raeburn 10334: 'youm' => "You must include some text to search for.",
10335: 'thte' => "The text you are searching for must contain at least two characters when using a 'begins' type search.",
10336: 'thet' => "The text you are searching for must contain at least three characters when using a 'contains' type search.",
10337: 'yomc' => "You must choose a domain when using an institutional directory search.",
10338: 'ymcd' => "You must choose a domain when using a domain search.",
10339: 'whus' => "When using searching by last,first you must include a comma as separator between last name and first name.",
10340: 'whse' => "When searching by last,first you must include at least one character in the first name.",
10341: 'thfo' => "The following need to be corrected before the search can be run:",
1.555 raeburn 10342: );
1.1222 damieng 10343: &html_escape(\%html_lt);
10344: &js_escape(\%js_lt);
1.1255 raeburn 10345: my $domform;
1.1277 raeburn 10346: my $allow_blank = 1;
1.1255 raeburn 10347: if ($fixeddom) {
1.1277 raeburn 10348: $allow_blank = 0;
10349: $domform = &select_dom_form($currdom,'srchdomain',$allow_blank,1,undef,[$currdom]);
1.1255 raeburn 10350: } else {
1.1287 raeburn 10351: my $defdom = $env{'request.role.domain'};
1.1288 raeburn 10352: my ($trusted,$untrusted);
1.1287 raeburn 10353: if (($context eq 'requestcrs') || ($context eq 'course')) {
1.1288 raeburn 10354: ($trusted,$untrusted) = &Apache::lonnet::trusted_domains('enroll',$defdom);
1.1287 raeburn 10355: } elsif ($context eq 'author') {
1.1288 raeburn 10356: ($trusted,$untrusted) = &Apache::lonnet::trusted_domains('othcoau',$defdom);
1.1287 raeburn 10357: } elsif ($context eq 'domain') {
1.1288 raeburn 10358: ($trusted,$untrusted) = &Apache::lonnet::trusted_domains('domroles',$defdom);
1.1287 raeburn 10359: }
1.1288 raeburn 10360: $domform = &select_dom_form($currdom,'srchdomain',$allow_blank,1,undef,$trusted,$untrusted);
1.1255 raeburn 10361: }
1.563 raeburn 10362: my $srchinsel = ' <select name="srchin">';
1.555 raeburn 10363:
10364: my @srchins = ('crs','dom','alc','instd');
10365:
10366: foreach my $option (@srchins) {
10367: # FIXME 'alc' option unavailable until
10368: # loncreateuser::print_user_query_page()
10369: # has been completed.
10370: next if ($option eq 'alc');
1.880 raeburn 10371: next if (($option eq 'crs') && ($env{'form.form'} eq 'requestcrs'));
1.555 raeburn 10372: next if ($option eq 'crs' && !$env{'request.course.id'});
1.1279 raeburn 10373: next if (($option eq 'instd') && ($noinstd));
1.563 raeburn 10374: if ($curr_selected{'srchin'} eq $option) {
10375: $srchinsel .= '
1.1222 damieng 10376: <option value="'.$option.'" selected="selected">'.$html_lt{$option}.'</option>';
1.563 raeburn 10377: } else {
10378: $srchinsel .= '
1.1222 damieng 10379: <option value="'.$option.'">'.$html_lt{$option}.'</option>';
1.563 raeburn 10380: }
1.555 raeburn 10381: }
1.563 raeburn 10382: $srchinsel .= "\n </select>\n";
1.555 raeburn 10383:
10384: my $srchbysel = ' <select name="srchby">';
1.580 raeburn 10385: foreach my $option ('lastname','lastfirst','uname') {
1.555 raeburn 10386: if ($curr_selected{'srchby'} eq $option) {
10387: $srchbysel .= '
1.1222 damieng 10388: <option value="'.$option.'" selected="selected">'.$html_lt{$option}.'</option>';
1.555 raeburn 10389: } else {
10390: $srchbysel .= '
1.1222 damieng 10391: <option value="'.$option.'">'.$html_lt{$option}.'</option>';
1.555 raeburn 10392: }
10393: }
10394: $srchbysel .= "\n </select>\n";
10395:
10396: my $srchtypesel = ' <select name="srchtype">';
1.580 raeburn 10397: foreach my $option ('begins','contains','exact') {
1.555 raeburn 10398: if ($curr_selected{'srchtype'} eq $option) {
10399: $srchtypesel .= '
1.1222 damieng 10400: <option value="'.$option.'" selected="selected">'.$html_lt{$option}.'</option>';
1.555 raeburn 10401: } else {
10402: $srchtypesel .= '
1.1222 damieng 10403: <option value="'.$option.'">'.$html_lt{$option}.'</option>';
1.555 raeburn 10404: }
10405: }
10406: $srchtypesel .= "\n </select>\n";
10407:
1.558 albertel 10408: my ($newuserscript,$new_user_create);
1.994 raeburn 10409: my $context_dom = $env{'request.role.domain'};
10410: if ($context eq 'requestcrs') {
10411: if ($env{'form.coursedom'} ne '') {
10412: $context_dom = $env{'form.coursedom'};
10413: }
10414: }
1.556 raeburn 10415: if ($forcenewuser) {
1.576 raeburn 10416: if (ref($srch) eq 'HASH') {
1.994 raeburn 10417: if ($srch->{'srchby'} eq 'uname' && $srch->{'srchtype'} eq 'exact' && $srch->{'srchin'} eq 'dom' && $srch->{'srchdomain'} eq $context_dom) {
1.627 raeburn 10418: if ($cancreate) {
10419: $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>';
10420: } else {
1.799 bisitz 10421: my $helplink = 'javascript:helpMenu('."'display'".')';
1.627 raeburn 10422: my %usertypetext = (
10423: official => 'institutional',
10424: unofficial => 'non-institutional',
10425: );
1.799 bisitz 10426: $new_user_create = '<p class="LC_warning">'
10427: .&mt("You are not authorized to create new $usertypetext{$usertype} users in this domain.")
10428: .' '
10429: .&mt('Please contact the [_1]helpdesk[_2] for assistance.'
10430: ,'<a href="'.$helplink.'">','</a>')
10431: .'</p><br />';
1.627 raeburn 10432: }
1.576 raeburn 10433: }
10434: }
10435:
1.556 raeburn 10436: $newuserscript = <<"ENDSCRIPT";
10437:
1.570 raeburn 10438: function setSearch(createnew,callingForm) {
1.556 raeburn 10439: if (createnew == 1) {
1.570 raeburn 10440: for (var i=0; i<callingForm.srchby.length; i++) {
10441: if (callingForm.srchby.options[i].value == 'uname') {
10442: callingForm.srchby.selectedIndex = i;
1.556 raeburn 10443: }
10444: }
1.570 raeburn 10445: for (var i=0; i<callingForm.srchin.length; i++) {
10446: if ( callingForm.srchin.options[i].value == 'dom') {
10447: callingForm.srchin.selectedIndex = i;
1.556 raeburn 10448: }
10449: }
1.570 raeburn 10450: for (var i=0; i<callingForm.srchtype.length; i++) {
10451: if (callingForm.srchtype.options[i].value == 'exact') {
10452: callingForm.srchtype.selectedIndex = i;
1.556 raeburn 10453: }
10454: }
1.570 raeburn 10455: for (var i=0; i<callingForm.srchdomain.length; i++) {
1.994 raeburn 10456: if (callingForm.srchdomain.options[i].value == '$context_dom') {
1.570 raeburn 10457: callingForm.srchdomain.selectedIndex = i;
1.556 raeburn 10458: }
10459: }
10460: }
10461: }
10462: ENDSCRIPT
1.558 albertel 10463:
1.556 raeburn 10464: }
10465:
1.555 raeburn 10466: my $output = <<"END_BLOCK";
1.556 raeburn 10467: <script type="text/javascript">
1.824 bisitz 10468: // <![CDATA[
1.570 raeburn 10469: function validateEntry(callingForm) {
1.558 albertel 10470:
1.556 raeburn 10471: var checkok = 1;
1.558 albertel 10472: var srchin;
1.570 raeburn 10473: for (var i=0; i<callingForm.srchin.length; i++) {
10474: if ( callingForm.srchin[i].checked ) {
10475: srchin = callingForm.srchin[i].value;
1.558 albertel 10476: }
10477: }
10478:
1.570 raeburn 10479: var srchtype = callingForm.srchtype.options[callingForm.srchtype.selectedIndex].value;
10480: var srchby = callingForm.srchby.options[callingForm.srchby.selectedIndex].value;
10481: var srchdomain = callingForm.srchdomain.options[callingForm.srchdomain.selectedIndex].value;
10482: var srchterm = callingForm.srchterm.value;
10483: var srchin = callingForm.srchin.options[callingForm.srchin.selectedIndex].value;
1.556 raeburn 10484: var msg = "";
10485:
10486: if (srchterm == "") {
10487: checkok = 0;
1.1222 damieng 10488: msg += "$js_lt{'youm'}\\n";
1.556 raeburn 10489: }
10490:
1.569 raeburn 10491: if (srchtype== 'begins') {
10492: if (srchterm.length < 2) {
10493: checkok = 0;
1.1222 damieng 10494: msg += "$js_lt{'thte'}\\n";
1.569 raeburn 10495: }
10496: }
10497:
1.556 raeburn 10498: if (srchtype== 'contains') {
10499: if (srchterm.length < 3) {
10500: checkok = 0;
1.1222 damieng 10501: msg += "$js_lt{'thet'}\\n";
1.556 raeburn 10502: }
10503: }
10504: if (srchin == 'instd') {
10505: if (srchdomain == '') {
10506: checkok = 0;
1.1222 damieng 10507: msg += "$js_lt{'yomc'}\\n";
1.556 raeburn 10508: }
10509: }
10510: if (srchin == 'dom') {
10511: if (srchdomain == '') {
10512: checkok = 0;
1.1222 damieng 10513: msg += "$js_lt{'ymcd'}\\n";
1.556 raeburn 10514: }
10515: }
10516: if (srchby == 'lastfirst') {
10517: if (srchterm.indexOf(",") == -1) {
10518: checkok = 0;
1.1222 damieng 10519: msg += "$js_lt{'whus'}\\n";
1.556 raeburn 10520: }
10521: if (srchterm.indexOf(",") == srchterm.length -1) {
10522: checkok = 0;
1.1222 damieng 10523: msg += "$js_lt{'whse'}\\n";
1.556 raeburn 10524: }
10525: }
10526: if (checkok == 0) {
1.1222 damieng 10527: alert("$js_lt{'thfo'}\\n"+msg);
1.556 raeburn 10528: return;
10529: }
10530: if (checkok == 1) {
1.570 raeburn 10531: callingForm.submit();
1.556 raeburn 10532: }
10533: }
10534:
10535: $newuserscript
10536:
1.824 bisitz 10537: // ]]>
1.556 raeburn 10538: </script>
1.558 albertel 10539:
10540: $new_user_create
10541:
1.555 raeburn 10542: END_BLOCK
1.558 albertel 10543:
1.876 raeburn 10544: $output .= &Apache::lonhtmlcommon::start_pick_box().
1.1222 damieng 10545: &Apache::lonhtmlcommon::row_title($html_lt{'doma'}).
1.876 raeburn 10546: $domform.
10547: &Apache::lonhtmlcommon::row_closure().
1.1222 damieng 10548: &Apache::lonhtmlcommon::row_title($html_lt{'usr'}).
1.876 raeburn 10549: $srchbysel.
10550: $srchtypesel.
10551: '<input type="text" size="15" name="srchterm" value="'.$srchterm.'" />'.
10552: $srchinsel.
10553: &Apache::lonhtmlcommon::row_closure(1).
10554: &Apache::lonhtmlcommon::end_pick_box().
10555: '<br />';
1.1253 raeburn 10556: return ($output,1);
1.555 raeburn 10557: }
10558:
1.612 raeburn 10559: sub user_rule_check {
1.615 raeburn 10560: my ($usershash,$checks,$alerts,$rulematch,$inst_results,$curr_rules,$got_rules) = @_;
1.1226 raeburn 10561: my ($response,%inst_response);
1.612 raeburn 10562: if (ref($usershash) eq 'HASH') {
1.1226 raeburn 10563: if (keys(%{$usershash}) > 1) {
10564: my (%by_username,%by_id,%userdoms);
10565: my $checkid;
10566: if (ref($checks) eq 'HASH') {
10567: if ((!defined($checks->{'username'})) && (defined($checks->{'id'}))) {
10568: $checkid = 1;
10569: }
10570: }
10571: foreach my $user (keys(%{$usershash})) {
10572: my ($uname,$udom) = split(/:/,$user);
10573: if ($checkid) {
10574: if (ref($usershash->{$user}) eq 'HASH') {
10575: if ($usershash->{$user}->{'id'} ne '') {
1.1227 raeburn 10576: $by_id{$udom}{$usershash->{$user}->{'id'}} = $uname;
1.1226 raeburn 10577: $userdoms{$udom} = 1;
1.1227 raeburn 10578: if (ref($inst_results) eq 'HASH') {
10579: $inst_results->{$uname.':'.$udom} = {};
10580: }
1.1226 raeburn 10581: }
10582: }
10583: } else {
10584: $by_username{$udom}{$uname} = 1;
10585: $userdoms{$udom} = 1;
1.1227 raeburn 10586: if (ref($inst_results) eq 'HASH') {
10587: $inst_results->{$uname.':'.$udom} = {};
10588: }
1.1226 raeburn 10589: }
10590: }
10591: foreach my $udom (keys(%userdoms)) {
10592: if (!$got_rules->{$udom}) {
10593: my %domconfig = &Apache::lonnet::get_dom('configuration',
10594: ['usercreation'],$udom);
10595: if (ref($domconfig{'usercreation'}) eq 'HASH') {
10596: foreach my $item ('username','id') {
10597: if (ref($domconfig{'usercreation'}{$item.'_rule'}) eq 'ARRAY') {
1.1227 raeburn 10598: $$curr_rules{$udom}{$item} =
10599: $domconfig{'usercreation'}{$item.'_rule'};
1.1226 raeburn 10600: }
10601: }
10602: }
10603: $got_rules->{$udom} = 1;
10604: }
1.612 raeburn 10605: }
1.1226 raeburn 10606: if ($checkid) {
10607: foreach my $udom (keys(%by_id)) {
10608: my ($outcome,$results) = &Apache::lonnet::get_multiple_instusers($udom,$by_id{$udom},'id');
10609: if ($outcome eq 'ok') {
1.1227 raeburn 10610: foreach my $id (keys(%{$by_id{$udom}})) {
10611: my $uname = $by_id{$udom}{$id};
10612: $inst_response{$uname.':'.$udom} = $outcome;
10613: }
1.1226 raeburn 10614: if (ref($results) eq 'HASH') {
10615: foreach my $uname (keys(%{$results})) {
1.1227 raeburn 10616: if (exists($inst_response{$uname.':'.$udom})) {
10617: $inst_response{$uname.':'.$udom} = $outcome;
10618: $inst_results->{$uname.':'.$udom} = $results->{$uname};
10619: }
1.1226 raeburn 10620: }
10621: }
10622: }
1.612 raeburn 10623: }
1.615 raeburn 10624: } else {
1.1226 raeburn 10625: foreach my $udom (keys(%by_username)) {
10626: my ($outcome,$results) = &Apache::lonnet::get_multiple_instusers($udom,$by_username{$udom});
10627: if ($outcome eq 'ok') {
1.1227 raeburn 10628: foreach my $uname (keys(%{$by_username{$udom}})) {
10629: $inst_response{$uname.':'.$udom} = $outcome;
10630: }
1.1226 raeburn 10631: if (ref($results) eq 'HASH') {
10632: foreach my $uname (keys(%{$results})) {
10633: $inst_results->{$uname.':'.$udom} = $results->{$uname};
10634: }
10635: }
10636: }
10637: }
1.612 raeburn 10638: }
1.1226 raeburn 10639: } elsif (keys(%{$usershash}) == 1) {
10640: my $user = (keys(%{$usershash}))[0];
10641: my ($uname,$udom) = split(/:/,$user);
10642: if (($udom ne '') && ($uname ne '')) {
10643: if (ref($usershash->{$user}) eq 'HASH') {
10644: if (ref($checks) eq 'HASH') {
10645: if (defined($checks->{'username'})) {
10646: ($inst_response{$user},%{$inst_results->{$user}}) =
10647: &Apache::lonnet::get_instuser($udom,$uname);
10648: } elsif (defined($checks->{'id'})) {
10649: if ($usershash->{$user}->{'id'} ne '') {
10650: ($inst_response{$user},%{$inst_results->{$user}}) =
10651: &Apache::lonnet::get_instuser($udom,undef,
10652: $usershash->{$user}->{'id'});
10653: } else {
10654: ($inst_response{$user},%{$inst_results->{$user}}) =
10655: &Apache::lonnet::get_instuser($udom,$uname);
10656: }
1.585 raeburn 10657: }
1.1226 raeburn 10658: } else {
10659: ($inst_response{$user},%{$inst_results->{$user}}) =
10660: &Apache::lonnet::get_instuser($udom,$uname);
10661: return;
10662: }
10663: if (!$got_rules->{$udom}) {
10664: my %domconfig = &Apache::lonnet::get_dom('configuration',
10665: ['usercreation'],$udom);
10666: if (ref($domconfig{'usercreation'}) eq 'HASH') {
10667: foreach my $item ('username','id') {
10668: if (ref($domconfig{'usercreation'}{$item.'_rule'}) eq 'ARRAY') {
10669: $$curr_rules{$udom}{$item} =
10670: $domconfig{'usercreation'}{$item.'_rule'};
10671: }
10672: }
10673: }
10674: $got_rules->{$udom} = 1;
1.585 raeburn 10675: }
10676: }
1.1226 raeburn 10677: } else {
10678: return;
10679: }
10680: } else {
10681: return;
10682: }
10683: foreach my $user (keys(%{$usershash})) {
10684: my ($uname,$udom) = split(/:/,$user);
10685: next if (($udom eq '') || ($uname eq ''));
10686: my $id;
1.1227 raeburn 10687: if (ref($inst_results) eq 'HASH') {
10688: if (ref($inst_results->{$user}) eq 'HASH') {
10689: $id = $inst_results->{$user}->{'id'};
10690: }
10691: }
10692: if ($id eq '') {
10693: if (ref($usershash->{$user})) {
10694: $id = $usershash->{$user}->{'id'};
10695: }
1.585 raeburn 10696: }
1.612 raeburn 10697: foreach my $item (keys(%{$checks})) {
10698: if (ref($$curr_rules{$udom}) eq 'HASH') {
10699: if (ref($$curr_rules{$udom}{$item}) eq 'ARRAY') {
10700: if (@{$$curr_rules{$udom}{$item}} > 0) {
1.1226 raeburn 10701: my %rule_check = &Apache::lonnet::inst_rulecheck($udom,$uname,$id,$item,
10702: $$curr_rules{$udom}{$item});
1.612 raeburn 10703: foreach my $rule (@{$$curr_rules{$udom}{$item}}) {
10704: if ($rule_check{$rule}) {
10705: $$rulematch{$user}{$item} = $rule;
1.1226 raeburn 10706: if ($inst_response{$user} eq 'ok') {
1.615 raeburn 10707: if (ref($inst_results) eq 'HASH') {
10708: if (ref($inst_results->{$user}) eq 'HASH') {
10709: if (keys(%{$inst_results->{$user}}) == 0) {
10710: $$alerts{$item}{$udom}{$uname} = 1;
1.1227 raeburn 10711: } elsif ($item eq 'id') {
10712: if ($inst_results->{$user}->{'id'} eq '') {
10713: $$alerts{$item}{$udom}{$uname} = 1;
10714: }
1.615 raeburn 10715: }
1.612 raeburn 10716: }
10717: }
1.615 raeburn 10718: }
10719: last;
1.585 raeburn 10720: }
10721: }
10722: }
10723: }
10724: }
10725: }
10726: }
10727: }
1.612 raeburn 10728: return;
10729: }
10730:
10731: sub user_rule_formats {
10732: my ($domain,$domdesc,$curr_rules,$check) = @_;
10733: my %text = (
10734: 'username' => 'Usernames',
10735: 'id' => 'IDs',
10736: );
10737: my $output;
10738: my ($rules,$ruleorder) = &Apache::lonnet::inst_userrules($domain,$check);
10739: if ((ref($rules) eq 'HASH') && (ref($ruleorder) eq 'ARRAY')) {
10740: if (@{$ruleorder} > 0) {
1.1102 raeburn 10741: $output = '<br />'.
10742: &mt($text{$check}.' with the following format(s) may [_1]only[_2] be used for verified users at [_3]:',
10743: '<span class="LC_cusr_emph">','</span>',$domdesc).
10744: ' <ul>';
1.612 raeburn 10745: foreach my $rule (@{$ruleorder}) {
10746: if (ref($curr_rules) eq 'ARRAY') {
10747: if (grep(/^\Q$rule\E$/,@{$curr_rules})) {
10748: if (ref($rules->{$rule}) eq 'HASH') {
10749: $output .= '<li>'.$rules->{$rule}{'name'}.': '.
10750: $rules->{$rule}{'desc'}.'</li>';
10751: }
10752: }
10753: }
10754: }
10755: $output .= '</ul>';
10756: }
10757: }
10758: return $output;
10759: }
10760:
10761: sub instrule_disallow_msg {
1.615 raeburn 10762: my ($checkitem,$domdesc,$count,$mode) = @_;
1.612 raeburn 10763: my $response;
10764: my %text = (
10765: item => 'username',
10766: items => 'usernames',
10767: match => 'matches',
10768: do => 'does',
10769: action => 'a username',
10770: one => 'one',
10771: );
10772: if ($count > 1) {
10773: $text{'item'} = 'usernames';
10774: $text{'match'} ='match';
10775: $text{'do'} = 'do';
10776: $text{'action'} = 'usernames',
10777: $text{'one'} = 'ones';
10778: }
10779: if ($checkitem eq 'id') {
10780: $text{'items'} = 'IDs';
10781: $text{'item'} = 'ID';
10782: $text{'action'} = 'an ID';
1.615 raeburn 10783: if ($count > 1) {
10784: $text{'item'} = 'IDs';
10785: $text{'action'} = 'IDs';
10786: }
1.612 raeburn 10787: }
1.674 bisitz 10788: $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 10789: if ($mode eq 'upload') {
10790: if ($checkitem eq 'username') {
10791: $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'}.");
10792: } elsif ($checkitem eq 'id') {
1.674 bisitz 10793: $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 10794: }
1.669 raeburn 10795: } elsif ($mode eq 'selfcreate') {
10796: if ($checkitem eq 'id') {
10797: $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.");
10798: }
1.615 raeburn 10799: } else {
10800: if ($checkitem eq 'username') {
10801: $response .= &mt("You must choose $text{'action'} with a different format -- $text{'one'} that will not conflict with 'official' institutional $text{'items'}.");
10802: } elsif ($checkitem eq 'id') {
10803: $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.");
10804: }
1.612 raeburn 10805: }
10806: return $response;
1.585 raeburn 10807: }
10808:
1.624 raeburn 10809: sub personal_data_fieldtitles {
10810: my %fieldtitles = &Apache::lonlocal::texthash (
10811: id => 'Student/Employee ID',
10812: permanentemail => 'E-mail address',
10813: lastname => 'Last Name',
10814: firstname => 'First Name',
10815: middlename => 'Middle Name',
10816: generation => 'Generation',
10817: gen => 'Generation',
1.765 raeburn 10818: inststatus => 'Affiliation',
1.624 raeburn 10819: );
10820: return %fieldtitles;
10821: }
10822:
1.642 raeburn 10823: sub sorted_inst_types {
10824: my ($dom) = @_;
1.1185 raeburn 10825: my ($usertypes,$order);
10826: my %domdefaults = &Apache::lonnet::get_domain_defaults($dom);
10827: if (ref($domdefaults{'inststatus'}) eq 'HASH') {
10828: $usertypes = $domdefaults{'inststatus'}{'inststatustypes'};
10829: $order = $domdefaults{'inststatus'}{'inststatusorder'};
10830: } else {
10831: ($usertypes,$order) = &Apache::lonnet::retrieve_inst_usertypes($dom);
10832: }
1.642 raeburn 10833: my $othertitle = &mt('All users');
10834: if ($env{'request.course.id'}) {
1.668 raeburn 10835: $othertitle = &mt('Any users');
1.642 raeburn 10836: }
10837: my @types;
10838: if (ref($order) eq 'ARRAY') {
10839: @types = @{$order};
10840: }
10841: if (@types == 0) {
10842: if (ref($usertypes) eq 'HASH') {
10843: @types = sort(keys(%{$usertypes}));
10844: }
10845: }
10846: if (keys(%{$usertypes}) > 0) {
10847: $othertitle = &mt('Other users');
10848: }
10849: return ($othertitle,$usertypes,\@types);
10850: }
10851:
1.645 raeburn 10852: sub get_institutional_codes {
10853: my ($settings,$allcourses,$LC_code) = @_;
10854: # Get complete list of course sections to update
10855: my @currsections = ();
10856: my @currxlists = ();
10857: my $coursecode = $$settings{'internal.coursecode'};
10858:
10859: if ($$settings{'internal.sectionnums'} ne '') {
10860: @currsections = split(/,/,$$settings{'internal.sectionnums'});
10861: }
10862:
10863: if ($$settings{'internal.crosslistings'} ne '') {
10864: @currxlists = split(/,/,$$settings{'internal.crosslistings'});
10865: }
10866:
10867: if (@currxlists > 0) {
10868: foreach (@currxlists) {
10869: if (m/^([^:]+):(\w*)$/) {
10870: unless (grep/^$1$/,@{$allcourses}) {
1.1263 raeburn 10871: push(@{$allcourses},$1);
1.645 raeburn 10872: $$LC_code{$1} = $2;
10873: }
10874: }
10875: }
10876: }
10877:
10878: if (@currsections > 0) {
10879: foreach (@currsections) {
10880: if (m/^(\w+):(\w*)$/) {
10881: my $sec = $coursecode.$1;
10882: my $lc_sec = $2;
10883: unless (grep/^$sec$/,@{$allcourses}) {
1.1263 raeburn 10884: push(@{$allcourses},$sec);
1.645 raeburn 10885: $$LC_code{$sec} = $lc_sec;
10886: }
10887: }
10888: }
10889: }
10890: return;
10891: }
10892:
1.971 raeburn 10893: sub get_standard_codeitems {
10894: return ('Year','Semester','Department','Number','Section');
10895: }
10896:
1.112 bowersj2 10897: =pod
10898:
1.780 raeburn 10899: =head1 Slot Helpers
10900:
10901: =over 4
10902:
10903: =item * sorted_slots()
10904:
1.1040 raeburn 10905: Sorts an array of slot names in order of an optional sort key,
10906: default sort is by slot start time (earliest first).
1.780 raeburn 10907:
10908: Inputs:
10909:
10910: =over 4
10911:
10912: slotsarr - Reference to array of unsorted slot names.
10913:
10914: slots - Reference to hash of hash, where outer hash keys are slot names.
10915:
1.1040 raeburn 10916: sortkey - Name of key in inner hash to be sorted on (e.g., starttime).
10917:
1.549 albertel 10918: =back
10919:
1.780 raeburn 10920: Returns:
10921:
10922: =over 4
10923:
1.1040 raeburn 10924: sorted - An array of slot names sorted by a specified sort key
10925: (default sort key is start time of the slot).
1.780 raeburn 10926:
10927: =back
10928:
10929: =cut
10930:
10931:
10932: sub sorted_slots {
1.1040 raeburn 10933: my ($slotsarr,$slots,$sortkey) = @_;
10934: if ($sortkey eq '') {
10935: $sortkey = 'starttime';
10936: }
1.780 raeburn 10937: my @sorted;
10938: if ((ref($slotsarr) eq 'ARRAY') && (ref($slots) eq 'HASH')) {
10939: @sorted =
10940: sort {
10941: if (ref($slots->{$a}) && ref($slots->{$b})) {
1.1040 raeburn 10942: return $slots->{$a}{$sortkey} <=> $slots->{$b}{$sortkey}
1.780 raeburn 10943: }
10944: if (ref($slots->{$a})) { return -1;}
10945: if (ref($slots->{$b})) { return 1;}
10946: return 0;
10947: } @{$slotsarr};
10948: }
10949: return @sorted;
10950: }
10951:
1.1040 raeburn 10952: =pod
10953:
10954: =item * get_future_slots()
10955:
10956: Inputs:
10957:
10958: =over 4
10959:
10960: cnum - course number
10961:
10962: cdom - course domain
10963:
10964: now - current UNIX time
10965:
10966: symb - optional symb
10967:
10968: =back
10969:
10970: Returns:
10971:
10972: =over 4
10973:
10974: sorted_reservable - ref to array of student_schedulable slots currently
10975: reservable, ordered by end date of reservation period.
10976:
10977: reservable_now - ref to hash of student_schedulable slots currently
10978: reservable.
10979:
10980: Keys in inner hash are:
10981: (a) symb: either blank or symb to which slot use is restricted.
1.1250 raeburn 10982: (b) endreserve: end date of reservation period.
10983: (c) uniqueperiod: start,end dates when slot is to be uniquely
10984: selected.
1.1040 raeburn 10985:
10986: sorted_future - ref to array of student_schedulable slots reservable in
10987: the future, ordered by start date of reservation period.
10988:
10989: future_reservable - ref to hash of student_schedulable slots reservable
10990: in the future.
10991:
10992: Keys in inner hash are:
10993: (a) symb: either blank or symb to which slot use is restricted.
1.1250 raeburn 10994: (b) startreserve: start date of reservation period.
10995: (c) uniqueperiod: start,end dates when slot is to be uniquely
10996: selected.
1.1040 raeburn 10997:
10998: =back
10999:
11000: =cut
11001:
11002: sub get_future_slots {
11003: my ($cnum,$cdom,$now,$symb) = @_;
1.1229 raeburn 11004: my $map;
11005: if ($symb) {
11006: ($map) = &Apache::lonnet::decode_symb($symb);
11007: }
1.1040 raeburn 11008: my (%reservable_now,%future_reservable,@sorted_reservable,@sorted_future);
11009: my %slots = &Apache::lonnet::get_course_slots($cnum,$cdom);
11010: foreach my $slot (keys(%slots)) {
11011: next unless($slots{$slot}->{'type'} eq 'schedulable_student');
11012: if ($symb) {
1.1229 raeburn 11013: if ($slots{$slot}->{'symb'} ne '') {
11014: my $canuse;
11015: my %oksymbs;
11016: my @slotsymbs = split(/\s*,\s*/,$slots{$slot}->{'symb'});
11017: map { $oksymbs{$_} = 1; } @slotsymbs;
11018: if ($oksymbs{$symb}) {
11019: $canuse = 1;
11020: } else {
11021: foreach my $item (@slotsymbs) {
11022: if ($item =~ /\.(page|sequence)$/) {
11023: (undef,undef,my $sloturl) = &Apache::lonnet::decode_symb($item);
11024: if (($map ne '') && ($map eq $sloturl)) {
11025: $canuse = 1;
11026: last;
11027: }
11028: }
11029: }
11030: }
11031: next unless ($canuse);
11032: }
1.1040 raeburn 11033: }
11034: if (($slots{$slot}->{'starttime'} > $now) &&
11035: ($slots{$slot}->{'endtime'} > $now)) {
11036: if (($slots{$slot}->{'allowedsections'}) || ($slots{$slot}->{'allowedusers'})) {
11037: my $userallowed = 0;
11038: if ($slots{$slot}->{'allowedsections'}) {
11039: my @allowed_sec = split(',',$slots{$slot}->{'allowedsections'});
11040: if (!defined($env{'request.role.sec'})
11041: && grep(/^No section assigned$/,@allowed_sec)) {
11042: $userallowed=1;
11043: } else {
11044: if (grep(/^\Q$env{'request.role.sec'}\E$/,@allowed_sec)) {
11045: $userallowed=1;
11046: }
11047: }
11048: unless ($userallowed) {
11049: if (defined($env{'request.course.groups'})) {
11050: my @groups = split(/:/,$env{'request.course.groups'});
11051: foreach my $group (@groups) {
11052: if (grep(/^\Q$group\E$/,@allowed_sec)) {
11053: $userallowed=1;
11054: last;
11055: }
11056: }
11057: }
11058: }
11059: }
11060: if ($slots{$slot}->{'allowedusers'}) {
11061: my @allowed_users = split(',',$slots{$slot}->{'allowedusers'});
11062: my $user = $env{'user.name'}.':'.$env{'user.domain'};
11063: if (grep(/^\Q$user\E$/,@allowed_users)) {
11064: $userallowed = 1;
11065: }
11066: }
11067: next unless($userallowed);
11068: }
11069: my $startreserve = $slots{$slot}->{'startreserve'};
11070: my $endreserve = $slots{$slot}->{'endreserve'};
11071: my $symb = $slots{$slot}->{'symb'};
1.1250 raeburn 11072: my $uniqueperiod;
11073: if (ref($slots{$slot}->{'uniqueperiod'}) eq 'ARRAY') {
11074: $uniqueperiod = join(',',@{$slots{$slot}->{'uniqueperiod'}});
11075: }
1.1040 raeburn 11076: if (($startreserve < $now) &&
11077: (!$endreserve || $endreserve > $now)) {
11078: my $lastres = $endreserve;
11079: if (!$lastres) {
11080: $lastres = $slots{$slot}->{'starttime'};
11081: }
11082: $reservable_now{$slot} = {
11083: symb => $symb,
1.1250 raeburn 11084: endreserve => $lastres,
11085: uniqueperiod => $uniqueperiod,
1.1040 raeburn 11086: };
11087: } elsif (($startreserve > $now) &&
11088: (!$endreserve || $endreserve > $startreserve)) {
11089: $future_reservable{$slot} = {
11090: symb => $symb,
1.1250 raeburn 11091: startreserve => $startreserve,
11092: uniqueperiod => $uniqueperiod,
1.1040 raeburn 11093: };
11094: }
11095: }
11096: }
11097: my @unsorted_reservable = keys(%reservable_now);
11098: if (@unsorted_reservable > 0) {
11099: @sorted_reservable =
11100: &sorted_slots(\@unsorted_reservable,\%reservable_now,'endreserve');
11101: }
11102: my @unsorted_future = keys(%future_reservable);
11103: if (@unsorted_future > 0) {
11104: @sorted_future =
11105: &sorted_slots(\@unsorted_future,\%future_reservable,'startreserve');
11106: }
11107: return (\@sorted_reservable,\%reservable_now,\@sorted_future,\%future_reservable);
11108: }
1.780 raeburn 11109:
11110: =pod
11111:
1.1057 foxr 11112: =back
11113:
1.549 albertel 11114: =head1 HTTP Helpers
11115:
11116: =over 4
11117:
1.648 raeburn 11118: =item * &get_unprocessed_cgi($query,$possible_names)
1.112 bowersj2 11119:
1.258 albertel 11120: Modify the %env hash to contain unprocessed CGI form parameters held in
1.112 bowersj2 11121: $query. The parameters listed in $possible_names (an array reference),
1.258 albertel 11122: will be set in $env{'form.name'} if they do not already exist.
1.112 bowersj2 11123:
11124: Typically called with $ENV{'QUERY_STRING'} as the first parameter.
11125: $possible_names is an ref to an array of form element names. As an example:
11126: get_unprocessed_cgi($ENV{'QUERY_STRING'},['uname','udom']);
1.258 albertel 11127: will result in $env{'form.uname'} and $env{'form.udom'} being set.
1.112 bowersj2 11128:
11129: =cut
1.1 albertel 11130:
1.6 albertel 11131: sub get_unprocessed_cgi {
1.25 albertel 11132: my ($query,$possible_names)= @_;
1.26 matthew 11133: # $Apache::lonxml::debug=1;
1.356 albertel 11134: foreach my $pair (split(/&/,$query)) {
11135: my ($name, $value) = split(/=/,$pair);
1.369 www 11136: $name = &unescape($name);
1.25 albertel 11137: if (!defined($possible_names) || (grep {$_ eq $name} @$possible_names)) {
11138: $value =~ tr/+/ /;
11139: $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
1.258 albertel 11140: unless (defined($env{'form.'.$name})) { &add_to_env('form.'.$name,$value) };
1.25 albertel 11141: }
1.16 harris41 11142: }
1.6 albertel 11143: }
11144:
1.112 bowersj2 11145: =pod
11146:
1.648 raeburn 11147: =item * &cacheheader()
1.112 bowersj2 11148:
11149: returns cache-controlling header code
11150:
11151: =cut
11152:
1.7 albertel 11153: sub cacheheader {
1.258 albertel 11154: unless ($env{'request.method'} eq 'GET') { return ''; }
1.216 albertel 11155: my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime);
11156: my $output .='<meta HTTP-EQUIV="Expires" CONTENT="'.$date.'" />
1.7 albertel 11157: <meta HTTP-EQUIV="Cache-control" CONTENT="no-cache" />
11158: <meta HTTP-EQUIV="Pragma" CONTENT="no-cache" />';
1.216 albertel 11159: return $output;
1.7 albertel 11160: }
11161:
1.112 bowersj2 11162: =pod
11163:
1.648 raeburn 11164: =item * &no_cache($r)
1.112 bowersj2 11165:
11166: specifies header code to not have cache
11167:
11168: =cut
11169:
1.9 albertel 11170: sub no_cache {
1.216 albertel 11171: my ($r) = @_;
11172: if ($ENV{'REQUEST_METHOD'} ne 'GET' &&
1.258 albertel 11173: $env{'request.method'} ne 'GET') { return ''; }
1.216 albertel 11174: my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime(time));
11175: $r->no_cache(1);
11176: $r->header_out("Expires" => $date);
11177: $r->header_out("Pragma" => "no-cache");
1.123 www 11178: }
11179:
11180: sub content_type {
1.181 albertel 11181: my ($r,$type,$charset) = @_;
1.299 foxr 11182: if ($r) {
11183: # Note that printout.pl calls this with undef for $r.
11184: &no_cache($r);
11185: }
1.258 albertel 11186: if ($env{'browser.mathml'} && $type eq 'text/html') { $type='text/xml'; }
1.181 albertel 11187: unless ($charset) {
11188: $charset=&Apache::lonlocal::current_encoding;
11189: }
11190: if ($charset) { $type.='; charset='.$charset; }
11191: if ($r) {
11192: $r->content_type($type);
11193: } else {
11194: print("Content-type: $type\n\n");
11195: }
1.9 albertel 11196: }
1.25 albertel 11197:
1.112 bowersj2 11198: =pod
11199:
1.648 raeburn 11200: =item * &add_to_env($name,$value)
1.112 bowersj2 11201:
1.258 albertel 11202: adds $name to the %env hash with value
1.112 bowersj2 11203: $value, if $name already exists, the entry is converted to an array
11204: reference and $value is added to the array.
11205:
11206: =cut
11207:
1.25 albertel 11208: sub add_to_env {
11209: my ($name,$value)=@_;
1.258 albertel 11210: if (defined($env{$name})) {
11211: if (ref($env{$name})) {
1.25 albertel 11212: #already have multiple values
1.258 albertel 11213: push(@{ $env{$name} },$value);
1.25 albertel 11214: } else {
11215: #first time seeing multiple values, convert hash entry to an arrayref
1.258 albertel 11216: my $first=$env{$name};
11217: undef($env{$name});
11218: push(@{ $env{$name} },$first,$value);
1.25 albertel 11219: }
11220: } else {
1.258 albertel 11221: $env{$name}=$value;
1.25 albertel 11222: }
1.31 albertel 11223: }
1.149 albertel 11224:
11225: =pod
11226:
1.648 raeburn 11227: =item * &get_env_multiple($name)
1.149 albertel 11228:
1.258 albertel 11229: gets $name from the %env hash, it seemlessly handles the cases where multiple
1.149 albertel 11230: values may be defined and end up as an array ref.
11231:
11232: returns an array of values
11233:
11234: =cut
11235:
11236: sub get_env_multiple {
11237: my ($name) = @_;
11238: my @values;
1.258 albertel 11239: if (defined($env{$name})) {
1.149 albertel 11240: # exists is it an array
1.258 albertel 11241: if (ref($env{$name})) {
11242: @values=@{ $env{$name} };
1.149 albertel 11243: } else {
1.258 albertel 11244: $values[0]=$env{$name};
1.149 albertel 11245: }
11246: }
11247: return(@values);
11248: }
11249:
1.1249 damieng 11250: # Looks at given dependencies, and returns something depending on the context.
11251: # For coursedocs paste, returns (undef, $counter, $numpathchg, \%existing).
11252: # For syllabus rewrites, returns (undef, $counter, $numpathchg, \%existing, \%mapping).
11253: # For all other contexts, returns ($output, $counter, $numpathchg).
11254: # $output: string with the HTML output. Can contain missing dependencies with an upload form, existing dependencies, and dependencies no longer in use.
11255: # $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.
11256: # $numpathchg: integer with the number of cleaned up dependency paths.
11257: # \%existing: hash reference clean path -> 1 only for existing dependencies.
11258: # \%mapping: hash reference clean path -> original path for all dependencies.
11259: # @param {string} actionurl - The path to the handler, indicative of the context.
11260: # @param {string} state - Can contain HTML with hidden inputs that will be added to the output form.
11261: # @param {hash reference} allfiles - List of file info from lonnet::extract_embedded_items
11262: # @param {hash reference} codebase - undef, not modified by lonnet::extract_embedded_items ?
11263: # @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)
11264: # @return {Array} - array depending on the context (not a reference)
1.660 raeburn 11265: sub ask_for_embedded_content {
1.1249 damieng 11266: # NOTE: documentation was added afterwards, it could be wrong
1.660 raeburn 11267: my ($actionurl,$state,$allfiles,$codebase,$args)=@_;
1.1071 raeburn 11268: my (%subdependencies,%dependencies,%mapping,%existing,%newfiles,%pathchanges,
1.1085 raeburn 11269: %currsubfile,%unused,$rem);
1.1071 raeburn 11270: my $counter = 0;
11271: my $numnew = 0;
1.987 raeburn 11272: my $numremref = 0;
11273: my $numinvalid = 0;
11274: my $numpathchg = 0;
11275: my $numexisting = 0;
1.1071 raeburn 11276: my $numunused = 0;
11277: my ($output,$upload_output,$toplevel,$url,$udom,$uname,$getpropath,$cdom,$cnum,
1.1156 raeburn 11278: $fileloc,$filename,$delete_output,$modify_output,$title,$symb,$path,$navmap);
1.1071 raeburn 11279: my $heading = &mt('Upload embedded files');
11280: my $buttontext = &mt('Upload');
11281:
1.1249 damieng 11282: # fills these variables based on the context:
11283: # $navmap, $cdom, $cnum, $udom, $uname, $url, $toplevel, $getpropath,
11284: # $path, $fileloc, $title, $rem, $filename
1.1085 raeburn 11285: if ($env{'request.course.id'}) {
1.1123 raeburn 11286: if ($actionurl eq '/adm/dependencies') {
11287: $navmap = Apache::lonnavmaps::navmap->new();
11288: }
11289: $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
11290: $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
1.1085 raeburn 11291: }
1.1123 raeburn 11292: if (($actionurl eq '/adm/portfolio') ||
11293: ($actionurl eq '/adm/coursegrp_portfolio')) {
1.984 raeburn 11294: my $current_path='/';
11295: if ($env{'form.currentpath'}) {
11296: $current_path = $env{'form.currentpath'};
11297: }
11298: if ($actionurl eq '/adm/coursegrp_portfolio') {
1.1123 raeburn 11299: $udom = $cdom;
11300: $uname = $cnum;
1.984 raeburn 11301: $url = '/userfiles/groups/'.$env{'form.group'}.'/portfolio';
11302: } else {
11303: $udom = $env{'user.domain'};
11304: $uname = $env{'user.name'};
11305: $url = '/userfiles/portfolio';
11306: }
1.987 raeburn 11307: $toplevel = $url.'/';
1.984 raeburn 11308: $url .= $current_path;
11309: $getpropath = 1;
1.987 raeburn 11310: } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank') ||
11311: ($actionurl eq '/adm/imsimport')) {
1.1022 www 11312: my ($udom,$uname,$rest) = ($args->{'current_path'} =~ m{/priv/($match_domain)/($match_username)/?(.*)$});
1.1026 raeburn 11313: $url = $Apache::lonnet::perlvar{'lonDocRoot'}."/priv/$udom/$uname/";
1.987 raeburn 11314: $toplevel = $url;
1.984 raeburn 11315: if ($rest ne '') {
1.987 raeburn 11316: $url .= $rest;
11317: }
11318: } elsif ($actionurl eq '/adm/coursedocs') {
11319: if (ref($args) eq 'HASH') {
1.1071 raeburn 11320: $url = $args->{'docs_url'};
11321: $toplevel = $url;
1.1084 raeburn 11322: if ($args->{'context'} eq 'paste') {
11323: ($cdom,$cnum) = ($url =~ m{^\Q/uploaded/\E($match_domain)/($match_courseid)/});
11324: ($path) =
11325: ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/\E(?:docs|supplemental)/(?:default|\d+)/\d+)/});
11326: $fileloc = &Apache::lonnet::filelocation('',$toplevel);
11327: $fileloc =~ s{^/}{};
11328: }
1.1071 raeburn 11329: }
1.1084 raeburn 11330: } elsif ($actionurl eq '/adm/dependencies') {
1.1071 raeburn 11331: if ($env{'request.course.id'} ne '') {
11332: if (ref($args) eq 'HASH') {
11333: $url = $args->{'docs_url'};
11334: $title = $args->{'docs_title'};
1.1126 raeburn 11335: $toplevel = $url;
11336: unless ($toplevel =~ m{^/}) {
11337: $toplevel = "/$url";
11338: }
1.1085 raeburn 11339: ($rem) = ($toplevel =~ m{^(.+/)[^/]+$});
1.1126 raeburn 11340: if ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/portfolio/syllabus\E)}) {
11341: $path = $1;
11342: } else {
11343: ($path) =
11344: ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/\E(?:docs|supplemental)/(?:default|\d+)/\d+)/});
11345: }
1.1195 raeburn 11346: if ($toplevel=~/^\/*(uploaded|editupload)/) {
11347: $fileloc = $toplevel;
11348: $fileloc=~ s/^\s*(\S+)\s*$/$1/;
11349: my ($udom,$uname,$fname) =
11350: ($fileloc=~ m{^/+(?:uploaded|editupload)/+($match_domain)/+($match_name)/+(.*)$});
11351: $fileloc = propath($udom,$uname).'/userfiles/'.$fname;
11352: } else {
11353: $fileloc = &Apache::lonnet::filelocation('',$toplevel);
11354: }
1.1071 raeburn 11355: $fileloc =~ s{^/}{};
11356: ($filename) = ($fileloc =~ m{.+/([^/]+)$});
11357: $heading = &mt('Status of dependencies in [_1]',"$title ($filename)");
11358: }
1.987 raeburn 11359: }
1.1123 raeburn 11360: } elsif ($actionurl eq "/public/$cdom/$cnum/syllabus") {
11361: $udom = $cdom;
11362: $uname = $cnum;
11363: $url = "/uploaded/$cdom/$cnum/portfolio/syllabus";
11364: $toplevel = $url;
11365: $path = $url;
11366: $fileloc = &Apache::lonnet::filelocation('',$toplevel).'/';
11367: $fileloc =~ s{^/}{};
1.987 raeburn 11368: }
1.1249 damieng 11369:
11370: # parses the dependency paths to get some info
11371: # fills $newfiles, $mapping, $subdependencies, $dependencies
11372: # $newfiles: hash URL -> 1 for new files or external URLs
11373: # (will be completed later)
11374: # $mapping:
11375: # for external URLs: external URL -> external URL
11376: # for relative paths: clean path -> original path
11377: # $subdependencies: hash clean path -> clean file name -> 1 for relative paths in subdirectories
11378: # $dependencies: hash clean or not file name -> 1 for relative paths not in subdirectories
1.1126 raeburn 11379: foreach my $file (keys(%{$allfiles})) {
11380: my $embed_file;
11381: if (($path eq "/uploaded/$cdom/$cnum/portfolio/syllabus") && ($file =~ m{^\Q$path/\E(.+)$})) {
11382: $embed_file = $1;
11383: } else {
11384: $embed_file = $file;
11385: }
1.1158 raeburn 11386: my ($absolutepath,$cleaned_file);
11387: if ($embed_file =~ m{^\w+://}) {
11388: $cleaned_file = $embed_file;
1.1147 raeburn 11389: $newfiles{$cleaned_file} = 1;
11390: $mapping{$cleaned_file} = $embed_file;
1.987 raeburn 11391: } else {
1.1158 raeburn 11392: $cleaned_file = &clean_path($embed_file);
1.987 raeburn 11393: if ($embed_file =~ m{^/}) {
11394: $absolutepath = $embed_file;
11395: }
1.1147 raeburn 11396: if ($cleaned_file =~ m{/}) {
11397: my ($path,$fname) = ($cleaned_file =~ m{^(.+)/([^/]*)$});
1.987 raeburn 11398: $path = &check_for_traversal($path,$url,$toplevel);
11399: my $item = $fname;
11400: if ($path ne '') {
11401: $item = $path.'/'.$fname;
11402: $subdependencies{$path}{$fname} = 1;
11403: } else {
11404: $dependencies{$item} = 1;
11405: }
11406: if ($absolutepath) {
11407: $mapping{$item} = $absolutepath;
11408: } else {
11409: $mapping{$item} = $embed_file;
11410: }
11411: } else {
11412: $dependencies{$embed_file} = 1;
11413: if ($absolutepath) {
1.1147 raeburn 11414: $mapping{$cleaned_file} = $absolutepath;
1.987 raeburn 11415: } else {
1.1147 raeburn 11416: $mapping{$cleaned_file} = $embed_file;
1.987 raeburn 11417: }
11418: }
1.984 raeburn 11419: }
11420: }
1.1249 damieng 11421:
11422: # looks for all existing files in dependency subdirectories (from $subdependencies filled above)
11423: # and lists
11424: # fills $currsubfile, $pathchanges, $existing, $numexisting, $newfiles, $unused
11425: # $currsubfile: hash clean path -> file name -> 1 for all existing files in the path
11426: # $pathchanges: hash clean path -> 1 if the file in subdirectory exists and
11427: # the path had to be cleaned up
11428: # $existing: hash clean path -> 1 if the file exists
11429: # $numexisting: number of keys in $existing
11430: # $newfiles: updated with clean path -> 1 for files in subdirectories that do not exist
11431: # $unused: only for /adm/dependencies, hash clean path -> 1 for existing files in
11432: # dependency subdirectories that are
11433: # not listed as dependencies, with some exceptions using $rem
1.1071 raeburn 11434: my $dirptr = 16384;
1.984 raeburn 11435: foreach my $path (keys(%subdependencies)) {
1.1071 raeburn 11436: $currsubfile{$path} = {};
1.1123 raeburn 11437: if (($actionurl eq '/adm/portfolio') ||
11438: ($actionurl eq '/adm/coursegrp_portfolio')) {
1.1021 raeburn 11439: my ($sublistref,$listerror) =
11440: &Apache::lonnet::dirlist($url.$path,$udom,$uname,$getpropath);
11441: if (ref($sublistref) eq 'ARRAY') {
11442: foreach my $line (@{$sublistref}) {
11443: my ($file_name,$rest) = split(/\&/,$line,2);
1.1071 raeburn 11444: $currsubfile{$path}{$file_name} = 1;
1.1021 raeburn 11445: }
1.984 raeburn 11446: }
1.987 raeburn 11447: } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
1.984 raeburn 11448: if (opendir(my $dir,$url.'/'.$path)) {
11449: my @subdir_list = grep(!/^\./,readdir($dir));
1.1071 raeburn 11450: map {$currsubfile{$path}{$_} = 1;} @subdir_list;
11451: }
1.1084 raeburn 11452: } elsif (($actionurl eq '/adm/dependencies') ||
11453: (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
1.1123 raeburn 11454: ($args->{'context'} eq 'paste')) ||
11455: ($actionurl eq "/public/$cdom/$cnum/syllabus")) {
1.1071 raeburn 11456: if ($env{'request.course.id'} ne '') {
1.1123 raeburn 11457: my $dir;
11458: if ($actionurl eq "/public/$cdom/$cnum/syllabus") {
11459: $dir = $fileloc;
11460: } else {
11461: ($dir) = ($fileloc =~ m{^(.+/)[^/]+$});
11462: }
1.1071 raeburn 11463: if ($dir ne '') {
11464: my ($sublistref,$listerror) =
11465: &Apache::lonnet::dirlist($dir.$path,$cdom,$cnum,$getpropath,undef,'/');
11466: if (ref($sublistref) eq 'ARRAY') {
11467: foreach my $line (@{$sublistref}) {
11468: my ($file_name,$dom,undef,$testdir,undef,undef,undef,undef,$size,
11469: undef,$mtime)=split(/\&/,$line,12);
11470: unless (($testdir&$dirptr) ||
11471: ($file_name =~ /^\.\.?$/)) {
11472: $currsubfile{$path}{$file_name} = [$size,$mtime];
11473: }
11474: }
11475: }
11476: }
1.984 raeburn 11477: }
11478: }
11479: foreach my $file (keys(%{$subdependencies{$path}})) {
1.1071 raeburn 11480: if (exists($currsubfile{$path}{$file})) {
1.987 raeburn 11481: my $item = $path.'/'.$file;
11482: unless ($mapping{$item} eq $item) {
11483: $pathchanges{$item} = 1;
11484: }
11485: $existing{$item} = 1;
11486: $numexisting ++;
11487: } else {
11488: $newfiles{$path.'/'.$file} = 1;
1.984 raeburn 11489: }
11490: }
1.1071 raeburn 11491: if ($actionurl eq '/adm/dependencies') {
11492: foreach my $path (keys(%currsubfile)) {
11493: if (ref($currsubfile{$path}) eq 'HASH') {
11494: foreach my $file (keys(%{$currsubfile{$path}})) {
11495: unless ($subdependencies{$path}{$file}) {
1.1085 raeburn 11496: next if (($rem ne '') &&
11497: (($env{"httpref.$rem"."$path/$file"} ne '') ||
11498: (ref($navmap) &&
11499: (($navmap->getResourceByUrl($rem."$path/$file") ne '') ||
11500: (($file =~ /^(.*\.s?html?)\.bak$/i) &&
11501: ($navmap->getResourceByUrl($rem."$path/$1")))))));
1.1071 raeburn 11502: $unused{$path.'/'.$file} = 1;
11503: }
11504: }
11505: }
11506: }
11507: }
1.984 raeburn 11508: }
1.1249 damieng 11509:
11510: # fills $currfile, hash file name -> 1 or [$size,$mtime]
11511: # for files in $url or $fileloc (target directory) in some contexts
1.987 raeburn 11512: my %currfile;
1.1123 raeburn 11513: if (($actionurl eq '/adm/portfolio') ||
11514: ($actionurl eq '/adm/coursegrp_portfolio')) {
1.1021 raeburn 11515: my ($dirlistref,$listerror) =
11516: &Apache::lonnet::dirlist($url,$udom,$uname,$getpropath);
11517: if (ref($dirlistref) eq 'ARRAY') {
11518: foreach my $line (@{$dirlistref}) {
11519: my ($file_name,$rest) = split(/\&/,$line,2);
11520: $currfile{$file_name} = 1;
11521: }
1.984 raeburn 11522: }
1.987 raeburn 11523: } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
1.984 raeburn 11524: if (opendir(my $dir,$url)) {
1.987 raeburn 11525: my @dir_list = grep(!/^\./,readdir($dir));
1.984 raeburn 11526: map {$currfile{$_} = 1;} @dir_list;
11527: }
1.1084 raeburn 11528: } elsif (($actionurl eq '/adm/dependencies') ||
11529: (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
1.1123 raeburn 11530: ($args->{'context'} eq 'paste')) ||
11531: ($actionurl eq "/public/$cdom/$cnum/syllabus")) {
1.1071 raeburn 11532: if ($env{'request.course.id'} ne '') {
11533: my ($dir) = ($fileloc =~ m{^(.+/)[^/]+$});
11534: if ($dir ne '') {
11535: my ($dirlistref,$listerror) =
11536: &Apache::lonnet::dirlist($dir,$cdom,$cnum,$getpropath,undef,'/');
11537: if (ref($dirlistref) eq 'ARRAY') {
11538: foreach my $line (@{$dirlistref}) {
11539: my ($file_name,$dom,undef,$testdir,undef,undef,undef,undef,
11540: $size,undef,$mtime)=split(/\&/,$line,12);
11541: unless (($testdir&$dirptr) ||
11542: ($file_name =~ /^\.\.?$/)) {
11543: $currfile{$file_name} = [$size,$mtime];
11544: }
11545: }
11546: }
11547: }
11548: }
1.984 raeburn 11549: }
1.1249 damieng 11550: # updates $pathchanges, $existing, $numexisting, $newfiles and $unused for files that
11551: # are not in subdirectories, using $currfile
1.984 raeburn 11552: foreach my $file (keys(%dependencies)) {
1.1071 raeburn 11553: if (exists($currfile{$file})) {
1.987 raeburn 11554: unless ($mapping{$file} eq $file) {
11555: $pathchanges{$file} = 1;
11556: }
11557: $existing{$file} = 1;
11558: $numexisting ++;
11559: } else {
1.984 raeburn 11560: $newfiles{$file} = 1;
11561: }
11562: }
1.1071 raeburn 11563: foreach my $file (keys(%currfile)) {
11564: unless (($file eq $filename) ||
11565: ($file eq $filename.'.bak') ||
11566: ($dependencies{$file})) {
1.1085 raeburn 11567: if ($actionurl eq '/adm/dependencies') {
1.1126 raeburn 11568: unless ($toplevel =~ m{^\Q/uploaded/$cdom/$cnum/portfolio/syllabus\E}) {
11569: next if (($rem ne '') &&
11570: (($env{"httpref.$rem".$file} ne '') ||
11571: (ref($navmap) &&
11572: (($navmap->getResourceByUrl($rem.$file) ne '') ||
11573: (($file =~ /^(.*\.s?html?)\.bak$/i) &&
11574: ($navmap->getResourceByUrl($rem.$1)))))));
11575: }
1.1085 raeburn 11576: }
1.1071 raeburn 11577: $unused{$file} = 1;
11578: }
11579: }
1.1249 damieng 11580:
11581: # returns some results for coursedocs paste and syllabus rewrites ($output is undef)
1.1084 raeburn 11582: if (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
11583: ($args->{'context'} eq 'paste')) {
11584: $counter = scalar(keys(%existing));
11585: $numpathchg = scalar(keys(%pathchanges));
1.1123 raeburn 11586: return ($output,$counter,$numpathchg,\%existing);
11587: } elsif (($actionurl eq "/public/$cdom/$cnum/syllabus") &&
11588: (ref($args) eq 'HASH') && ($args->{'context'} eq 'rewrites')) {
11589: $counter = scalar(keys(%existing));
11590: $numpathchg = scalar(keys(%pathchanges));
11591: return ($output,$counter,$numpathchg,\%existing,\%mapping);
1.1084 raeburn 11592: }
1.1249 damieng 11593:
11594: # returns HTML otherwise, with dependency results and to ask for more uploads
11595:
11596: # $upload_output: missing dependencies (with upload form)
11597: # $modify_output: uploaded dependencies (in use)
11598: # $delete_output: files no longer in use (unused files are not listed for londocs, bug?)
1.984 raeburn 11599: foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%newfiles)) {
1.1071 raeburn 11600: if ($actionurl eq '/adm/dependencies') {
11601: next if ($embed_file =~ m{^\w+://});
11602: }
1.660 raeburn 11603: $upload_output .= &start_data_table_row().
1.1123 raeburn 11604: '<td valign="top"><img src="'.&icon($embed_file).'" /> '.
1.1071 raeburn 11605: '<span class="LC_filename">'.$embed_file.'</span>';
1.987 raeburn 11606: unless ($mapping{$embed_file} eq $embed_file) {
1.1123 raeburn 11607: $upload_output .= '<br /><span class="LC_info" style="font-size:smaller;">'.
11608: &mt('changed from: [_1]',$mapping{$embed_file}).'</span>';
1.987 raeburn 11609: }
1.1123 raeburn 11610: $upload_output .= '</td>';
1.1071 raeburn 11611: if ($args->{'ignore_remote_references'} && $embed_file =~ m{^\w+://}) {
1.1123 raeburn 11612: $upload_output.='<td align="right">'.
11613: '<span class="LC_info LC_fontsize_medium">'.
11614: &mt("URL points to web address").'</span>';
1.987 raeburn 11615: $numremref++;
1.660 raeburn 11616: } elsif ($args->{'error_on_invalid_names'}
11617: && $embed_file ne &Apache::lonnet::clean_filename($embed_file,{'keep_path' => 1,})) {
1.1123 raeburn 11618: $upload_output.='<td align="right"><span class="LC_warning">'.
11619: &mt('Invalid characters').'</span>';
1.987 raeburn 11620: $numinvalid++;
1.660 raeburn 11621: } else {
1.1123 raeburn 11622: $upload_output .= '<td>'.
11623: &embedded_file_element('upload_embedded',$counter,
1.987 raeburn 11624: $embed_file,\%mapping,
1.1071 raeburn 11625: $allfiles,$codebase,'upload');
11626: $counter ++;
11627: $numnew ++;
1.987 raeburn 11628: }
11629: $upload_output .= '</td>'.&Apache::loncommon::end_data_table_row()."\n";
11630: }
11631: foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%existing)) {
1.1071 raeburn 11632: if ($actionurl eq '/adm/dependencies') {
11633: my ($size,$mtime) = &get_dependency_details(\%currfile,\%currsubfile,$embed_file);
11634: $modify_output .= &start_data_table_row().
11635: '<td><a href="'.$path.'/'.$embed_file.'" style="text-decoration:none;">'.
11636: '<img src="'.&icon($embed_file).'" border="0" />'.
11637: ' <span class="LC_filename">'.$embed_file.'</span></a></td>'.
11638: '<td>'.$size.'</td>'.
11639: '<td>'.$mtime.'</td>'.
11640: '<td><label><input type="checkbox" name="mod_upload_dep" '.
11641: 'onclick="toggleBrowse('."'$counter'".')" id="mod_upload_dep_'.
11642: $counter.'" value="'.$counter.'" />'.&mt('Yes').'</label>'.
11643: '<div id="moduploaddep_'.$counter.'" style="display:none;">'.
11644: &embedded_file_element('upload_embedded',$counter,
11645: $embed_file,\%mapping,
11646: $allfiles,$codebase,'modify').
11647: '</div></td>'.
11648: &end_data_table_row()."\n";
11649: $counter ++;
11650: } else {
11651: $upload_output .= &start_data_table_row().
1.1123 raeburn 11652: '<td valign="top"><img src="'.&icon($embed_file).'" /> '.
11653: '<span class="LC_filename">'.$embed_file.'</span></td>'.
11654: '<td align="right"><span class="LC_info LC_fontsize_medium">'.&mt('Already exists').'</span></td>'.
1.1071 raeburn 11655: &Apache::loncommon::end_data_table_row()."\n";
11656: }
11657: }
11658: my $delidx = $counter;
11659: foreach my $oldfile (sort {lc($a) cmp lc($b)} keys(%unused)) {
11660: my ($size,$mtime) = &get_dependency_details(\%currfile,\%currsubfile,$oldfile);
11661: $delete_output .= &start_data_table_row().
11662: '<td><img src="'.&icon($oldfile).'" />'.
11663: ' <span class="LC_filename">'.$oldfile.'</span></td>'.
11664: '<td>'.$size.'</td>'.
11665: '<td>'.$mtime.'</td>'.
11666: '<td><label><input type="checkbox" name="del_upload_dep" '.
11667: ' value="'.$delidx.'" />'.&mt('Yes').'</label>'.
11668: &embedded_file_element('upload_embedded',$delidx,
11669: $oldfile,\%mapping,$allfiles,
11670: $codebase,'delete').'</td>'.
11671: &end_data_table_row()."\n";
11672: $numunused ++;
11673: $delidx ++;
1.987 raeburn 11674: }
11675: if ($upload_output) {
11676: $upload_output = &start_data_table().
11677: $upload_output.
11678: &end_data_table()."\n";
11679: }
1.1071 raeburn 11680: if ($modify_output) {
11681: $modify_output = &start_data_table().
11682: &start_data_table_header_row().
11683: '<th>'.&mt('File').'</th>'.
11684: '<th>'.&mt('Size (KB)').'</th>'.
11685: '<th>'.&mt('Modified').'</th>'.
11686: '<th>'.&mt('Upload replacement?').'</th>'.
11687: &end_data_table_header_row().
11688: $modify_output.
11689: &end_data_table()."\n";
11690: }
11691: if ($delete_output) {
11692: $delete_output = &start_data_table().
11693: &start_data_table_header_row().
11694: '<th>'.&mt('File').'</th>'.
11695: '<th>'.&mt('Size (KB)').'</th>'.
11696: '<th>'.&mt('Modified').'</th>'.
11697: '<th>'.&mt('Delete?').'</th>'.
11698: &end_data_table_header_row().
11699: $delete_output.
11700: &end_data_table()."\n";
11701: }
1.987 raeburn 11702: my $applies = 0;
11703: if ($numremref) {
11704: $applies ++;
11705: }
11706: if ($numinvalid) {
11707: $applies ++;
11708: }
11709: if ($numexisting) {
11710: $applies ++;
11711: }
1.1071 raeburn 11712: if ($counter || $numunused) {
1.987 raeburn 11713: $output = '<form name="upload_embedded" action="'.$actionurl.'"'.
11714: ' method="post" enctype="multipart/form-data">'."\n".
1.1071 raeburn 11715: $state.'<h3>'.$heading.'</h3>';
11716: if ($actionurl eq '/adm/dependencies') {
11717: if ($numnew) {
11718: $output .= '<h4>'.&mt('Missing dependencies').'</h4>'.
11719: '<p>'.&mt('The following files need to be uploaded.').'</p>'."\n".
11720: $upload_output.'<br />'."\n";
11721: }
11722: if ($numexisting) {
11723: $output .= '<h4>'.&mt('Uploaded dependencies (in use)').'</h4>'.
11724: '<p>'.&mt('Upload a new file to replace the one currently in use.').'</p>'."\n".
11725: $modify_output.'<br />'."\n";
11726: $buttontext = &mt('Save changes');
11727: }
11728: if ($numunused) {
11729: $output .= '<h4>'.&mt('Unused files').'</h4>'.
11730: '<p>'.&mt('The following uploaded files are no longer used.').'</p>'."\n".
11731: $delete_output.'<br />'."\n";
11732: $buttontext = &mt('Save changes');
11733: }
11734: } else {
11735: $output .= $upload_output.'<br />'."\n";
11736: }
11737: $output .= '<input type ="hidden" name="number_embedded_items" value="'.
11738: $counter.'" />'."\n";
11739: if ($actionurl eq '/adm/dependencies') {
11740: $output .= '<input type ="hidden" name="number_newemb_items" value="'.
11741: $numnew.'" />'."\n";
11742: } elsif ($actionurl eq '') {
1.987 raeburn 11743: $output .= '<input type="hidden" name="phase" value="three" />';
11744: }
11745: } elsif ($applies) {
11746: $output = '<b>'.&mt('Referenced files').'</b>:<br />';
11747: if ($applies > 1) {
11748: $output .=
1.1123 raeburn 11749: &mt('No dependencies need to be uploaded, as one of the following applies to each reference:').'<ul>';
1.987 raeburn 11750: if ($numremref) {
11751: $output .= '<li>'.&mt('reference is to a URL which points to another server').'</li>'."\n";
11752: }
11753: if ($numinvalid) {
11754: $output .= '<li>'.&mt('reference is to file with a name containing invalid characters').'</li>'."\n";
11755: }
11756: if ($numexisting) {
11757: $output .= '<li>'.&mt('reference is to an existing file at the specified location').'</li>'."\n";
11758: }
11759: $output .= '</ul><br />';
11760: } elsif ($numremref) {
11761: $output .= '<p>'.&mt('None to upload, as all references are to URLs pointing to another server.').'</p>';
11762: } elsif ($numinvalid) {
11763: $output .= '<p>'.&mt('None to upload, as all references are to files with names containing invalid characters.').'</p>';
11764: } elsif ($numexisting) {
11765: $output .= '<p>'.&mt('None to upload, as all references are to existing files.').'</p>';
11766: }
11767: $output .= $upload_output.'<br />';
11768: }
11769: my ($pathchange_output,$chgcount);
1.1071 raeburn 11770: $chgcount = $counter;
1.987 raeburn 11771: if (keys(%pathchanges) > 0) {
11772: foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%pathchanges)) {
1.1071 raeburn 11773: if ($counter) {
1.987 raeburn 11774: $output .= &embedded_file_element('pathchange',$chgcount,
11775: $embed_file,\%mapping,
1.1071 raeburn 11776: $allfiles,$codebase,'change');
1.987 raeburn 11777: } else {
11778: $pathchange_output .=
11779: &start_data_table_row().
11780: '<td><input type ="checkbox" name="namechange" value="'.
11781: $chgcount.'" checked="checked" /></td>'.
11782: '<td>'.$mapping{$embed_file}.'</td>'.
11783: '<td>'.$embed_file.
11784: &embedded_file_element('pathchange',$numpathchg,$embed_file,
1.1071 raeburn 11785: \%mapping,$allfiles,$codebase,'change').
1.987 raeburn 11786: '</td>'.&end_data_table_row();
1.660 raeburn 11787: }
1.987 raeburn 11788: $numpathchg ++;
11789: $chgcount ++;
1.660 raeburn 11790: }
11791: }
1.1127 raeburn 11792: if (($counter) || ($numunused)) {
1.987 raeburn 11793: if ($numpathchg) {
11794: $output .= '<input type ="hidden" name="number_pathchange_items" value="'.
11795: $numpathchg.'" />'."\n";
11796: }
11797: if (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank') ||
11798: ($actionurl eq '/adm/imsimport')) {
11799: $output .= '<input type="hidden" name="phase" value="three" />'."\n";
11800: } elsif ($actionurl eq '/adm/portfolio' || $actionurl eq '/adm/coursegrp_portfolio') {
11801: $output .= '<input type="hidden" name="action" value="upload_embedded" />';
1.1071 raeburn 11802: } elsif ($actionurl eq '/adm/dependencies') {
11803: $output .= '<input type="hidden" name="action" value="process_changes" />';
1.987 raeburn 11804: }
1.1123 raeburn 11805: $output .= '<input type ="submit" value="'.$buttontext.'" />'."\n".'</form>'."\n";
1.987 raeburn 11806: } elsif ($numpathchg) {
11807: my %pathchange = ();
11808: $output .= &modify_html_form('pathchange',$actionurl,$state,\%pathchange,$pathchange_output);
11809: if (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
11810: $output .= '<p>'.&mt('or').'</p>';
1.1123 raeburn 11811: }
1.987 raeburn 11812: }
1.1071 raeburn 11813: return ($output,$counter,$numpathchg);
1.987 raeburn 11814: }
11815:
1.1147 raeburn 11816: =pod
11817:
11818: =item * clean_path($name)
11819:
11820: Performs clean-up of directories, subdirectories and filename in an
11821: embedded object, referenced in an HTML file which is being uploaded
11822: to a course or portfolio, where
11823: "Upload embedded images/multimedia files if HTML file" checkbox was
11824: checked.
11825:
11826: Clean-up is similar to replacements in lonnet::clean_filename()
11827: except each / between sub-directory and next level is preserved.
11828:
11829: =cut
11830:
11831: sub clean_path {
11832: my ($embed_file) = @_;
11833: $embed_file =~s{^/+}{};
11834: my @contents;
11835: if ($embed_file =~ m{/}) {
11836: @contents = split(/\//,$embed_file);
11837: } else {
11838: @contents = ($embed_file);
11839: }
11840: my $lastidx = scalar(@contents)-1;
11841: for (my $i=0; $i<=$lastidx; $i++) {
11842: $contents[$i]=~s{\\}{/}g;
11843: $contents[$i]=~s/\s+/\_/g;
11844: $contents[$i]=~s{[^/\w\.\-]}{}g;
11845: if ($i == $lastidx) {
11846: $contents[$i]=~s/\.(\d+)(?=\.)/_$1/g;
11847: }
11848: }
11849: if ($lastidx > 0) {
11850: return join('/',@contents);
11851: } else {
11852: return $contents[0];
11853: }
11854: }
11855:
1.987 raeburn 11856: sub embedded_file_element {
1.1071 raeburn 11857: my ($context,$num,$embed_file,$mapping,$allfiles,$codebase,$type) = @_;
1.987 raeburn 11858: return unless ((ref($mapping) eq 'HASH') && (ref($allfiles) eq 'HASH') &&
11859: (ref($codebase) eq 'HASH'));
11860: my $output;
1.1071 raeburn 11861: if (($context eq 'upload_embedded') && ($type ne 'delete')) {
1.987 raeburn 11862: $output = '<input name="embedded_item_'.$num.'" type="file" value="" />'."\n";
11863: }
11864: $output .= '<input name="embedded_orig_'.$num.'" type="hidden" value="'.
11865: &escape($embed_file).'" />';
11866: unless (($context eq 'upload_embedded') &&
11867: ($mapping->{$embed_file} eq $embed_file)) {
11868: $output .='
11869: <input name="embedded_ref_'.$num.'" type="hidden" value="'.&escape($mapping->{$embed_file}).'" />';
11870: }
11871: my $attrib;
11872: if (ref($allfiles->{$mapping->{$embed_file}}) eq 'ARRAY') {
11873: $attrib = &escape(join(':',@{$allfiles->{$mapping->{$embed_file}}}));
11874: }
11875: $output .=
11876: "\n\t\t".
11877: '<input name="embedded_attrib_'.$num.'" type="hidden" value="'.
11878: $attrib.'" />';
11879: if (exists($codebase->{$mapping->{$embed_file}})) {
11880: $output .=
11881: "\n\t\t".
11882: '<input name="codebase_'.$num.'" type="hidden" value="'.
11883: &escape($codebase->{$mapping->{$embed_file}}).'" />';
1.984 raeburn 11884: }
1.987 raeburn 11885: return $output;
1.660 raeburn 11886: }
11887:
1.1071 raeburn 11888: sub get_dependency_details {
11889: my ($currfile,$currsubfile,$embed_file) = @_;
11890: my ($size,$mtime,$showsize,$showmtime);
11891: if ((ref($currfile) eq 'HASH') && (ref($currsubfile))) {
11892: if ($embed_file =~ m{/}) {
11893: my ($path,$fname) = split(/\//,$embed_file);
11894: if (ref($currsubfile->{$path}{$fname}) eq 'ARRAY') {
11895: ($size,$mtime) = @{$currsubfile->{$path}{$fname}};
11896: }
11897: } else {
11898: if (ref($currfile->{$embed_file}) eq 'ARRAY') {
11899: ($size,$mtime) = @{$currfile->{$embed_file}};
11900: }
11901: }
11902: $showsize = $size/1024.0;
11903: $showsize = sprintf("%.1f",$showsize);
11904: if ($mtime > 0) {
11905: $showmtime = &Apache::lonlocal::locallocaltime($mtime);
11906: }
11907: }
11908: return ($showsize,$showmtime);
11909: }
11910:
11911: sub ask_embedded_js {
11912: return <<"END";
11913: <script type="text/javascript"">
11914: // <![CDATA[
11915: function toggleBrowse(counter) {
11916: var chkboxid = document.getElementById('mod_upload_dep_'+counter);
11917: var fileid = document.getElementById('embedded_item_'+counter);
11918: var uploaddivid = document.getElementById('moduploaddep_'+counter);
11919: if (chkboxid.checked == true) {
11920: uploaddivid.style.display='block';
11921: } else {
11922: uploaddivid.style.display='none';
11923: fileid.value = '';
11924: }
11925: }
11926: // ]]>
11927: </script>
11928:
11929: END
11930: }
11931:
1.661 raeburn 11932: sub upload_embedded {
11933: my ($context,$dirpath,$uname,$udom,$dir_root,$url_root,$group,$disk_quota,
1.987 raeburn 11934: $current_disk_usage,$hiddenstate,$actionurl) = @_;
11935: my (%pathchange,$output,$modifyform,$footer,$returnflag);
1.661 raeburn 11936: for (my $i=0; $i<$env{'form.number_embedded_items'}; $i++) {
11937: next if (!exists($env{'form.embedded_item_'.$i.'.filename'}));
11938: my $orig_uploaded_filename =
11939: $env{'form.embedded_item_'.$i.'.filename'};
1.987 raeburn 11940: foreach my $type ('orig','ref','attrib','codebase') {
11941: if ($env{'form.embedded_'.$type.'_'.$i} ne '') {
11942: $env{'form.embedded_'.$type.'_'.$i} =
11943: &unescape($env{'form.embedded_'.$type.'_'.$i});
11944: }
11945: }
1.661 raeburn 11946: my ($path,$fname) =
11947: ($env{'form.embedded_orig_'.$i} =~ m{(.*/)([^/]*)});
11948: # no path, whole string is fname
11949: if (!$fname) { $fname = $env{'form.embedded_orig_'.$i} };
11950: $fname = &Apache::lonnet::clean_filename($fname);
11951: # See if there is anything left
11952: next if ($fname eq '');
11953:
11954: # Check if file already exists as a file or directory.
11955: my ($state,$msg);
11956: if ($context eq 'portfolio') {
11957: my $port_path = $dirpath;
11958: if ($group ne '') {
11959: $port_path = "groups/$group/$port_path";
11960: }
1.987 raeburn 11961: ($state,$msg) = &check_for_upload($env{'form.currentpath'}.$path,
11962: $fname,$group,'embedded_item_'.$i,
1.661 raeburn 11963: $dir_root,$port_path,$disk_quota,
11964: $current_disk_usage,$uname,$udom);
11965: if ($state eq 'will_exceed_quota'
1.984 raeburn 11966: || $state eq 'file_locked') {
1.661 raeburn 11967: $output .= $msg;
11968: next;
11969: }
11970: } elsif (($context eq 'author') || ($context eq 'testbank')) {
11971: ($state,$msg) = &check_for_existing($path,$fname,'embedded_item_'.$i);
11972: if ($state eq 'exists') {
11973: $output .= $msg;
11974: next;
11975: }
11976: }
11977: # Check if extension is valid
11978: if (($fname =~ /\.(\w+)$/) &&
11979: (&Apache::loncommon::fileembstyle($1) eq 'hdn')) {
1.1155 bisitz 11980: $output .= &mt('Invalid file extension ([_1]) - reserved for internal use.',$1)
11981: .' '.&mt('Rename the file with a different extension and re-upload.').'<br />';
1.661 raeburn 11982: next;
11983: } elsif (($fname =~ /\.(\w+)$/) &&
11984: (!defined(&Apache::loncommon::fileembstyle($1)))) {
1.987 raeburn 11985: $output .= &mt('Unrecognized file extension ([_1]) - rename the file with a proper extension and re-upload.',$1).'<br />';
1.661 raeburn 11986: next;
11987: } elsif ($fname=~/\.(\d+)\.(\w+)$/) {
1.1120 bisitz 11988: $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 11989: next;
11990: }
11991: $env{'form.embedded_item_'.$i.'.filename'}=$fname;
1.1123 raeburn 11992: my $subdir = $path;
11993: $subdir =~ s{/+$}{};
1.661 raeburn 11994: if ($context eq 'portfolio') {
1.984 raeburn 11995: my $result;
11996: if ($state eq 'existingfile') {
11997: $result=
11998: &Apache::lonnet::userfileupload('embedded_item_'.$i,'existingfile',
1.1123 raeburn 11999: $dirpath.$env{'form.currentpath'}.$subdir);
1.661 raeburn 12000: } else {
1.984 raeburn 12001: $result=
12002: &Apache::lonnet::userfileupload('embedded_item_'.$i,'',
1.987 raeburn 12003: $dirpath.
1.1123 raeburn 12004: $env{'form.currentpath'}.$subdir);
1.984 raeburn 12005: if ($result !~ m|^/uploaded/|) {
12006: $output .= '<span class="LC_error">'
12007: .&mt('An error occurred ([_1]) while trying to upload [_2] for embedded element [_3].'
12008: ,$result,$orig_uploaded_filename,$env{'form.embedded_orig_'.$i})
12009: .'</span><br />';
12010: next;
12011: } else {
1.987 raeburn 12012: $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
12013: $path.$fname.'</span>').'<br />';
1.984 raeburn 12014: }
1.661 raeburn 12015: }
1.1123 raeburn 12016: } elsif (($context eq 'coursedoc') || ($context eq 'syllabus')) {
1.1126 raeburn 12017: my $extendedsubdir = $dirpath.'/'.$subdir;
12018: $extendedsubdir =~ s{/+$}{};
1.987 raeburn 12019: my $result =
1.1126 raeburn 12020: &Apache::lonnet::userfileupload('embedded_item_'.$i,$context,$extendedsubdir);
1.987 raeburn 12021: if ($result !~ m|^/uploaded/|) {
12022: $output .= '<span class="LC_error">'
12023: .&mt('An error occurred ([_1]) while trying to upload [_2] for embedded element [_3].'
12024: ,$result,$orig_uploaded_filename,$env{'form.embedded_orig_'.$i})
12025: .'</span><br />';
12026: next;
12027: } else {
12028: $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
12029: $path.$fname.'</span>').'<br />';
1.1125 raeburn 12030: if ($context eq 'syllabus') {
12031: &Apache::lonnet::make_public_indefinitely($result);
12032: }
1.987 raeburn 12033: }
1.661 raeburn 12034: } else {
12035: # Save the file
12036: my $target = $env{'form.embedded_item_'.$i};
12037: my $fullpath = $dir_root.$dirpath.'/'.$path;
12038: my $dest = $fullpath.$fname;
12039: my $url = $url_root.$dirpath.'/'.$path.$fname;
1.1027 raeburn 12040: my @parts=split(/\//,"$dirpath/$path");
1.661 raeburn 12041: my $count;
12042: my $filepath = $dir_root;
1.1027 raeburn 12043: foreach my $subdir (@parts) {
12044: $filepath .= "/$subdir";
12045: if (!-e $filepath) {
1.661 raeburn 12046: mkdir($filepath,0770);
12047: }
12048: }
12049: my $fh;
12050: if (!open($fh,'>'.$dest)) {
12051: &Apache::lonnet::logthis('Failed to create '.$dest);
12052: $output .= '<span class="LC_error">'.
1.1071 raeburn 12053: &mt('An error occurred while trying to upload [_1] for embedded element [_2].',
12054: $orig_uploaded_filename,$env{'form.embedded_orig_'.$i}).
1.661 raeburn 12055: '</span><br />';
12056: } else {
12057: if (!print $fh $env{'form.embedded_item_'.$i}) {
12058: &Apache::lonnet::logthis('Failed to write to '.$dest);
12059: $output .= '<span class="LC_error">'.
1.1071 raeburn 12060: &mt('An error occurred while writing the file [_1] for embedded element [_2].',
12061: $orig_uploaded_filename,$env{'form.embedded_orig_'.$i}).
1.661 raeburn 12062: '</span><br />';
12063: } else {
1.987 raeburn 12064: $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
12065: $url.'</span>').'<br />';
12066: unless ($context eq 'testbank') {
12067: $footer .= &mt('View embedded file: [_1]',
12068: '<a href="'.$url.'">'.$fname.'</a>').'<br />';
12069: }
12070: }
12071: close($fh);
12072: }
12073: }
12074: if ($env{'form.embedded_ref_'.$i}) {
12075: $pathchange{$i} = 1;
12076: }
12077: }
12078: if ($output) {
12079: $output = '<p>'.$output.'</p>';
12080: }
12081: $output .= &modify_html_form('upload_embedded',$actionurl,$hiddenstate,\%pathchange);
12082: $returnflag = 'ok';
1.1071 raeburn 12083: my $numpathchgs = scalar(keys(%pathchange));
12084: if ($numpathchgs > 0) {
1.987 raeburn 12085: if ($context eq 'portfolio') {
12086: $output .= '<p>'.&mt('or').'</p>';
12087: } elsif ($context eq 'testbank') {
1.1071 raeburn 12088: $output .= '<p>'.&mt('Or [_1]continue[_2] the testbank import without modifying the reference(s).',
12089: '<a href="javascript:document.testbankForm.submit();">','</a>').'</p>';
1.987 raeburn 12090: $returnflag = 'modify_orightml';
12091: }
12092: }
1.1071 raeburn 12093: return ($output.$footer,$returnflag,$numpathchgs);
1.987 raeburn 12094: }
12095:
12096: sub modify_html_form {
12097: my ($context,$actionurl,$hiddenstate,$pathchange,$pathchgtable) = @_;
12098: my $end = 0;
12099: my $modifyform;
12100: if ($context eq 'upload_embedded') {
12101: return unless (ref($pathchange) eq 'HASH');
12102: if ($env{'form.number_embedded_items'}) {
12103: $end += $env{'form.number_embedded_items'};
12104: }
12105: if ($env{'form.number_pathchange_items'}) {
12106: $end += $env{'form.number_pathchange_items'};
12107: }
12108: if ($end) {
12109: for (my $i=0; $i<$end; $i++) {
12110: if ($i < $env{'form.number_embedded_items'}) {
12111: next unless($pathchange->{$i});
12112: }
12113: $modifyform .=
12114: &start_data_table_row().
12115: '<td><input type ="checkbox" name="namechange" value="'.$i.'" '.
12116: 'checked="checked" /></td>'.
12117: '<td>'.$env{'form.embedded_ref_'.$i}.
12118: '<input type="hidden" name="embedded_ref_'.$i.'" value="'.
12119: &escape($env{'form.embedded_ref_'.$i}).'" />'.
12120: '<input type="hidden" name="embedded_codebase_'.$i.'" value="'.
12121: &escape($env{'form.embedded_codebase_'.$i}).'" />'.
12122: '<input type="hidden" name="embedded_attrib_'.$i.'" value="'.
12123: &escape($env{'form.embedded_attrib_'.$i}).'" /></td>'.
12124: '<td>'.$env{'form.embedded_orig_'.$i}.
12125: '<input type="hidden" name="embedded_orig_'.$i.'" value="'.
12126: &escape($env{'form.embedded_orig_'.$i}).'" /></td>'.
12127: &end_data_table_row();
1.1071 raeburn 12128: }
1.987 raeburn 12129: }
12130: } else {
12131: $modifyform = $pathchgtable;
12132: if (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
12133: $hiddenstate .= '<input type="hidden" name="phase" value="four" />';
12134: } elsif (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
12135: $hiddenstate .= '<input type="hidden" name="action" value="modify_orightml" />';
12136: }
12137: }
12138: if ($modifyform) {
1.1071 raeburn 12139: if ($actionurl eq '/adm/dependencies') {
12140: $hiddenstate .= '<input type="hidden" name="action" value="modifyhrefs" />';
12141: }
1.987 raeburn 12142: return '<h3>'.&mt('Changes in content of HTML file required').'</h3>'."\n".
12143: '<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".
12144: '<li>'.&mt('For consistency between the reference(s) and the location of the corresponding stored file within LON-CAPA.').'</li>'."\n".
12145: '<li>'.&mt('To change absolute paths to relative paths, or replace directory traversal via "../" within the original reference.').'</li>'."\n".
12146: '</ol></p>'."\n".'<p>'.
12147: &mt('LON-CAPA can make the required changes to your HTML file.').'</p>'."\n".
12148: '<form method="post" name="refchanger" action="'.$actionurl.'">'.
12149: &start_data_table()."\n".
12150: &start_data_table_header_row().
12151: '<th>'.&mt('Change?').'</th>'.
12152: '<th>'.&mt('Current reference').'</th>'.
12153: '<th>'.&mt('Required reference').'</th>'.
12154: &end_data_table_header_row()."\n".
12155: $modifyform.
12156: &end_data_table().'<br />'."\n".$hiddenstate.
12157: '<input type="submit" name="pathchanges" value="'.&mt('Modify HTML file').'" />'.
12158: '</form>'."\n";
12159: }
12160: return;
12161: }
12162:
12163: sub modify_html_refs {
1.1123 raeburn 12164: my ($context,$dirpath,$uname,$udom,$dir_root,$url) = @_;
1.987 raeburn 12165: my $container;
12166: if ($context eq 'portfolio') {
12167: $container = $env{'form.container'};
12168: } elsif ($context eq 'coursedoc') {
12169: $container = $env{'form.primaryurl'};
1.1071 raeburn 12170: } elsif ($context eq 'manage_dependencies') {
12171: (undef,undef,$container) = &Apache::lonnet::decode_symb($env{'form.symb'});
12172: $container = "/$container";
1.1123 raeburn 12173: } elsif ($context eq 'syllabus') {
12174: $container = $url;
1.987 raeburn 12175: } else {
1.1027 raeburn 12176: $container = $Apache::lonnet::perlvar{'lonDocRoot'}.$env{'form.filename'};
1.987 raeburn 12177: }
12178: my (%allfiles,%codebase,$output,$content);
12179: my @changes = &get_env_multiple('form.namechange');
1.1126 raeburn 12180: unless ((@changes > 0) || ($context eq 'syllabus')) {
1.1071 raeburn 12181: if (wantarray) {
12182: return ('',0,0);
12183: } else {
12184: return;
12185: }
12186: }
12187: if (($context eq 'portfolio') || ($context eq 'coursedoc') ||
1.1123 raeburn 12188: ($context eq 'manage_dependencies') || ($context eq 'syllabus')) {
1.1071 raeburn 12189: unless ($container =~ m{^/uploaded/\Q$udom\E/\Q$uname\E/}) {
12190: if (wantarray) {
12191: return ('',0,0);
12192: } else {
12193: return;
12194: }
12195: }
1.987 raeburn 12196: $content = &Apache::lonnet::getfile($container);
1.1071 raeburn 12197: if ($content eq '-1') {
12198: if (wantarray) {
12199: return ('',0,0);
12200: } else {
12201: return;
12202: }
12203: }
1.987 raeburn 12204: } else {
1.1071 raeburn 12205: unless ($container =~ /^\Q$dir_root\E/) {
12206: if (wantarray) {
12207: return ('',0,0);
12208: } else {
12209: return;
12210: }
12211: }
1.1317 raeburn 12212: if (open(my $fh,'<',$container)) {
1.987 raeburn 12213: $content = join('', <$fh>);
12214: close($fh);
12215: } else {
1.1071 raeburn 12216: if (wantarray) {
12217: return ('',0,0);
12218: } else {
12219: return;
12220: }
1.987 raeburn 12221: }
12222: }
12223: my ($count,$codebasecount) = (0,0);
12224: my $mm = new File::MMagic;
12225: my $mime_type = $mm->checktype_contents($content);
12226: if ($mime_type eq 'text/html') {
12227: my $parse_result =
12228: &Apache::lonnet::extract_embedded_items($container,\%allfiles,
12229: \%codebase,\$content);
12230: if ($parse_result eq 'ok') {
12231: foreach my $i (@changes) {
12232: my $orig = &unescape($env{'form.embedded_orig_'.$i});
12233: my $ref = &unescape($env{'form.embedded_ref_'.$i});
12234: if ($allfiles{$ref}) {
12235: my $newname = $orig;
12236: my ($attrib_regexp,$codebase);
1.1006 raeburn 12237: $attrib_regexp = &unescape($env{'form.embedded_attrib_'.$i});
1.987 raeburn 12238: if ($attrib_regexp =~ /:/) {
12239: $attrib_regexp =~ s/\:/|/g;
12240: }
12241: if ($content =~ m{($attrib_regexp\s*=\s*['"]?)\Q$ref\E(['"]?)}) {
12242: my $numchg = ($content =~ s{($attrib_regexp\s*=\s*['"]?)\Q$ref\E(['"]?)}{$1$newname$2}gi);
12243: $count += $numchg;
1.1123 raeburn 12244: $allfiles{$newname} = $allfiles{$ref};
1.1148 raeburn 12245: delete($allfiles{$ref});
1.987 raeburn 12246: }
12247: if ($env{'form.embedded_codebase_'.$i} ne '') {
1.1006 raeburn 12248: $codebase = &unescape($env{'form.embedded_codebase_'.$i});
1.987 raeburn 12249: my $numchg = ($content =~ s/(codebase\s*=\s*["']?)\Q$codebase\E(["']?)/$1.$2/i); #' stupid emacs
12250: $codebasecount ++;
12251: }
12252: }
12253: }
1.1123 raeburn 12254: my $skiprewrites;
1.987 raeburn 12255: if ($count || $codebasecount) {
12256: my $saveresult;
1.1071 raeburn 12257: if (($context eq 'portfolio') || ($context eq 'coursedoc') ||
1.1123 raeburn 12258: ($context eq 'manage_dependencies') || ($context eq 'syllabus')) {
1.987 raeburn 12259: my $url = &Apache::lonnet::store_edited_file($container,$content,$udom,$uname,\$saveresult);
12260: if ($url eq $container) {
12261: my ($fname) = ($container =~ m{/([^/]+)$});
12262: $output = '<p>'.&mt('Updated [quant,_1,reference] in [_2].',
12263: $count,'<span class="LC_filename">'.
1.1071 raeburn 12264: $fname.'</span>').'</p>';
1.987 raeburn 12265: } else {
12266: $output = '<p class="LC_error">'.
12267: &mt('Error: update failed for: [_1].',
12268: '<span class="LC_filename">'.
12269: $container.'</span>').'</p>';
12270: }
1.1123 raeburn 12271: if ($context eq 'syllabus') {
12272: unless ($saveresult eq 'ok') {
12273: $skiprewrites = 1;
12274: }
12275: }
1.987 raeburn 12276: } else {
1.1317 raeburn 12277: if (open(my $fh,'>',$container)) {
1.987 raeburn 12278: print $fh $content;
12279: close($fh);
12280: $output = '<p>'.&mt('Updated [quant,_1,reference] in [_2].',
12281: $count,'<span class="LC_filename">'.
12282: $container.'</span>').'</p>';
1.661 raeburn 12283: } else {
1.987 raeburn 12284: $output = '<p class="LC_error">'.
12285: &mt('Error: could not update [_1].',
12286: '<span class="LC_filename">'.
12287: $container.'</span>').'</p>';
1.661 raeburn 12288: }
12289: }
12290: }
1.1123 raeburn 12291: if (($context eq 'syllabus') && (!$skiprewrites)) {
12292: my ($actionurl,$state);
12293: $actionurl = "/public/$udom/$uname/syllabus";
12294: my ($ignore,$num,$numpathchanges,$existing,$mapping) =
12295: &ask_for_embedded_content($actionurl,$state,\%allfiles,
12296: \%codebase,
12297: {'context' => 'rewrites',
12298: 'ignore_remote_references' => 1,});
12299: if (ref($mapping) eq 'HASH') {
12300: my $rewrites = 0;
12301: foreach my $key (keys(%{$mapping})) {
12302: next if ($key =~ m{^https?://});
12303: my $ref = $mapping->{$key};
12304: my $newname = "/uploaded/$udom/$uname/portfolio/syllabus/$key";
12305: my $attrib;
12306: if (ref($allfiles{$mapping->{$key}}) eq 'ARRAY') {
12307: $attrib = join('|',@{$allfiles{$mapping->{$key}}});
12308: }
12309: if ($content =~ m{($attrib\s*=\s*['"]?)\Q$ref\E(['"]?)}) {
12310: my $numchg = ($content =~ s{($attrib\s*=\s*['"]?)\Q$ref\E(['"]?)}{$1$newname$2}gi);
12311: $rewrites += $numchg;
12312: }
12313: }
12314: if ($rewrites) {
12315: my $saveresult;
12316: my $url = &Apache::lonnet::store_edited_file($container,$content,$udom,$uname,\$saveresult);
12317: if ($url eq $container) {
12318: my ($fname) = ($container =~ m{/([^/]+)$});
12319: $output .= '<p>'.&mt('Rewrote [quant,_1,link] as [quant,_1,absolute link] in [_2].',
12320: $count,'<span class="LC_filename">'.
12321: $fname.'</span>').'</p>';
12322: } else {
12323: $output .= '<p class="LC_error">'.
12324: &mt('Error: could not update links in [_1].',
12325: '<span class="LC_filename">'.
12326: $container.'</span>').'</p>';
12327:
12328: }
12329: }
12330: }
12331: }
1.987 raeburn 12332: } else {
12333: &logthis('Failed to parse '.$container.
12334: ' to modify references: '.$parse_result);
1.661 raeburn 12335: }
12336: }
1.1071 raeburn 12337: if (wantarray) {
12338: return ($output,$count,$codebasecount);
12339: } else {
12340: return $output;
12341: }
1.661 raeburn 12342: }
12343:
12344: sub check_for_existing {
12345: my ($path,$fname,$element) = @_;
12346: my ($state,$msg);
12347: if (-d $path.'/'.$fname) {
12348: $state = 'exists';
12349: $msg = &mt('Unable to upload [_1]. A directory by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$path);
12350: } elsif (-e $path.'/'.$fname) {
12351: $state = 'exists';
12352: $msg = &mt('Unable to upload [_1]. A file by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$path);
12353: }
12354: if ($state eq 'exists') {
12355: $msg = '<span class="LC_error">'.$msg.'</span><br />';
12356: }
12357: return ($state,$msg);
12358: }
12359:
12360: sub check_for_upload {
12361: my ($path,$fname,$group,$element,$portfolio_root,$port_path,
12362: $disk_quota,$current_disk_usage,$uname,$udom) = @_;
1.985 raeburn 12363: my $filesize = length($env{'form.'.$element});
12364: if (!$filesize) {
12365: my $msg = '<span class="LC_error">'.
12366: &mt('Unable to upload [_1]. (size = [_2] bytes)',
12367: '<span class="LC_filename">'.$fname.'</span>',
12368: $filesize).'<br />'.
1.1007 raeburn 12369: &mt('Either the file you attempted to upload was empty, or your web browser was unable to read its contents.').'<br />'.
1.985 raeburn 12370: '</span>';
12371: return ('zero_bytes',$msg);
12372: }
12373: $filesize = $filesize/1000; #express in k (1024?)
1.661 raeburn 12374: my $getpropath = 1;
1.1021 raeburn 12375: my ($dirlistref,$listerror) =
12376: &Apache::lonnet::dirlist($portfolio_root.$path,$udom,$uname,$getpropath);
1.661 raeburn 12377: my $found_file = 0;
12378: my $locked_file = 0;
1.991 raeburn 12379: my @lockers;
12380: my $navmap;
12381: if ($env{'request.course.id'}) {
12382: $navmap = Apache::lonnavmaps::navmap->new();
12383: }
1.1021 raeburn 12384: if (ref($dirlistref) eq 'ARRAY') {
12385: foreach my $line (@{$dirlistref}) {
12386: my ($file_name,$rest)=split(/\&/,$line,2);
12387: if ($file_name eq $fname){
12388: $file_name = $path.$file_name;
12389: if ($group ne '') {
12390: $file_name = $group.$file_name;
12391: }
12392: $found_file = 1;
12393: if (&Apache::lonnet::is_locked($file_name,$udom,$uname,\@lockers) eq 'true') {
12394: foreach my $lock (@lockers) {
12395: if (ref($lock) eq 'ARRAY') {
12396: my ($symb,$crsid) = @{$lock};
12397: if ($crsid eq $env{'request.course.id'}) {
12398: if (ref($navmap)) {
12399: my $res = $navmap->getBySymb($symb);
12400: foreach my $part (@{$res->parts()}) {
12401: my ($slot_status,$slot_time,$slot_name)=$res->check_for_slot($part);
12402: unless (($slot_status == $res->RESERVED) ||
12403: ($slot_status == $res->RESERVED_LOCATION)) {
12404: $locked_file = 1;
12405: }
1.991 raeburn 12406: }
1.1021 raeburn 12407: } else {
12408: $locked_file = 1;
1.991 raeburn 12409: }
12410: } else {
12411: $locked_file = 1;
12412: }
12413: }
1.1021 raeburn 12414: }
12415: } else {
12416: my @info = split(/\&/,$rest);
12417: my $currsize = $info[6]/1000;
12418: if ($currsize < $filesize) {
12419: my $extra = $filesize - $currsize;
12420: if (($current_disk_usage + $extra) > $disk_quota) {
1.1179 bisitz 12421: my $msg = '<p class="LC_warning">'.
1.1021 raeburn 12422: &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 12423: '<span class="LC_filename">'.$fname.'</span>',$filesize,$currsize).'</p>'.
12424: '<p>'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',
12425: $disk_quota,$current_disk_usage).'</p>';
1.1021 raeburn 12426: return ('will_exceed_quota',$msg);
12427: }
1.984 raeburn 12428: }
12429: }
1.661 raeburn 12430: }
12431: }
12432: }
12433: if (($current_disk_usage + $filesize) > $disk_quota){
1.1179 bisitz 12434: my $msg = '<p class="LC_warning">'.
12435: &mt('Unable to upload [_1]. (size = [_2] kilobytes). Disk quota will be exceeded.','<span class="LC_filename">'.$fname.'</span>',$filesize).'</p>'.
1.1184 raeburn 12436: '<p>'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',$disk_quota,$current_disk_usage).'</p>';
1.661 raeburn 12437: return ('will_exceed_quota',$msg);
12438: } elsif ($found_file) {
12439: if ($locked_file) {
1.1179 bisitz 12440: my $msg = '<p class="LC_warning">';
1.661 raeburn 12441: $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 12442: $msg .= '</p>';
1.661 raeburn 12443: $msg .= &mt('You will be able to rename or delete existing [_1] after a grade has been assigned.','<span class="LC_filename">'.$fname.'</span>');
12444: return ('file_locked',$msg);
12445: } else {
1.1179 bisitz 12446: my $msg = '<p class="LC_error">';
1.984 raeburn 12447: $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 12448: $msg .= '</p>';
1.984 raeburn 12449: return ('existingfile',$msg);
1.661 raeburn 12450: }
12451: }
12452: }
12453:
1.987 raeburn 12454: sub check_for_traversal {
12455: my ($path,$url,$toplevel) = @_;
12456: my @parts=split(/\//,$path);
12457: my $cleanpath;
12458: my $fullpath = $url;
12459: for (my $i=0;$i<@parts;$i++) {
12460: next if ($parts[$i] eq '.');
12461: if ($parts[$i] eq '..') {
12462: $fullpath =~ s{([^/]+/)$}{};
12463: } else {
12464: $fullpath .= $parts[$i].'/';
12465: }
12466: }
12467: if ($fullpath =~ /^\Q$url\E(.*)$/) {
12468: $cleanpath = $1;
12469: } elsif ($fullpath =~ /^\Q$toplevel\E(.*)$/) {
12470: my $curr_toprel = $1;
12471: my @parts = split(/\//,$curr_toprel);
12472: my ($url_toprel) = ($url =~ /^\Q$toplevel\E(.*)$/);
12473: my @urlparts = split(/\//,$url_toprel);
12474: my $doubledots;
12475: my $startdiff = -1;
12476: for (my $i=0; $i<@urlparts; $i++) {
12477: if ($startdiff == -1) {
12478: unless ($urlparts[$i] eq $parts[$i]) {
12479: $startdiff = $i;
12480: $doubledots .= '../';
12481: }
12482: } else {
12483: $doubledots .= '../';
12484: }
12485: }
12486: if ($startdiff > -1) {
12487: $cleanpath = $doubledots;
12488: for (my $i=$startdiff; $i<@parts; $i++) {
12489: $cleanpath .= $parts[$i].'/';
12490: }
12491: }
12492: }
12493: $cleanpath =~ s{(/)$}{};
12494: return $cleanpath;
12495: }
1.31 albertel 12496:
1.1053 raeburn 12497: sub is_archive_file {
12498: my ($mimetype) = @_;
12499: if (($mimetype eq 'application/octet-stream') ||
12500: ($mimetype eq 'application/x-stuffit') ||
12501: ($mimetype =~ m{^application/(x\-)?(compressed|tar|zip|tgz|gz|gtar|gzip|gunzip|bz|bz2|bzip2)})) {
12502: return 1;
12503: }
12504: return;
12505: }
12506:
12507: sub decompress_form {
1.1065 raeburn 12508: my ($mimetype,$archiveurl,$action,$noextract,$hiddenelements,$dirlist) = @_;
1.1053 raeburn 12509: my %lt = &Apache::lonlocal::texthash (
12510: this => 'This file is an archive file.',
1.1067 raeburn 12511: camt => 'This file is a Camtasia archive file.',
1.1065 raeburn 12512: itsc => 'Its contents are as follows:',
1.1053 raeburn 12513: youm => 'You may wish to extract its contents.',
12514: extr => 'Extract contents',
1.1067 raeburn 12515: auto => 'LON-CAPA can process the files automatically, or you can decide how each should be handled.',
12516: proa => 'Process automatically?',
1.1053 raeburn 12517: yes => 'Yes',
12518: no => 'No',
1.1067 raeburn 12519: fold => 'Title for folder containing movie',
12520: movi => 'Title for page containing embedded movie',
1.1053 raeburn 12521: );
1.1065 raeburn 12522: my $fileloc = &Apache::lonnet::filelocation(undef,$archiveurl);
1.1067 raeburn 12523: my ($is_camtasia,$topdir,%toplevel,@paths);
1.1065 raeburn 12524: my $info = &list_archive_contents($fileloc,\@paths);
12525: if (@paths) {
12526: foreach my $path (@paths) {
12527: $path =~ s{^/}{};
1.1067 raeburn 12528: if ($path =~ m{^([^/]+)/$}) {
12529: $topdir = $1;
12530: }
1.1065 raeburn 12531: if ($path =~ m{^([^/]+)/}) {
12532: $toplevel{$1} = $path;
12533: } else {
12534: $toplevel{$path} = $path;
12535: }
12536: }
12537: }
1.1067 raeburn 12538: if ($mimetype =~ m{^application/(x\-)?(compressed|zip)}) {
1.1164 raeburn 12539: my @camtasia6 = ("$topdir/","$topdir/index.html",
1.1067 raeburn 12540: "$topdir/media/",
12541: "$topdir/media/$topdir.mp4",
12542: "$topdir/media/FirstFrame.png",
12543: "$topdir/media/player.swf",
12544: "$topdir/media/swfobject.js",
12545: "$topdir/media/expressInstall.swf");
1.1197 raeburn 12546: my @camtasia8_1 = ("$topdir/","$topdir/$topdir.html",
1.1164 raeburn 12547: "$topdir/$topdir.mp4",
12548: "$topdir/$topdir\_config.xml",
12549: "$topdir/$topdir\_controller.swf",
12550: "$topdir/$topdir\_embed.css",
12551: "$topdir/$topdir\_First_Frame.png",
12552: "$topdir/$topdir\_player.html",
12553: "$topdir/$topdir\_Thumbnails.png",
12554: "$topdir/playerProductInstall.swf",
12555: "$topdir/scripts/",
12556: "$topdir/scripts/config_xml.js",
12557: "$topdir/scripts/handlebars.js",
12558: "$topdir/scripts/jquery-1.7.1.min.js",
12559: "$topdir/scripts/jquery-ui-1.8.15.custom.min.js",
12560: "$topdir/scripts/modernizr.js",
12561: "$topdir/scripts/player-min.js",
12562: "$topdir/scripts/swfobject.js",
12563: "$topdir/skins/",
12564: "$topdir/skins/configuration_express.xml",
12565: "$topdir/skins/express_show/",
12566: "$topdir/skins/express_show/player-min.css",
12567: "$topdir/skins/express_show/spritesheet.png");
1.1197 raeburn 12568: my @camtasia8_4 = ("$topdir/","$topdir/$topdir.html",
12569: "$topdir/$topdir.mp4",
12570: "$topdir/$topdir\_config.xml",
12571: "$topdir/$topdir\_controller.swf",
12572: "$topdir/$topdir\_embed.css",
12573: "$topdir/$topdir\_First_Frame.png",
12574: "$topdir/$topdir\_player.html",
12575: "$topdir/$topdir\_Thumbnails.png",
12576: "$topdir/playerProductInstall.swf",
12577: "$topdir/scripts/",
12578: "$topdir/scripts/config_xml.js",
12579: "$topdir/scripts/techsmith-smart-player.min.js",
12580: "$topdir/skins/",
12581: "$topdir/skins/configuration_express.xml",
12582: "$topdir/skins/express_show/",
12583: "$topdir/skins/express_show/spritesheet.min.css",
12584: "$topdir/skins/express_show/spritesheet.png",
12585: "$topdir/skins/express_show/techsmith-smart-player.min.css");
1.1164 raeburn 12586: my @diffs = &compare_arrays(\@paths,\@camtasia6);
1.1067 raeburn 12587: if (@diffs == 0) {
1.1164 raeburn 12588: $is_camtasia = 6;
12589: } else {
1.1197 raeburn 12590: @diffs = &compare_arrays(\@paths,\@camtasia8_1);
1.1164 raeburn 12591: if (@diffs == 0) {
12592: $is_camtasia = 8;
1.1197 raeburn 12593: } else {
12594: @diffs = &compare_arrays(\@paths,\@camtasia8_4);
12595: if (@diffs == 0) {
12596: $is_camtasia = 8;
12597: }
1.1164 raeburn 12598: }
1.1067 raeburn 12599: }
12600: }
12601: my $output;
12602: if ($is_camtasia) {
12603: $output = <<"ENDCAM";
12604: <script type="text/javascript" language="Javascript">
12605: // <![CDATA[
12606:
12607: function camtasiaToggle() {
12608: for (var i=0; i<document.uploaded_decompress.autoextract_camtasia.length; i++) {
12609: if (document.uploaded_decompress.autoextract_camtasia[i].checked) {
1.1164 raeburn 12610: if (document.uploaded_decompress.autoextract_camtasia[i].value == $is_camtasia) {
1.1067 raeburn 12611: document.getElementById('camtasia_titles').style.display='block';
12612: } else {
12613: document.getElementById('camtasia_titles').style.display='none';
12614: }
12615: }
12616: }
12617: return;
12618: }
12619:
12620: // ]]>
12621: </script>
12622: <p>$lt{'camt'}</p>
12623: ENDCAM
1.1065 raeburn 12624: } else {
1.1067 raeburn 12625: $output = '<p>'.$lt{'this'};
12626: if ($info eq '') {
12627: $output .= ' '.$lt{'youm'}.'</p>'."\n";
12628: } else {
12629: $output .= ' '.$lt{'itsc'}.'</p>'."\n".
12630: '<div><pre>'.$info.'</pre></div>';
12631: }
1.1065 raeburn 12632: }
1.1067 raeburn 12633: $output .= '<form name="uploaded_decompress" action="'.$action.'" method="post">'."\n";
1.1065 raeburn 12634: my $duplicates;
12635: my $num = 0;
12636: if (ref($dirlist) eq 'ARRAY') {
12637: foreach my $item (@{$dirlist}) {
12638: if (ref($item) eq 'ARRAY') {
12639: if (exists($toplevel{$item->[0]})) {
12640: $duplicates .=
12641: &start_data_table_row().
12642: '<td><label><input type="radio" name="archive_overwrite_'.$num.'" '.
12643: 'value="0" checked="checked" />'.&mt('No').'</label>'.
12644: ' <label><input type="radio" name="archive_overwrite_'.$num.'" '.
12645: 'value="1" />'.&mt('Yes').'</label>'.
12646: '<input type="hidden" name="archive_overwrite_name_'.$num.'" value="'.$item->[0].'" /></td>'."\n".
12647: '<td>'.$item->[0].'</td>';
12648: if ($item->[2]) {
12649: $duplicates .= '<td>'.&mt('Directory').'</td>';
12650: } else {
12651: $duplicates .= '<td>'.&mt('File').'</td>';
12652: }
12653: $duplicates .= '<td>'.$item->[3].'</td>'.
12654: '<td>'.
12655: &Apache::lonlocal::locallocaltime($item->[4]).
12656: '</td>'.
12657: &end_data_table_row();
12658: $num ++;
12659: }
12660: }
12661: }
12662: }
12663: my $itemcount;
12664: if (@paths > 0) {
12665: $itemcount = scalar(@paths);
12666: } else {
12667: $itemcount = 1;
12668: }
1.1067 raeburn 12669: if ($is_camtasia) {
12670: $output .= $lt{'auto'}.'<br />'.
12671: '<span class="LC_nobreak">'.$lt{'proa'}.'<label>'.
1.1164 raeburn 12672: '<input type="radio" name="autoextract_camtasia" value="'.$is_camtasia.'" onclick="javascript:camtasiaToggle();" checked="checked" />'.
1.1067 raeburn 12673: $lt{'yes'}.'</label> <label>'.
12674: '<input type="radio" name="autoextract_camtasia" value="0" onclick="javascript:camtasiaToggle();" />'.
12675: $lt{'no'}.'</label></span><br />'.
12676: '<div id="camtasia_titles" style="display:block">'.
12677: &Apache::lonhtmlcommon::start_pick_box().
12678: &Apache::lonhtmlcommon::row_title($lt{'fold'}).
12679: '<input type="textbox" name="camtasia_foldername" value="'.$env{'form.comment'}.'" />'."\n".
12680: &Apache::lonhtmlcommon::row_closure().
12681: &Apache::lonhtmlcommon::row_title($lt{'movi'}).
12682: '<input type="textbox" name="camtasia_moviename" value="" />'."\n".
12683: &Apache::lonhtmlcommon::row_closure(1).
12684: &Apache::lonhtmlcommon::end_pick_box().
12685: '</div>';
12686: }
1.1065 raeburn 12687: $output .=
12688: '<input type="hidden" name="archive_overwrite_total" value="'.$num.'" />'.
1.1067 raeburn 12689: '<input type="hidden" name="archive_itemcount" value="'.$itemcount.'" />'.
12690: "\n";
1.1065 raeburn 12691: if ($duplicates ne '') {
12692: $output .= '<p><span class="LC_warning">'.
12693: &mt('Warning: decompression of the archive will overwrite the following items which already exist:').'</span><br />'.
12694: &start_data_table().
12695: &start_data_table_header_row().
12696: '<th>'.&mt('Overwrite?').'</th>'.
12697: '<th>'.&mt('Name').'</th>'.
12698: '<th>'.&mt('Type').'</th>'.
12699: '<th>'.&mt('Size').'</th>'.
12700: '<th>'.&mt('Last modified').'</th>'.
12701: &end_data_table_header_row().
12702: $duplicates.
12703: &end_data_table().
12704: '</p>';
12705: }
1.1067 raeburn 12706: $output .= '<input type="hidden" name="archiveurl" value="'.$archiveurl.'" />'."\n";
1.1053 raeburn 12707: if (ref($hiddenelements) eq 'HASH') {
12708: foreach my $hidden (sort(keys(%{$hiddenelements}))) {
12709: $output .= '<input type="hidden" name="'.$hidden.'" value="'.$hiddenelements->{$hidden}.'" />'."\n";
12710: }
12711: }
12712: $output .= <<"END";
1.1067 raeburn 12713: <br />
1.1053 raeburn 12714: <input type="submit" name="decompress" value="$lt{'extr'}" />
12715: </form>
12716: $noextract
12717: END
12718: return $output;
12719: }
12720:
1.1065 raeburn 12721: sub decompression_utility {
12722: my ($program) = @_;
12723: my @utilities = ('tar','gunzip','bunzip2','unzip');
12724: my $location;
12725: if (grep(/^\Q$program\E$/,@utilities)) {
12726: foreach my $dir ('/bin/','/usr/bin/','/usr/local/bin/','/sbin/',
12727: '/usr/sbin/') {
12728: if (-x $dir.$program) {
12729: $location = $dir.$program;
12730: last;
12731: }
12732: }
12733: }
12734: return $location;
12735: }
12736:
12737: sub list_archive_contents {
12738: my ($file,$pathsref) = @_;
12739: my (@cmd,$output);
12740: my $needsregexp;
12741: if ($file =~ /\.zip$/) {
12742: @cmd = (&decompression_utility('unzip'),"-l");
12743: $needsregexp = 1;
12744: } elsif (($file =~ m/\.tar\.gz$/) ||
12745: ($file =~ /\.tgz$/)) {
12746: @cmd = (&decompression_utility('tar'),"-ztf");
12747: } elsif ($file =~ /\.tar\.bz2$/) {
12748: @cmd = (&decompression_utility('tar'),"-jtf");
12749: } elsif ($file =~ m|\.tar$|) {
12750: @cmd = (&decompression_utility('tar'),"-tf");
12751: }
12752: if (@cmd) {
12753: undef($!);
12754: undef($@);
12755: if (open(my $fh,"-|", @cmd, $file)) {
12756: while (my $line = <$fh>) {
12757: $output .= $line;
12758: chomp($line);
12759: my $item;
12760: if ($needsregexp) {
12761: ($item) = ($line =~ /^\s*\d+\s+[\d\-]+\s+[\d:]+\s*(.+)$/);
12762: } else {
12763: $item = $line;
12764: }
12765: if ($item ne '') {
12766: unless (grep(/^\Q$item\E$/,@{$pathsref})) {
12767: push(@{$pathsref},$item);
12768: }
12769: }
12770: }
12771: close($fh);
12772: }
12773: }
12774: return $output;
12775: }
12776:
1.1053 raeburn 12777: sub decompress_uploaded_file {
12778: my ($file,$dir) = @_;
12779: &Apache::lonnet::appenv({'cgi.file' => $file});
12780: &Apache::lonnet::appenv({'cgi.dir' => $dir});
12781: my $result = &Apache::lonnet::ssi_body('/cgi-bin/decompress.pl');
12782: my ($handle) = ($env{'user.environment'} =~m{/([^/]+)\.id$});
12783: my $lonidsdir = $Apache::lonnet::perlvar{'lonIDsDir'};
12784: &Apache::lonnet::transfer_profile_to_env($lonidsdir,$handle,1);
12785: my $decompressed = $env{'cgi.decompressed'};
12786: &Apache::lonnet::delenv('cgi.file');
12787: &Apache::lonnet::delenv('cgi.dir');
12788: &Apache::lonnet::delenv('cgi.decompressed');
12789: return ($decompressed,$result);
12790: }
12791:
1.1055 raeburn 12792: sub process_decompression {
12793: my ($docudom,$docuname,$file,$destination,$dir_root,$hiddenelem) = @_;
1.1292 raeburn 12794: unless (($dir_root eq '/userfiles') && ($destination =~ m{^(docs|supplemental)/(default|\d+)/\d+$})) {
12795: return '<p class="LC_error">'.&mt('Not extracted.').'<br />'.
12796: &mt('Unexpected file path.').'</p>'."\n";
12797: }
12798: unless (($docudom =~ /^$match_domain$/) && ($docuname =~ /^$match_courseid$/)) {
12799: return '<p class="LC_error">'.&mt('Not extracted.').'<br />'.
12800: &mt('Unexpected course context.').'</p>'."\n";
12801: }
1.1293 raeburn 12802: unless ($file eq &Apache::lonnet::clean_filename($file)) {
1.1292 raeburn 12803: return '<p class="LC_error">'.&mt('Not extracted.').'<br />'.
12804: &mt('Filename contained unexpected characters.').'</p>'."\n";
12805: }
1.1055 raeburn 12806: my ($dir,$error,$warning,$output);
1.1180 raeburn 12807: if ($file !~ /\.(zip|tar|bz2|gz|tar.gz|tar.bz2|tgz)$/i) {
1.1120 bisitz 12808: $error = &mt('Filename not a supported archive file type.').
12809: '<br />'.&mt('Filename should end with one of: [_1].',
1.1055 raeburn 12810: '.zip, .tar, .bz2, .gz, .tar.gz, .tar.bz2, .tgz');
12811: } else {
12812: my $docuhome = &Apache::lonnet::homeserver($docuname,$docudom);
12813: if ($docuhome eq 'no_host') {
12814: $error = &mt('Could not determine home server for course.');
12815: } else {
12816: my @ids=&Apache::lonnet::current_machine_ids();
12817: my $currdir = "$dir_root/$destination";
12818: if (grep(/^\Q$docuhome\E$/,@ids)) {
12819: $dir = &LONCAPA::propath($docudom,$docuname).
12820: "$dir_root/$destination";
12821: } else {
12822: $dir = $Apache::lonnet::perlvar{'lonDocRoot'}.
12823: "$dir_root/$docudom/$docuname/$destination";
12824: unless (&Apache::lonnet::repcopy_userfile("$dir/$file") eq 'ok') {
12825: $error = &mt('Archive file not found.');
12826: }
12827: }
1.1065 raeburn 12828: my (@to_overwrite,@to_skip);
12829: if ($env{'form.archive_overwrite_total'} > 0) {
12830: my $total = $env{'form.archive_overwrite_total'};
12831: for (my $i=0; $i<$total; $i++) {
12832: if ($env{'form.archive_overwrite_'.$i} == 1) {
12833: push(@to_overwrite,$env{'form.archive_overwrite_name_'.$i});
12834: } elsif ($env{'form.archive_overwrite_'.$i} == 0) {
12835: push(@to_skip,$env{'form.archive_overwrite_name_'.$i});
12836: }
12837: }
12838: }
12839: my $numskip = scalar(@to_skip);
1.1292 raeburn 12840: my $numoverwrite = scalar(@to_overwrite);
12841: if (($numskip) && (!$numoverwrite)) {
1.1065 raeburn 12842: $warning = &mt('All items in the archive file already exist, and no overwriting of existing files has been requested.');
12843: } elsif ($dir eq '') {
1.1055 raeburn 12844: $error = &mt('Directory containing archive file unavailable.');
12845: } elsif (!$error) {
1.1065 raeburn 12846: my ($decompressed,$display);
1.1292 raeburn 12847: if (($numskip) || ($numoverwrite)) {
1.1065 raeburn 12848: my $tempdir = time.'_'.$$.int(rand(10000));
12849: mkdir("$dir/$tempdir",0755);
1.1292 raeburn 12850: if (&File::Copy::move("$dir/$file","$dir/$tempdir/$file")) {
12851: ($decompressed,$display) =
12852: &decompress_uploaded_file($file,"$dir/$tempdir");
12853: foreach my $item (@to_skip) {
12854: if (($item ne '') && ($item !~ /\.\./)) {
12855: if (-f "$dir/$tempdir/$item") {
12856: unlink("$dir/$tempdir/$item");
12857: } elsif (-d "$dir/$tempdir/$item") {
1.1300 raeburn 12858: &File::Path::remove_tree("$dir/$tempdir/$item",{ safe => 1 });
1.1292 raeburn 12859: }
12860: }
12861: }
12862: foreach my $item (@to_overwrite) {
12863: if ((-e "$dir/$tempdir/$item") && (-e "$dir/$item")) {
12864: if (($item ne '') && ($item !~ /\.\./)) {
12865: if (-f "$dir/$item") {
12866: unlink("$dir/$item");
12867: } elsif (-d "$dir/$item") {
1.1300 raeburn 12868: &File::Path::remove_tree("$dir/$item",{ safe => 1 });
1.1292 raeburn 12869: }
12870: &File::Copy::move("$dir/$tempdir/$item","$dir/$item");
12871: }
1.1065 raeburn 12872: }
12873: }
1.1292 raeburn 12874: if (&File::Copy::move("$dir/$tempdir/$file","$dir/$file")) {
1.1300 raeburn 12875: &File::Path::remove_tree("$dir/$tempdir",{ safe => 1 });
1.1292 raeburn 12876: }
1.1065 raeburn 12877: }
12878: } else {
12879: ($decompressed,$display) =
12880: &decompress_uploaded_file($file,$dir);
12881: }
1.1055 raeburn 12882: if ($decompressed eq 'ok') {
1.1065 raeburn 12883: $output = '<p class="LC_info">'.
12884: &mt('Files extracted successfully from archive.').
12885: '</p>'."\n";
1.1055 raeburn 12886: my ($warning,$result,@contents);
12887: my ($newdirlistref,$newlisterror) =
12888: &Apache::lonnet::dirlist($currdir,$docudom,
12889: $docuname,1);
12890: my (%is_dir,%changes,@newitems);
12891: my $dirptr = 16384;
1.1065 raeburn 12892: if (ref($newdirlistref) eq 'ARRAY') {
1.1055 raeburn 12893: foreach my $dir_line (@{$newdirlistref}) {
12894: my ($item,undef,undef,$testdir)=split(/\&/,$dir_line,5);
1.1292 raeburn 12895: unless (($item =~ /^\.+$/) || ($item eq $file)) {
1.1055 raeburn 12896: push(@newitems,$item);
12897: if ($dirptr&$testdir) {
12898: $is_dir{$item} = 1;
12899: }
12900: $changes{$item} = 1;
12901: }
12902: }
12903: }
12904: if (keys(%changes) > 0) {
12905: foreach my $item (sort(@newitems)) {
12906: if ($changes{$item}) {
12907: push(@contents,$item);
12908: }
12909: }
12910: }
12911: if (@contents > 0) {
1.1067 raeburn 12912: my $wantform;
12913: unless ($env{'form.autoextract_camtasia'}) {
12914: $wantform = 1;
12915: }
1.1056 raeburn 12916: my (%children,%parent,%dirorder,%titles);
1.1055 raeburn 12917: my ($count,$datatable) = &get_extracted($docudom,$docuname,
12918: $currdir,\%is_dir,
12919: \%children,\%parent,
1.1056 raeburn 12920: \@contents,\%dirorder,
12921: \%titles,$wantform);
1.1055 raeburn 12922: if ($datatable ne '') {
12923: $output .= &archive_options_form('decompressed',$datatable,
12924: $count,$hiddenelem);
1.1065 raeburn 12925: my $startcount = 6;
1.1055 raeburn 12926: $output .= &archive_javascript($startcount,$count,
1.1056 raeburn 12927: \%titles,\%children);
1.1055 raeburn 12928: }
1.1067 raeburn 12929: if ($env{'form.autoextract_camtasia'}) {
1.1164 raeburn 12930: my $version = $env{'form.autoextract_camtasia'};
1.1067 raeburn 12931: my %displayed;
12932: my $total = 1;
12933: $env{'form.archive_directory'} = [];
12934: foreach my $i (sort { $a <=> $b } keys(%dirorder)) {
12935: my $path = join('/',map { $titles{$_}; } @{$dirorder{$i}});
12936: $path =~ s{/$}{};
12937: my $item;
12938: if ($path ne '') {
12939: $item = "$path/$titles{$i}";
12940: } else {
12941: $item = $titles{$i};
12942: }
12943: $env{'form.archive_content_'.$i} = "$dir_root/$destination/$item";
12944: if ($item eq $contents[0]) {
12945: push(@{$env{'form.archive_directory'}},$i);
12946: $env{'form.archive_'.$i} = 'display';
12947: $env{'form.archive_title_'.$i} = $env{'form.camtasia_foldername'};
12948: $displayed{'folder'} = $i;
1.1164 raeburn 12949: } elsif ((($item eq "$contents[0]/index.html") && ($version == 6)) ||
12950: (($item eq "$contents[0]/$contents[0]".'.html') && ($version == 8))) {
1.1067 raeburn 12951: $env{'form.archive_'.$i} = 'display';
12952: $env{'form.archive_title_'.$i} = $env{'form.camtasia_moviename'};
12953: $displayed{'web'} = $i;
12954: } else {
1.1164 raeburn 12955: if ((($item eq "$contents[0]/media") && ($version == 6)) ||
12956: ((($item eq "$contents[0]/scripts") || ($item eq "$contents[0]/skins") ||
12957: ($item eq "$contents[0]/skins/express_show")) && ($version == 8))) {
1.1067 raeburn 12958: push(@{$env{'form.archive_directory'}},$i);
12959: }
12960: $env{'form.archive_'.$i} = 'dependency';
12961: }
12962: $total ++;
12963: }
12964: for (my $i=1; $i<$total; $i++) {
12965: next if ($i == $displayed{'web'});
12966: next if ($i == $displayed{'folder'});
12967: $env{'form.archive_dependent_on_'.$i} = $displayed{'web'};
12968: }
12969: $env{'form.phase'} = 'decompress_cleanup';
12970: $env{'form.archivedelete'} = 1;
12971: $env{'form.archive_count'} = $total-1;
12972: $output .=
12973: &process_extracted_files('coursedocs',$docudom,
12974: $docuname,$destination,
12975: $dir_root,$hiddenelem);
12976: }
1.1055 raeburn 12977: } else {
12978: $warning = &mt('No new items extracted from archive file.');
12979: }
12980: } else {
12981: $output = $display;
12982: $error = &mt('An error occurred during extraction from the archive file.');
12983: }
12984: }
12985: }
12986: }
12987: if ($error) {
12988: $output .= '<p class="LC_error">'.&mt('Not extracted.').'<br />'.
12989: $error.'</p>'."\n";
12990: }
12991: if ($warning) {
12992: $output .= '<p class="LC_warning">'.$warning.'</p>'."\n";
12993: }
12994: return $output;
12995: }
12996:
12997: sub get_extracted {
1.1056 raeburn 12998: my ($docudom,$docuname,$currdir,$is_dir,$children,$parent,$contents,$dirorder,
12999: $titles,$wantform) = @_;
1.1055 raeburn 13000: my $count = 0;
13001: my $depth = 0;
13002: my $datatable;
1.1056 raeburn 13003: my @hierarchy;
1.1055 raeburn 13004: return unless ((ref($is_dir) eq 'HASH') && (ref($children) eq 'HASH') &&
1.1056 raeburn 13005: (ref($parent) eq 'HASH') && (ref($contents) eq 'ARRAY') &&
13006: (ref($dirorder) eq 'HASH') && (ref($titles) eq 'HASH'));
1.1055 raeburn 13007: foreach my $item (@{$contents}) {
13008: $count ++;
1.1056 raeburn 13009: @{$dirorder->{$count}} = @hierarchy;
13010: $titles->{$count} = $item;
1.1055 raeburn 13011: &archive_hierarchy($depth,$count,$parent,$children);
13012: if ($wantform) {
13013: $datatable .= &archive_row($is_dir->{$item},$item,
13014: $currdir,$depth,$count);
13015: }
13016: if ($is_dir->{$item}) {
13017: $depth ++;
1.1056 raeburn 13018: push(@hierarchy,$count);
13019: $parent->{$depth} = $count;
1.1055 raeburn 13020: $datatable .=
13021: &recurse_extracted_archive("$currdir/$item",$docudom,$docuname,
1.1056 raeburn 13022: \$depth,\$count,\@hierarchy,$dirorder,
13023: $children,$parent,$titles,$wantform);
1.1055 raeburn 13024: $depth --;
1.1056 raeburn 13025: pop(@hierarchy);
1.1055 raeburn 13026: }
13027: }
13028: return ($count,$datatable);
13029: }
13030:
13031: sub recurse_extracted_archive {
1.1056 raeburn 13032: my ($currdir,$docudom,$docuname,$depth,$count,$hierarchy,$dirorder,
13033: $children,$parent,$titles,$wantform) = @_;
1.1055 raeburn 13034: my $result='';
1.1056 raeburn 13035: unless ((ref($depth)) && (ref($count)) && (ref($hierarchy) eq 'ARRAY') &&
13036: (ref($children) eq 'HASH') && (ref($parent) eq 'HASH') &&
13037: (ref($dirorder) eq 'HASH')) {
1.1055 raeburn 13038: return $result;
13039: }
13040: my $dirptr = 16384;
13041: my ($newdirlistref,$newlisterror) =
13042: &Apache::lonnet::dirlist($currdir,$docudom,$docuname,1);
13043: if (ref($newdirlistref) eq 'ARRAY') {
13044: foreach my $dir_line (@{$newdirlistref}) {
13045: my ($item,undef,undef,$testdir)=split(/\&/,$dir_line,5);
13046: unless ($item =~ /^\.+$/) {
13047: $$count ++;
1.1056 raeburn 13048: @{$dirorder->{$$count}} = @{$hierarchy};
13049: $titles->{$$count} = $item;
1.1055 raeburn 13050: &archive_hierarchy($$depth,$$count,$parent,$children);
1.1056 raeburn 13051:
1.1055 raeburn 13052: my $is_dir;
13053: if ($dirptr&$testdir) {
13054: $is_dir = 1;
13055: }
13056: if ($wantform) {
13057: $result .= &archive_row($is_dir,$item,$currdir,$$depth,$$count);
13058: }
13059: if ($is_dir) {
13060: $$depth ++;
1.1056 raeburn 13061: push(@{$hierarchy},$$count);
13062: $parent->{$$depth} = $$count;
1.1055 raeburn 13063: $result .=
13064: &recurse_extracted_archive("$currdir/$item",$docudom,
13065: $docuname,$depth,$count,
1.1056 raeburn 13066: $hierarchy,$dirorder,$children,
13067: $parent,$titles,$wantform);
1.1055 raeburn 13068: $$depth --;
1.1056 raeburn 13069: pop(@{$hierarchy});
1.1055 raeburn 13070: }
13071: }
13072: }
13073: }
13074: return $result;
13075: }
13076:
13077: sub archive_hierarchy {
13078: my ($depth,$count,$parent,$children) =@_;
13079: if ((ref($parent) eq 'HASH') && (ref($children) eq 'HASH')) {
13080: if (exists($parent->{$depth})) {
13081: $children->{$parent->{$depth}} .= $count.':';
13082: }
13083: }
13084: return;
13085: }
13086:
13087: sub archive_row {
13088: my ($is_dir,$item,$currdir,$depth,$count) = @_;
13089: my ($name) = ($item =~ m{([^/]+)$});
13090: my %choices = &Apache::lonlocal::texthash (
1.1059 raeburn 13091: 'display' => 'Add as file',
1.1055 raeburn 13092: 'dependency' => 'Include as dependency',
13093: 'discard' => 'Discard',
13094: );
13095: if ($is_dir) {
1.1059 raeburn 13096: $choices{'display'} = &mt('Add as folder');
1.1055 raeburn 13097: }
1.1056 raeburn 13098: my $output = &start_data_table_row().'<td align="right">'.$count.'</td>'."\n";
13099: my $offset = 0;
1.1055 raeburn 13100: foreach my $action ('display','dependency','discard') {
1.1056 raeburn 13101: $offset ++;
1.1065 raeburn 13102: if ($action ne 'display') {
13103: $offset ++;
13104: }
1.1055 raeburn 13105: $output .= '<td><span class="LC_nobreak">'.
13106: '<label><input type="radio" name="archive_'.$count.
13107: '" id="archive_'.$action.'_'.$count.'" value="'.$action.'"';
13108: my $text = $choices{$action};
13109: if ($is_dir) {
13110: $output .= ' onclick="javascript:propagateCheck(this.form,'."'$count'".');"';
13111: if ($action eq 'display') {
1.1059 raeburn 13112: $text = &mt('Add as folder');
1.1055 raeburn 13113: }
1.1056 raeburn 13114: } else {
13115: $output .= ' onclick="javascript:dependencyCheck(this.form,'."$count,$offset".');"';
13116:
13117: }
13118: $output .= ' /> '.$choices{$action}.'</label></span>';
13119: if ($action eq 'dependency') {
13120: $output .= '<div id="arc_depon_'.$count.'" style="display:none;">'."\n".
13121: &mt('Used by:').' <select name="archive_dependent_on_'.$count.'" '.
13122: 'onchange="propagateSelect(this.form,'."$count,$offset".')">'."\n".
13123: '<option value=""></option>'."\n".
13124: '</select>'."\n".
13125: '</div>';
1.1059 raeburn 13126: } elsif ($action eq 'display') {
13127: $output .= '<div id="arc_title_'.$count.'" style="display:none;">'."\n".
13128: &mt('Title:').' <input type="text" name="archive_title_'.$count.'" id="archive_title_'.$count.'" />'."\n".
13129: '</div>';
1.1055 raeburn 13130: }
1.1056 raeburn 13131: $output .= '</td>';
1.1055 raeburn 13132: }
13133: $output .= '<td><input type="hidden" name="archive_content_'.$count.'" value="'.
13134: &HTML::Entities::encode("$currdir/$item",'"<>&').'" />'.(' ' x 2);
13135: for (my $i=0; $i<$depth; $i++) {
13136: $output .= ('<img src="/adm/lonIcons/whitespace1.gif" class="LC_docs_spacer" alt="" />' x2)."\n";
13137: }
13138: if ($is_dir) {
13139: $output .= '<img src="/adm/lonIcons/navmap.folder.open.gif" alt="" /> '."\n".
13140: '<input type="hidden" name="archive_directory" value="'.$count.'" />'."\n";
13141: } else {
13142: $output .= '<input type="hidden" name="archive_file" value="'.$count.'" />'."\n";
13143: }
13144: $output .= ' '.$name.'</td>'."\n".
13145: &end_data_table_row();
13146: return $output;
13147: }
13148:
13149: sub archive_options_form {
1.1065 raeburn 13150: my ($form,$display,$count,$hiddenelem) = @_;
13151: my %lt = &Apache::lonlocal::texthash(
13152: perm => 'Permanently remove archive file?',
13153: hows => 'How should each extracted item be incorporated in the course?',
13154: cont => 'Content actions for all',
13155: addf => 'Add as folder/file',
13156: incd => 'Include as dependency for a displayed file',
13157: disc => 'Discard',
13158: no => 'No',
13159: yes => 'Yes',
13160: save => 'Save',
13161: );
13162: my $output = <<"END";
13163: <form name="$form" method="post" action="">
13164: <p><span class="LC_nobreak">$lt{'perm'}
13165: <label>
13166: <input type="radio" name="archivedelete" value="0" checked="checked" />$lt{'no'}
13167: </label>
13168:
13169: <label>
13170: <input type="radio" name="archivedelete" value="1" />$lt{'yes'}</label>
13171: </span>
13172: </p>
13173: <input type="hidden" name="phase" value="decompress_cleanup" />
13174: <br />$lt{'hows'}
13175: <div class="LC_columnSection">
13176: <fieldset>
13177: <legend>$lt{'cont'}</legend>
13178: <input type="button" value="$lt{'addf'}" onclick="javascript:checkAll(document.$form,'display');" />
13179: <input type="button" value="$lt{'incd'}" onclick="javascript:checkAll(document.$form,'dependency');" />
13180: <input type="button" value="$lt{'disc'}" onclick="javascript:checkAll(document.$form,'discard');" />
13181: </fieldset>
13182: </div>
13183: END
13184: return $output.
1.1055 raeburn 13185: &start_data_table()."\n".
1.1065 raeburn 13186: $display."\n".
1.1055 raeburn 13187: &end_data_table()."\n".
13188: '<input type="hidden" name="archive_count" value="'.$count.'" />'.
13189: $hiddenelem.
1.1065 raeburn 13190: '<br /><input type="submit" name="archive_submit" value="'.$lt{'save'}.'" />'.
1.1055 raeburn 13191: '</form>';
13192: }
13193:
13194: sub archive_javascript {
1.1056 raeburn 13195: my ($startcount,$numitems,$titles,$children) = @_;
13196: return unless ((ref($titles) eq 'HASH') && (ref($children) eq 'HASH'));
1.1059 raeburn 13197: my $maintitle = $env{'form.comment'};
1.1055 raeburn 13198: my $scripttag = <<START;
13199: <script type="text/javascript">
13200: // <![CDATA[
13201:
13202: function checkAll(form,prefix) {
13203: var idstr = new RegExp("^archive_"+prefix+"_\\\\d+\$");
13204: for (var i=0; i < form.elements.length; i++) {
13205: var id = form.elements[i].id;
13206: if ((id != '') && (id != undefined)) {
13207: if (idstr.test(id)) {
13208: if (form.elements[i].type == 'radio') {
13209: form.elements[i].checked = true;
1.1056 raeburn 13210: var nostart = i-$startcount;
1.1059 raeburn 13211: var offset = nostart%7;
13212: var count = (nostart-offset)/7;
1.1056 raeburn 13213: dependencyCheck(form,count,offset);
1.1055 raeburn 13214: }
13215: }
13216: }
13217: }
13218: }
13219:
13220: function propagateCheck(form,count) {
13221: if (count > 0) {
1.1059 raeburn 13222: var startelement = $startcount + ((count-1) * 7);
13223: for (var j=1; j<6; j++) {
13224: if ((j != 2) && (j != 4)) {
1.1056 raeburn 13225: var item = startelement + j;
13226: if (form.elements[item].type == 'radio') {
13227: if (form.elements[item].checked) {
13228: containerCheck(form,count,j);
13229: break;
13230: }
1.1055 raeburn 13231: }
13232: }
13233: }
13234: }
13235: }
13236:
13237: numitems = $numitems
1.1056 raeburn 13238: var titles = new Array(numitems);
13239: var parents = new Array(numitems);
1.1055 raeburn 13240: for (var i=0; i<numitems; i++) {
1.1056 raeburn 13241: parents[i] = new Array;
1.1055 raeburn 13242: }
1.1059 raeburn 13243: var maintitle = '$maintitle';
1.1055 raeburn 13244:
13245: START
13246:
1.1056 raeburn 13247: foreach my $container (sort { $a <=> $b } (keys(%{$children}))) {
13248: my @contents = split(/:/,$children->{$container});
1.1055 raeburn 13249: for (my $i=0; $i<@contents; $i ++) {
13250: $scripttag .= 'parents['.$container.']['.$i.'] = '.$contents[$i]."\n";
13251: }
13252: }
13253:
1.1056 raeburn 13254: foreach my $key (sort { $a <=> $b } (keys(%{$titles}))) {
13255: $scripttag .= "titles[$key] = '".$titles->{$key}."';\n";
13256: }
13257:
1.1055 raeburn 13258: $scripttag .= <<END;
13259:
13260: function containerCheck(form,count,offset) {
13261: if (count > 0) {
1.1056 raeburn 13262: dependencyCheck(form,count,offset);
1.1059 raeburn 13263: var item = (offset+$startcount)+7*(count-1);
1.1055 raeburn 13264: form.elements[item].checked = true;
13265: if(Object.prototype.toString.call(parents[count]) === '[object Array]') {
13266: if (parents[count].length > 0) {
13267: for (var j=0; j<parents[count].length; j++) {
1.1056 raeburn 13268: containerCheck(form,parents[count][j],offset);
13269: }
13270: }
13271: }
13272: }
13273: }
13274:
13275: function dependencyCheck(form,count,offset) {
13276: if (count > 0) {
1.1059 raeburn 13277: var chosen = (offset+$startcount)+7*(count-1);
13278: var depitem = $startcount + ((count-1) * 7) + 4;
1.1056 raeburn 13279: var currtype = form.elements[depitem].type;
13280: if (form.elements[chosen].value == 'dependency') {
13281: document.getElementById('arc_depon_'+count).style.display='block';
13282: form.elements[depitem].options.length = 0;
13283: form.elements[depitem].options[0] = new Option('Select','',true,true);
1.1085 raeburn 13284: for (var i=1; i<=numitems; i++) {
13285: if (i == count) {
13286: continue;
13287: }
1.1059 raeburn 13288: var startelement = $startcount + (i-1) * 7;
13289: for (var j=1; j<6; j++) {
13290: if ((j != 2) && (j!= 4)) {
1.1056 raeburn 13291: var item = startelement + j;
13292: if (form.elements[item].type == 'radio') {
13293: if (form.elements[item].checked) {
13294: if (form.elements[item].value == 'display') {
13295: var n = form.elements[depitem].options.length;
13296: form.elements[depitem].options[n] = new Option(titles[i],i,false,false);
13297: }
13298: }
13299: }
13300: }
13301: }
13302: }
13303: } else {
13304: document.getElementById('arc_depon_'+count).style.display='none';
13305: form.elements[depitem].options.length = 0;
13306: form.elements[depitem].options[0] = new Option('Select','',true,true);
13307: }
1.1059 raeburn 13308: titleCheck(form,count,offset);
1.1056 raeburn 13309: }
13310: }
13311:
13312: function propagateSelect(form,count,offset) {
13313: if (count > 0) {
1.1065 raeburn 13314: var item = (1+offset+$startcount)+7*(count-1);
1.1056 raeburn 13315: var picked = form.elements[item].options[form.elements[item].selectedIndex].value;
13316: if (Object.prototype.toString.call(parents[count]) === '[object Array]') {
13317: if (parents[count].length > 0) {
13318: for (var j=0; j<parents[count].length; j++) {
13319: containerSelect(form,parents[count][j],offset,picked);
1.1055 raeburn 13320: }
13321: }
13322: }
13323: }
13324: }
1.1056 raeburn 13325:
13326: function containerSelect(form,count,offset,picked) {
13327: if (count > 0) {
1.1065 raeburn 13328: var item = (offset+$startcount)+7*(count-1);
1.1056 raeburn 13329: if (form.elements[item].type == 'radio') {
13330: if (form.elements[item].value == 'dependency') {
13331: if (form.elements[item+1].type == 'select-one') {
13332: for (var i=0; i<form.elements[item+1].options.length; i++) {
13333: if (form.elements[item+1].options[i].value == picked) {
13334: form.elements[item+1].selectedIndex = i;
13335: break;
13336: }
13337: }
13338: }
13339: if (Object.prototype.toString.call(parents[count]) === '[object Array]') {
13340: if (parents[count].length > 0) {
13341: for (var j=0; j<parents[count].length; j++) {
13342: containerSelect(form,parents[count][j],offset,picked);
13343: }
13344: }
13345: }
13346: }
13347: }
13348: }
13349: }
13350:
1.1059 raeburn 13351: function titleCheck(form,count,offset) {
13352: if (count > 0) {
13353: var chosen = (offset+$startcount)+7*(count-1);
13354: var depitem = $startcount + ((count-1) * 7) + 2;
13355: var currtype = form.elements[depitem].type;
13356: if (form.elements[chosen].value == 'display') {
13357: document.getElementById('arc_title_'+count).style.display='block';
13358: if ((count==1) && ((parents[count].length > 0) || (numitems == 1))) {
13359: document.getElementById('archive_title_'+count).value=maintitle;
13360: }
13361: } else {
13362: document.getElementById('arc_title_'+count).style.display='none';
13363: if (currtype == 'text') {
13364: document.getElementById('archive_title_'+count).value='';
13365: }
13366: }
13367: }
13368: return;
13369: }
13370:
1.1055 raeburn 13371: // ]]>
13372: </script>
13373: END
13374: return $scripttag;
13375: }
13376:
13377: sub process_extracted_files {
1.1067 raeburn 13378: my ($context,$docudom,$docuname,$destination,$dir_root,$hiddenelem) = @_;
1.1055 raeburn 13379: my $numitems = $env{'form.archive_count'};
1.1294 raeburn 13380: return if ((!$numitems) || ($numitems =~ /\D/));
1.1055 raeburn 13381: my @ids=&Apache::lonnet::current_machine_ids();
13382: my ($prefix,$pathtocheck,$dir,$ishome,$error,$warning,%toplevelitems,%is_dir,
1.1067 raeburn 13383: %folders,%containers,%mapinner,%prompttofetch);
1.1055 raeburn 13384: my $docuhome = &Apache::lonnet::homeserver($docuname,$docudom);
13385: if (grep(/^\Q$docuhome\E$/,@ids)) {
13386: $prefix = &LONCAPA::propath($docudom,$docuname);
13387: $pathtocheck = "$dir_root/$destination";
13388: $dir = $dir_root;
13389: $ishome = 1;
13390: } else {
13391: $prefix = $Apache::lonnet::perlvar{'lonDocRoot'};
13392: $pathtocheck = "$dir_root/$docudom/$docuname/$destination";
1.1294 raeburn 13393: $dir = "$dir_root/$docudom/$docuname";
1.1055 raeburn 13394: }
13395: my $currdir = "$dir_root/$destination";
13396: (my $docstype,$mapinner{'0'}) = ($destination =~ m{^(docs|supplemental)/(\w+)/});
13397: if ($env{'form.folderpath'}) {
13398: my @items = split('&',$env{'form.folderpath'});
13399: $folders{'0'} = $items[-2];
1.1099 raeburn 13400: if ($env{'form.folderpath'} =~ /\:1$/) {
13401: $containers{'0'}='page';
13402: } else {
13403: $containers{'0'}='sequence';
13404: }
1.1055 raeburn 13405: }
13406: my @archdirs = &get_env_multiple('form.archive_directory');
13407: if ($numitems) {
13408: for (my $i=1; $i<=$numitems; $i++) {
13409: my $path = $env{'form.archive_content_'.$i};
13410: if ($path =~ m{^\Q$pathtocheck\E/([^/]+)$}) {
13411: my $item = $1;
13412: $toplevelitems{$item} = $i;
13413: if (grep(/^\Q$i\E$/,@archdirs)) {
13414: $is_dir{$item} = 1;
13415: }
13416: }
13417: }
13418: }
1.1067 raeburn 13419: my ($output,%children,%parent,%titles,%dirorder,$result);
1.1055 raeburn 13420: if (keys(%toplevelitems) > 0) {
13421: my @contents = sort(keys(%toplevelitems));
1.1056 raeburn 13422: (my $count,undef) = &get_extracted($docudom,$docuname,$currdir,\%is_dir,\%children,
13423: \%parent,\@contents,\%dirorder,\%titles);
1.1055 raeburn 13424: }
1.1066 raeburn 13425: my (%referrer,%orphaned,%todelete,%todeletedir,%newdest,%newseqid);
1.1055 raeburn 13426: if ($numitems) {
13427: for (my $i=1; $i<=$numitems; $i++) {
1.1086 raeburn 13428: next if ($env{'form.archive_'.$i} eq 'dependency');
1.1055 raeburn 13429: my $path = $env{'form.archive_content_'.$i};
13430: if ($path =~ /^\Q$pathtocheck\E/) {
13431: if ($env{'form.archive_'.$i} eq 'discard') {
13432: if ($prefix ne '' && $path ne '') {
13433: if (-e $prefix.$path) {
1.1066 raeburn 13434: if ((@archdirs > 0) &&
13435: (grep(/^\Q$i\E$/,@archdirs))) {
13436: $todeletedir{$prefix.$path} = 1;
13437: } else {
13438: $todelete{$prefix.$path} = 1;
13439: }
1.1055 raeburn 13440: }
13441: }
13442: } elsif ($env{'form.archive_'.$i} eq 'display') {
1.1059 raeburn 13443: my ($docstitle,$title,$url,$outer);
1.1055 raeburn 13444: ($title) = ($path =~ m{/([^/]+)$});
1.1059 raeburn 13445: $docstitle = $env{'form.archive_title_'.$i};
13446: if ($docstitle eq '') {
13447: $docstitle = $title;
13448: }
1.1055 raeburn 13449: $outer = 0;
1.1056 raeburn 13450: if (ref($dirorder{$i}) eq 'ARRAY') {
13451: if (@{$dirorder{$i}} > 0) {
13452: foreach my $item (reverse(@{$dirorder{$i}})) {
1.1055 raeburn 13453: if ($env{'form.archive_'.$item} eq 'display') {
13454: $outer = $item;
13455: last;
13456: }
13457: }
13458: }
13459: }
13460: my ($errtext,$fatal) =
13461: &LONCAPA::map::mapread('/uploaded/'.$docudom.'/'.$docuname.
13462: '/'.$folders{$outer}.'.'.
13463: $containers{$outer});
13464: next if ($fatal);
13465: if ((@archdirs > 0) && (grep(/^\Q$i\E$/,@archdirs))) {
13466: if ($context eq 'coursedocs') {
1.1056 raeburn 13467: $mapinner{$i} = time;
1.1055 raeburn 13468: $folders{$i} = 'default_'.$mapinner{$i};
13469: $containers{$i} = 'sequence';
13470: my $url = '/uploaded/'.$docudom.'/'.$docuname.'/'.
13471: $folders{$i}.'.'.$containers{$i};
13472: my $newidx = &LONCAPA::map::getresidx();
13473: $LONCAPA::map::resources[$newidx]=
1.1059 raeburn 13474: $docstitle.':'.$url.':false:normal:res';
1.1055 raeburn 13475: push(@LONCAPA::map::order,$newidx);
13476: my ($outtext,$errtext) =
13477: &LONCAPA::map::storemap('/uploaded/'.$docudom.'/'.
13478: $docuname.'/'.$folders{$outer}.
1.1087 raeburn 13479: '.'.$containers{$outer},1,1);
1.1056 raeburn 13480: $newseqid{$i} = $newidx;
1.1067 raeburn 13481: unless ($errtext) {
1.1294 raeburn 13482: $result .= '<li>'.&mt('Folder: [_1] added to course',
13483: &HTML::Entities::encode($docstitle,'<>&"')).
13484: '</li>'."\n";
1.1067 raeburn 13485: }
1.1055 raeburn 13486: }
13487: } else {
13488: if ($context eq 'coursedocs') {
13489: my $newidx=&LONCAPA::map::getresidx();
13490: my $url = '/uploaded/'.$docudom.'/'.$docuname.'/'.
13491: $docstype.'/'.$mapinner{$outer}.'/'.$newidx.'/'.
13492: $title;
1.1294 raeburn 13493: if (($outer !~ /\D/) && ($mapinner{$outer} !~ /\D/) && ($newidx !~ /\D/)) {
13494: if (!-e "$prefix$dir/$docstype/$mapinner{$outer}") {
13495: mkdir("$prefix$dir/$docstype/$mapinner{$outer}",0755);
13496: }
13497: if (!-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx") {
13498: mkdir("$prefix$dir/$docstype/$mapinner{$outer}/$newidx");
13499: }
13500: if (-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx") {
13501: if (rename("$prefix$path","$prefix$dir/$docstype/$mapinner{$outer}/$newidx/$title")) {
13502: $newdest{$i} = "$prefix$dir/$docstype/$mapinner{$outer}/$newidx";
13503: unless ($ishome) {
13504: my $fetch = "$newdest{$i}/$title";
13505: $fetch =~ s/^\Q$prefix$dir\E//;
13506: $prompttofetch{$fetch} = 1;
13507: }
1.1292 raeburn 13508: }
1.1067 raeburn 13509: }
1.1294 raeburn 13510: $LONCAPA::map::resources[$newidx]=
13511: $docstitle.':'.$url.':false:normal:res';
13512: push(@LONCAPA::map::order, $newidx);
13513: my ($outtext,$errtext)=
13514: &LONCAPA::map::storemap('/uploaded/'.$docudom.'/'.
13515: $docuname.'/'.$folders{$outer}.
13516: '.'.$containers{$outer},1,1);
13517: unless ($errtext) {
13518: if (-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx/$title") {
13519: $result .= '<li>'.&mt('File: [_1] added to course',
13520: &HTML::Entities::encode($docstitle,'<>&"')).
13521: '</li>'."\n";
13522: }
1.1067 raeburn 13523: }
1.1294 raeburn 13524: } else {
13525: $warning .= &mt('Item extracted from archive: [_1] has unexpected path.',
13526: &HTML::Entities::encode($path,'<>&"')).'<br />';
1.1296 raeburn 13527: }
1.1055 raeburn 13528: }
13529: }
1.1086 raeburn 13530: }
13531: } else {
1.1294 raeburn 13532: $warning .= &mt('Item extracted from archive: [_1] has unexpected path.',
13533: &HTML::Entities::encode($path,'<>&"')).'<br />';
1.1086 raeburn 13534: }
13535: }
13536: for (my $i=1; $i<=$numitems; $i++) {
13537: next unless ($env{'form.archive_'.$i} eq 'dependency');
13538: my $path = $env{'form.archive_content_'.$i};
13539: if ($path =~ /^\Q$pathtocheck\E/) {
13540: my ($title) = ($path =~ m{/([^/]+)$});
13541: $referrer{$i} = $env{'form.archive_dependent_on_'.$i};
13542: if ($env{'form.archive_'.$referrer{$i}} eq 'display') {
13543: if (ref($dirorder{$i}) eq 'ARRAY') {
13544: my ($itemidx,$fullpath,$relpath);
13545: if (ref($dirorder{$referrer{$i}}) eq 'ARRAY') {
13546: my $container = $dirorder{$referrer{$i}}->[-1];
1.1056 raeburn 13547: for (my $j=0; $j<@{$dirorder{$i}}; $j++) {
1.1086 raeburn 13548: if ($dirorder{$i}->[$j] eq $container) {
13549: $itemidx = $j;
1.1056 raeburn 13550: }
13551: }
1.1086 raeburn 13552: }
13553: if ($itemidx eq '') {
13554: $itemidx = 0;
13555: }
13556: if (grep(/^\Q$referrer{$i}\E$/,@archdirs)) {
13557: if ($mapinner{$referrer{$i}}) {
13558: $fullpath = "$prefix$dir/$docstype/$mapinner{$referrer{$i}}";
13559: for (my $j=$itemidx; $j<@{$dirorder{$i}}; $j++) {
13560: if (grep(/^\Q$dirorder{$i}->[$j]\E$/,@archdirs)) {
13561: unless (defined($newseqid{$dirorder{$i}->[$j]})) {
13562: $fullpath .= '/'.$titles{$dirorder{$i}->[$j]};
13563: $relpath .= '/'.$titles{$dirorder{$i}->[$j]};
13564: if (!-e $fullpath) {
13565: mkdir($fullpath,0755);
1.1056 raeburn 13566: }
13567: }
1.1086 raeburn 13568: } else {
13569: last;
1.1056 raeburn 13570: }
1.1086 raeburn 13571: }
13572: }
13573: } elsif ($newdest{$referrer{$i}}) {
13574: $fullpath = $newdest{$referrer{$i}};
13575: for (my $j=$itemidx; $j<@{$dirorder{$i}}; $j++) {
13576: if ($env{'form.archive_'.$dirorder{$i}->[$j]} eq 'discard') {
13577: $orphaned{$i} = $env{'form.archive_'.$dirorder{$i}->[$j]};
13578: last;
13579: } elsif (grep(/^\Q$dirorder{$i}->[$j]\E$/,@archdirs)) {
13580: unless (defined($newseqid{$dirorder{$i}->[$j]})) {
13581: $fullpath .= '/'.$titles{$dirorder{$i}->[$j]};
13582: $relpath .= '/'.$titles{$dirorder{$i}->[$j]};
13583: if (!-e $fullpath) {
13584: mkdir($fullpath,0755);
1.1056 raeburn 13585: }
13586: }
1.1086 raeburn 13587: } else {
13588: last;
1.1056 raeburn 13589: }
1.1055 raeburn 13590: }
13591: }
1.1086 raeburn 13592: if ($fullpath ne '') {
13593: if (-e "$prefix$path") {
1.1292 raeburn 13594: unless (rename("$prefix$path","$fullpath/$title")) {
13595: $warning .= &mt('Failed to rename dependency').'<br />';
13596: }
1.1086 raeburn 13597: }
13598: if (-e "$fullpath/$title") {
13599: my $showpath;
13600: if ($relpath ne '') {
13601: $showpath = "$relpath/$title";
13602: } else {
13603: $showpath = "/$title";
13604: }
1.1294 raeburn 13605: $result .= '<li>'.&mt('[_1] included as a dependency',
13606: &HTML::Entities::encode($showpath,'<>&"')).
13607: '</li>'."\n";
1.1292 raeburn 13608: unless ($ishome) {
13609: my $fetch = "$fullpath/$title";
13610: $fetch =~ s/^\Q$prefix$dir\E//;
13611: $prompttofetch{$fetch} = 1;
13612: }
1.1086 raeburn 13613: }
13614: }
1.1055 raeburn 13615: }
1.1086 raeburn 13616: } elsif ($env{'form.archive_'.$referrer{$i}} eq 'discard') {
13617: $warning .= &mt('[_1] is a dependency of [_2], which was discarded.',
1.1294 raeburn 13618: &HTML::Entities::encode($path,'<>&"'),
13619: &HTML::Entities::encode($env{'form.archive_content_'.$referrer{$i}},'<>&"')).
13620: '<br />';
1.1055 raeburn 13621: }
13622: } else {
1.1294 raeburn 13623: $warning .= &mt('Item extracted from archive: [_1] has unexpected path.',
1.1296 raeburn 13624: &HTML::Entities::encode($path)).'<br />';
1.1055 raeburn 13625: }
13626: }
13627: if (keys(%todelete)) {
13628: foreach my $key (keys(%todelete)) {
13629: unlink($key);
1.1066 raeburn 13630: }
13631: }
13632: if (keys(%todeletedir)) {
13633: foreach my $key (keys(%todeletedir)) {
13634: rmdir($key);
13635: }
13636: }
13637: foreach my $dir (sort(keys(%is_dir))) {
13638: if (($pathtocheck ne '') && ($dir ne '')) {
13639: &cleanup_empty_dirs($prefix."$pathtocheck/$dir");
1.1055 raeburn 13640: }
13641: }
1.1067 raeburn 13642: if ($result ne '') {
13643: $output .= '<ul>'."\n".
13644: $result."\n".
13645: '</ul>';
13646: }
13647: unless ($ishome) {
13648: my $replicationfail;
13649: foreach my $item (keys(%prompttofetch)) {
13650: my $fetchresult= &Apache::lonnet::reply('fetchuserfile:'.$item,$docuhome);
13651: unless ($fetchresult eq 'ok') {
13652: $replicationfail .= '<li>'.$item.'</li>'."\n";
13653: }
13654: }
13655: if ($replicationfail) {
13656: $output .= '<p class="LC_error">'.
13657: &mt('Course home server failed to retrieve:').'<ul>'.
13658: $replicationfail.
13659: '</ul></p>';
13660: }
13661: }
1.1055 raeburn 13662: } else {
13663: $warning = &mt('No items found in archive.');
13664: }
13665: if ($error) {
13666: $output .= '<p class="LC_error">'.&mt('Not extracted.').'<br />'.
13667: $error.'</p>'."\n";
13668: }
13669: if ($warning) {
13670: $output .= '<p class="LC_warning">'.$warning.'</p>'."\n";
13671: }
13672: return $output;
13673: }
13674:
1.1066 raeburn 13675: sub cleanup_empty_dirs {
13676: my ($path) = @_;
13677: if (($path ne '') && (-d $path)) {
13678: if (opendir(my $dirh,$path)) {
13679: my @dircontents = grep(!/^\./,readdir($dirh));
13680: my $numitems = 0;
13681: foreach my $item (@dircontents) {
13682: if (-d "$path/$item") {
1.1111 raeburn 13683: &cleanup_empty_dirs("$path/$item");
1.1066 raeburn 13684: if (-e "$path/$item") {
13685: $numitems ++;
13686: }
13687: } else {
13688: $numitems ++;
13689: }
13690: }
13691: if ($numitems == 0) {
13692: rmdir($path);
13693: }
13694: closedir($dirh);
13695: }
13696: }
13697: return;
13698: }
13699:
1.41 ng 13700: =pod
1.45 matthew 13701:
1.1162 raeburn 13702: =item * &get_folder_hierarchy()
1.1068 raeburn 13703:
13704: Provides hierarchy of names of folders/sub-folders containing the current
13705: item,
13706:
13707: Inputs: 3
13708: - $navmap - navmaps object
13709:
13710: - $map - url for map (either the trigger itself, or map containing
13711: the resource, which is the trigger).
13712:
13713: - $showitem - 1 => show title for map itself; 0 => do not show.
13714:
13715: Outputs: 1 @pathitems - array of folder/subfolder names.
13716:
13717: =cut
13718:
13719: sub get_folder_hierarchy {
13720: my ($navmap,$map,$showitem) = @_;
13721: my @pathitems;
13722: if (ref($navmap)) {
13723: my $mapres = $navmap->getResourceByUrl($map);
13724: if (ref($mapres)) {
13725: my $pcslist = $mapres->map_hierarchy();
13726: if ($pcslist ne '') {
13727: my @pcs = split(/,/,$pcslist);
13728: foreach my $pc (@pcs) {
13729: if ($pc == 1) {
1.1129 raeburn 13730: push(@pathitems,&mt('Main Content'));
1.1068 raeburn 13731: } else {
13732: my $res = $navmap->getByMapPc($pc);
13733: if (ref($res)) {
13734: my $title = $res->compTitle();
13735: $title =~ s/\W+/_/g;
13736: if ($title ne '') {
13737: push(@pathitems,$title);
13738: }
13739: }
13740: }
13741: }
13742: }
1.1071 raeburn 13743: if ($showitem) {
13744: if ($mapres->{ID} eq '0.0') {
1.1129 raeburn 13745: push(@pathitems,&mt('Main Content'));
1.1071 raeburn 13746: } else {
13747: my $maptitle = $mapres->compTitle();
13748: $maptitle =~ s/\W+/_/g;
13749: if ($maptitle ne '') {
13750: push(@pathitems,$maptitle);
13751: }
1.1068 raeburn 13752: }
13753: }
13754: }
13755: }
13756: return @pathitems;
13757: }
13758:
13759: =pod
13760:
1.1015 raeburn 13761: =item * &get_turnedin_filepath()
13762:
13763: Determines path in a user's portfolio file for storage of files uploaded
13764: to a specific essayresponse or dropbox item.
13765:
13766: Inputs: 3 required + 1 optional.
13767: $symb is symb for resource, $uname and $udom are for current user (required).
13768: $caller is optional (can be "submission", if routine is called when storing
13769: an upoaded file when "Submit Answer" button was pressed).
13770:
13771: Returns array containing $path and $multiresp.
13772: $path is path in portfolio. $multiresp is 1 if this resource contains more
13773: than one file upload item. Callers of routine should append partid as a
13774: subdirectory to $path in cases where $multiresp is 1.
13775:
13776: Called by: homework/essayresponse.pm and homework/structuretags.pm
13777:
13778: =cut
13779:
13780: sub get_turnedin_filepath {
13781: my ($symb,$uname,$udom,$caller) = @_;
13782: my ($map,$resid,$resurl)=&Apache::lonnet::decode_symb($symb);
13783: my $turnindir;
13784: my %userhash = &Apache::lonnet::userenvironment($udom,$uname,'turnindir');
13785: $turnindir = $userhash{'turnindir'};
13786: my ($path,$multiresp);
13787: if ($turnindir eq '') {
13788: if ($caller eq 'submission') {
13789: $turnindir = &mt('turned in');
13790: $turnindir =~ s/\W+/_/g;
13791: my %newhash = (
13792: 'turnindir' => $turnindir,
13793: );
13794: &Apache::lonnet::put('environment',\%newhash,$udom,$uname);
13795: }
13796: }
13797: if ($turnindir ne '') {
13798: $path = '/'.$turnindir.'/';
13799: my ($multipart,$turnin,@pathitems);
13800: my $navmap = Apache::lonnavmaps::navmap->new();
13801: if (defined($navmap)) {
13802: my $mapres = $navmap->getResourceByUrl($map);
13803: if (ref($mapres)) {
13804: my $pcslist = $mapres->map_hierarchy();
13805: if ($pcslist ne '') {
13806: foreach my $pc (split(/,/,$pcslist)) {
13807: my $res = $navmap->getByMapPc($pc);
13808: if (ref($res)) {
13809: my $title = $res->compTitle();
13810: $title =~ s/\W+/_/g;
13811: if ($title ne '') {
1.1149 raeburn 13812: if (($pc > 1) && (length($title) > 12)) {
13813: $title = substr($title,0,12);
13814: }
1.1015 raeburn 13815: push(@pathitems,$title);
13816: }
13817: }
13818: }
13819: }
13820: my $maptitle = $mapres->compTitle();
13821: $maptitle =~ s/\W+/_/g;
13822: if ($maptitle ne '') {
1.1149 raeburn 13823: if (length($maptitle) > 12) {
13824: $maptitle = substr($maptitle,0,12);
13825: }
1.1015 raeburn 13826: push(@pathitems,$maptitle);
13827: }
13828: unless ($env{'request.state'} eq 'construct') {
13829: my $res = $navmap->getBySymb($symb);
13830: if (ref($res)) {
13831: my $partlist = $res->parts();
13832: my $totaluploads = 0;
13833: if (ref($partlist) eq 'ARRAY') {
13834: foreach my $part (@{$partlist}) {
13835: my @types = $res->responseType($part);
13836: my @ids = $res->responseIds($part);
13837: for (my $i=0; $i < scalar(@ids); $i++) {
13838: if ($types[$i] eq 'essay') {
13839: my $partid = $part.'_'.$ids[$i];
13840: if (&Apache::lonnet::EXT("resource.$partid.uploadedfiletypes") ne '') {
13841: $totaluploads ++;
13842: }
13843: }
13844: }
13845: }
13846: if ($totaluploads > 1) {
13847: $multiresp = 1;
13848: }
13849: }
13850: }
13851: }
13852: } else {
13853: return;
13854: }
13855: } else {
13856: return;
13857: }
13858: my $restitle=&Apache::lonnet::gettitle($symb);
13859: $restitle =~ s/\W+/_/g;
13860: if ($restitle eq '') {
13861: $restitle = ($resurl =~ m{/[^/]+$});
13862: if ($restitle eq '') {
13863: $restitle = time;
13864: }
13865: }
1.1149 raeburn 13866: if (length($restitle) > 12) {
13867: $restitle = substr($restitle,0,12);
13868: }
1.1015 raeburn 13869: push(@pathitems,$restitle);
13870: $path .= join('/',@pathitems);
13871: }
13872: return ($path,$multiresp);
13873: }
13874:
13875: =pod
13876:
1.464 albertel 13877: =back
1.41 ng 13878:
1.112 bowersj2 13879: =head1 CSV Upload/Handling functions
1.38 albertel 13880:
1.41 ng 13881: =over 4
13882:
1.648 raeburn 13883: =item * &upfile_store($r)
1.41 ng 13884:
13885: Store uploaded file, $r should be the HTTP Request object,
1.258 albertel 13886: needs $env{'form.upfile'}
1.41 ng 13887: returns $datatoken to be put into hidden field
13888:
13889: =cut
1.31 albertel 13890:
13891: sub upfile_store {
13892: my $r=shift;
1.258 albertel 13893: $env{'form.upfile'}=~s/\r/\n/gs;
13894: $env{'form.upfile'}=~s/\f/\n/gs;
13895: $env{'form.upfile'}=~s/\n+/\n/gs;
13896: $env{'form.upfile'}=~s/\n+$//gs;
1.31 albertel 13897:
1.1299 raeburn 13898: my $datatoken = &valid_datatoken($env{'user.name'}.'_'.$env{'user.domain'}.
13899: '_enroll_'.$env{'request.course.id'}.'_'.
13900: time.'_'.$$);
13901: return if ($datatoken eq '');
13902:
1.31 albertel 13903: {
1.158 raeburn 13904: my $datafile = $r->dir_config('lonDaemons').
13905: '/tmp/'.$datatoken.'.tmp';
1.1317 raeburn 13906: if ( open(my $fh,'>',$datafile) ) {
1.258 albertel 13907: print $fh $env{'form.upfile'};
1.158 raeburn 13908: close($fh);
13909: }
1.31 albertel 13910: }
13911: return $datatoken;
13912: }
13913:
1.56 matthew 13914: =pod
13915:
1.1290 raeburn 13916: =item * &load_tmp_file($r,$datatoken)
1.41 ng 13917:
13918: Load uploaded file from tmp, $r should be the HTTP Request object,
1.1290 raeburn 13919: $datatoken is the name to assign to the temporary file.
1.258 albertel 13920: sets $env{'form.upfile'} to the contents of the file
1.41 ng 13921:
13922: =cut
1.31 albertel 13923:
13924: sub load_tmp_file {
1.1290 raeburn 13925: my ($r,$datatoken) = @_;
13926: return if ($datatoken eq '');
1.31 albertel 13927: my @studentdata=();
13928: {
1.158 raeburn 13929: my $studentfile = $r->dir_config('lonDaemons').
1.1290 raeburn 13930: '/tmp/'.$datatoken.'.tmp';
1.1317 raeburn 13931: if ( open(my $fh,'<',$studentfile) ) {
1.158 raeburn 13932: @studentdata=<$fh>;
13933: close($fh);
13934: }
1.31 albertel 13935: }
1.258 albertel 13936: $env{'form.upfile'}=join('',@studentdata);
1.31 albertel 13937: }
13938:
1.1290 raeburn 13939: sub valid_datatoken {
13940: my ($datatoken) = @_;
1.1325 raeburn 13941: if ($datatoken =~ /^$match_username\_$match_domain\_enroll_(|$match_domain\_$match_courseid)\_\d+_\d+$/) {
1.1290 raeburn 13942: return $datatoken;
13943: }
13944: return;
13945: }
13946:
1.56 matthew 13947: =pod
13948:
1.648 raeburn 13949: =item * &upfile_record_sep()
1.41 ng 13950:
13951: Separate uploaded file into records
13952: returns array of records,
1.258 albertel 13953: needs $env{'form.upfile'} and $env{'form.upfiletype'}
1.41 ng 13954:
13955: =cut
1.31 albertel 13956:
13957: sub upfile_record_sep {
1.258 albertel 13958: if ($env{'form.upfiletype'} eq 'xml') {
1.31 albertel 13959: } else {
1.248 albertel 13960: my @records;
1.258 albertel 13961: foreach my $line (split(/\n/,$env{'form.upfile'})) {
1.248 albertel 13962: if ($line=~/^\s*$/) { next; }
13963: push(@records,$line);
13964: }
13965: return @records;
1.31 albertel 13966: }
13967: }
13968:
1.56 matthew 13969: =pod
13970:
1.648 raeburn 13971: =item * &record_sep($record)
1.41 ng 13972:
1.258 albertel 13973: Separate a record into fields $record should be an item from the upfile_record_sep(), needs $env{'form.upfiletype'}
1.41 ng 13974:
13975: =cut
13976:
1.263 www 13977: sub takeleft {
13978: my $index=shift;
13979: return substr('0000'.$index,-4,4);
13980: }
13981:
1.31 albertel 13982: sub record_sep {
13983: my $record=shift;
13984: my %components=();
1.258 albertel 13985: if ($env{'form.upfiletype'} eq 'xml') {
13986: } elsif ($env{'form.upfiletype'} eq 'space') {
1.31 albertel 13987: my $i=0;
1.356 albertel 13988: foreach my $field (split(/\s+/,$record)) {
1.31 albertel 13989: $field=~s/^(\"|\')//;
13990: $field=~s/(\"|\')$//;
1.263 www 13991: $components{&takeleft($i)}=$field;
1.31 albertel 13992: $i++;
13993: }
1.258 albertel 13994: } elsif ($env{'form.upfiletype'} eq 'tab') {
1.31 albertel 13995: my $i=0;
1.356 albertel 13996: foreach my $field (split(/\t/,$record)) {
1.31 albertel 13997: $field=~s/^(\"|\')//;
13998: $field=~s/(\"|\')$//;
1.263 www 13999: $components{&takeleft($i)}=$field;
1.31 albertel 14000: $i++;
14001: }
14002: } else {
1.561 www 14003: my $separator=',';
1.480 banghart 14004: if ($env{'form.upfiletype'} eq 'semisv') {
1.561 www 14005: $separator=';';
1.480 banghart 14006: }
1.31 albertel 14007: my $i=0;
1.561 www 14008: # the character we are looking for to indicate the end of a quote or a record
14009: my $looking_for=$separator;
14010: # do not add the characters to the fields
14011: my $ignore=0;
14012: # we just encountered a separator (or the beginning of the record)
14013: my $just_found_separator=1;
14014: # store the field we are working on here
14015: my $field='';
14016: # work our way through all characters in record
14017: foreach my $character ($record=~/(.)/g) {
14018: if ($character eq $looking_for) {
14019: if ($character ne $separator) {
14020: # Found the end of a quote, again looking for separator
14021: $looking_for=$separator;
14022: $ignore=1;
14023: } else {
14024: # Found a separator, store away what we got
14025: $components{&takeleft($i)}=$field;
14026: $i++;
14027: $just_found_separator=1;
14028: $ignore=0;
14029: $field='';
14030: }
14031: next;
14032: }
14033: # single or double quotation marks after a separator indicate beginning of a quote
14034: # we are now looking for the end of the quote and need to ignore separators
14035: if ((($character eq '"') || ($character eq "'")) && ($just_found_separator)) {
14036: $looking_for=$character;
14037: next;
14038: }
14039: # ignore would be true after we reached the end of a quote
14040: if ($ignore) { next; }
14041: if (($just_found_separator) && ($character=~/\s/)) { next; }
14042: $field.=$character;
14043: $just_found_separator=0;
1.31 albertel 14044: }
1.561 www 14045: # catch the very last entry, since we never encountered the separator
14046: $components{&takeleft($i)}=$field;
1.31 albertel 14047: }
14048: return %components;
14049: }
14050:
1.144 matthew 14051: ######################################################
14052: ######################################################
14053:
1.56 matthew 14054: =pod
14055:
1.648 raeburn 14056: =item * &upfile_select_html()
1.41 ng 14057:
1.144 matthew 14058: Return HTML code to select a file from the users machine and specify
14059: the file type.
1.41 ng 14060:
14061: =cut
14062:
1.144 matthew 14063: ######################################################
14064: ######################################################
1.31 albertel 14065: sub upfile_select_html {
1.144 matthew 14066: my %Types = (
14067: csv => &mt('CSV (comma separated values, spreadsheet)'),
1.480 banghart 14068: semisv => &mt('Semicolon separated values'),
1.144 matthew 14069: space => &mt('Space separated'),
14070: tab => &mt('Tabulator separated'),
14071: # xml => &mt('HTML/XML'),
14072: );
14073: my $Str = '<input type="file" name="upfile" size="50" />'.
1.727 riegler 14074: '<br />'.&mt('Type').': <select name="upfiletype">';
1.144 matthew 14075: foreach my $type (sort(keys(%Types))) {
14076: $Str .= '<option value="'.$type.'" >'.$Types{$type}."</option>\n";
14077: }
14078: $Str .= "</select>\n";
14079: return $Str;
1.31 albertel 14080: }
14081:
1.301 albertel 14082: sub get_samples {
14083: my ($records,$toget) = @_;
14084: my @samples=({});
14085: my $got=0;
14086: foreach my $rec (@$records) {
14087: my %temp = &record_sep($rec);
14088: if (! grep(/\S/, values(%temp))) { next; }
14089: if (%temp) {
14090: $samples[$got]=\%temp;
14091: $got++;
14092: if ($got == $toget) { last; }
14093: }
14094: }
14095: return \@samples;
14096: }
14097:
1.144 matthew 14098: ######################################################
14099: ######################################################
14100:
1.56 matthew 14101: =pod
14102:
1.648 raeburn 14103: =item * &csv_print_samples($r,$records)
1.41 ng 14104:
14105: Prints a table of sample values from each column uploaded $r is an
14106: Apache Request ref, $records is an arrayref from
14107: &Apache::loncommon::upfile_record_sep
14108:
14109: =cut
14110:
1.144 matthew 14111: ######################################################
14112: ######################################################
1.31 albertel 14113: sub csv_print_samples {
14114: my ($r,$records) = @_;
1.662 bisitz 14115: my $samples = &get_samples($records,5);
1.301 albertel 14116:
1.594 raeburn 14117: $r->print(&mt('Samples').'<br />'.&start_data_table().
14118: &start_data_table_header_row());
1.356 albertel 14119: foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
1.845 bisitz 14120: $r->print('<th>'.&mt('Column [_1]',($sample+1)).'</th>'); }
1.594 raeburn 14121: $r->print(&end_data_table_header_row());
1.301 albertel 14122: foreach my $hash (@$samples) {
1.594 raeburn 14123: $r->print(&start_data_table_row());
1.356 albertel 14124: foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
1.31 albertel 14125: $r->print('<td>');
1.356 albertel 14126: if (defined($$hash{$sample})) { $r->print($$hash{$sample}); }
1.31 albertel 14127: $r->print('</td>');
14128: }
1.594 raeburn 14129: $r->print(&end_data_table_row());
1.31 albertel 14130: }
1.594 raeburn 14131: $r->print(&end_data_table().'<br />'."\n");
1.31 albertel 14132: }
14133:
1.144 matthew 14134: ######################################################
14135: ######################################################
14136:
1.56 matthew 14137: =pod
14138:
1.648 raeburn 14139: =item * &csv_print_select_table($r,$records,$d)
1.41 ng 14140:
14141: Prints a table to create associations between values and table columns.
1.144 matthew 14142:
1.41 ng 14143: $r is an Apache Request ref,
14144: $records is an arrayref from &Apache::loncommon::upfile_record_sep,
1.174 matthew 14145: $d is an array of 2 element arrays (internal name, displayed name,defaultcol)
1.41 ng 14146:
14147: =cut
14148:
1.144 matthew 14149: ######################################################
14150: ######################################################
1.31 albertel 14151: sub csv_print_select_table {
14152: my ($r,$records,$d) = @_;
1.301 albertel 14153: my $i=0;
14154: my $samples = &get_samples($records,1);
1.144 matthew 14155: $r->print(&mt('Associate columns with student attributes.')."\n".
1.594 raeburn 14156: &start_data_table().&start_data_table_header_row().
1.144 matthew 14157: '<th>'.&mt('Attribute').'</th>'.
1.594 raeburn 14158: '<th>'.&mt('Column').'</th>'.
14159: &end_data_table_header_row()."\n");
1.356 albertel 14160: foreach my $array_ref (@$d) {
14161: my ($value,$display,$defaultcol)=@{ $array_ref };
1.729 raeburn 14162: $r->print(&start_data_table_row().'<td>'.$display.'</td>');
1.31 albertel 14163:
1.875 bisitz 14164: $r->print('<td><select name="f'.$i.'"'.
1.32 matthew 14165: ' onchange="javascript:flip(this.form,'.$i.');">');
1.31 albertel 14166: $r->print('<option value="none"></option>');
1.356 albertel 14167: foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
14168: $r->print('<option value="'.$sample.'"'.
14169: ($sample eq $defaultcol ? ' selected="selected" ' : '').
1.662 bisitz 14170: '>'.&mt('Column [_1]',($sample+1)).'</option>');
1.31 albertel 14171: }
1.594 raeburn 14172: $r->print('</select></td>'.&end_data_table_row()."\n");
1.31 albertel 14173: $i++;
14174: }
1.594 raeburn 14175: $r->print(&end_data_table());
1.31 albertel 14176: $i--;
14177: return $i;
14178: }
1.56 matthew 14179:
1.144 matthew 14180: ######################################################
14181: ######################################################
14182:
1.56 matthew 14183: =pod
1.31 albertel 14184:
1.648 raeburn 14185: =item * &csv_samples_select_table($r,$records,$d)
1.41 ng 14186:
14187: Prints a table of sample values from the upload and can make associate samples to internal names.
14188:
14189: $r is an Apache Request ref,
14190: $records is an arrayref from &Apache::loncommon::upfile_record_sep,
14191: $d is an array of 2 element arrays (internal name, displayed name)
14192:
14193: =cut
14194:
1.144 matthew 14195: ######################################################
14196: ######################################################
1.31 albertel 14197: sub csv_samples_select_table {
14198: my ($r,$records,$d) = @_;
14199: my $i=0;
1.144 matthew 14200: #
1.662 bisitz 14201: my $max_samples = 5;
14202: my $samples = &get_samples($records,$max_samples);
1.594 raeburn 14203: $r->print(&start_data_table().
14204: &start_data_table_header_row().'<th>'.
14205: &mt('Field').'</th><th>'.&mt('Samples').'</th>'.
14206: &end_data_table_header_row());
1.301 albertel 14207:
14208: foreach my $key (sort(keys(%{ $samples->[0] }))) {
1.594 raeburn 14209: $r->print(&start_data_table_row().'<td><select name="f'.$i.'"'.
1.32 matthew 14210: ' onchange="javascript:flip(this.form,'.$i.');">');
1.301 albertel 14211: foreach my $option (@$d) {
14212: my ($value,$display,$defaultcol)=@{ $option };
1.174 matthew 14213: $r->print('<option value="'.$value.'"'.
1.253 albertel 14214: ($i eq $defaultcol ? ' selected="selected" ':'').'>'.
1.174 matthew 14215: $display.'</option>');
1.31 albertel 14216: }
14217: $r->print('</select></td><td>');
1.662 bisitz 14218: foreach my $line (0..($max_samples-1)) {
1.301 albertel 14219: if (defined($samples->[$line]{$key})) {
14220: $r->print($samples->[$line]{$key}."<br />\n");
14221: }
14222: }
1.594 raeburn 14223: $r->print('</td>'.&end_data_table_row());
1.31 albertel 14224: $i++;
14225: }
1.594 raeburn 14226: $r->print(&end_data_table());
1.31 albertel 14227: $i--;
14228: return($i);
1.115 matthew 14229: }
14230:
1.144 matthew 14231: ######################################################
14232: ######################################################
14233:
1.115 matthew 14234: =pod
14235:
1.648 raeburn 14236: =item * &clean_excel_name($name)
1.115 matthew 14237:
14238: Returns a replacement for $name which does not contain any illegal characters.
14239:
14240: =cut
14241:
1.144 matthew 14242: ######################################################
14243: ######################################################
1.115 matthew 14244: sub clean_excel_name {
14245: my ($name) = @_;
14246: $name =~ s/[:\*\?\/\\]//g;
14247: if (length($name) > 31) {
14248: $name = substr($name,0,31);
14249: }
14250: return $name;
1.25 albertel 14251: }
1.84 albertel 14252:
1.85 albertel 14253: =pod
14254:
1.648 raeburn 14255: =item * &check_if_partid_hidden($id,$symb,$udom,$uname)
1.85 albertel 14256:
14257: Returns either 1 or undef
14258:
14259: 1 if the part is to be hidden, undef if it is to be shown
14260:
14261: Arguments are:
14262:
14263: $id the id of the part to be checked
14264: $symb, optional the symb of the resource to check
14265: $udom, optional the domain of the user to check for
14266: $uname, optional the username of the user to check for
14267:
14268: =cut
1.84 albertel 14269:
14270: sub check_if_partid_hidden {
14271: my ($id,$symb,$udom,$uname) = @_;
1.133 albertel 14272: my $hiddenparts=&Apache::lonnet::EXT('resource.0.hiddenparts',
1.84 albertel 14273: $symb,$udom,$uname);
1.141 albertel 14274: my $truth=1;
14275: #if the string starts with !, then the list is the list to show not hide
14276: if ($hiddenparts=~s/^\s*!//) { $truth=undef; }
1.84 albertel 14277: my @hiddenlist=split(/,/,$hiddenparts);
14278: foreach my $checkid (@hiddenlist) {
1.141 albertel 14279: if ($checkid =~ /^\s*\Q$id\E\s*$/) { return $truth; }
1.84 albertel 14280: }
1.141 albertel 14281: return !$truth;
1.84 albertel 14282: }
1.127 matthew 14283:
1.138 matthew 14284:
14285: ############################################################
14286: ############################################################
14287:
14288: =pod
14289:
1.157 matthew 14290: =back
14291:
1.138 matthew 14292: =head1 cgi-bin script and graphing routines
14293:
1.157 matthew 14294: =over 4
14295:
1.648 raeburn 14296: =item * &get_cgi_id()
1.138 matthew 14297:
14298: Inputs: none
14299:
14300: Returns an id which can be used to pass environment variables
14301: to various cgi-bin scripts. These environment variables will
14302: be removed from the users environment after a given time by
14303: the routine &Apache::lonnet::transfer_profile_to_env.
14304:
14305: =cut
14306:
14307: ############################################################
14308: ############################################################
1.152 albertel 14309: my $uniq=0;
1.136 matthew 14310: sub get_cgi_id {
1.154 albertel 14311: $uniq=($uniq+1)%100000;
1.280 albertel 14312: return (time.'_'.$$.'_'.$uniq);
1.136 matthew 14313: }
14314:
1.127 matthew 14315: ############################################################
14316: ############################################################
14317:
14318: =pod
14319:
1.648 raeburn 14320: =item * &DrawBarGraph()
1.127 matthew 14321:
1.138 matthew 14322: Facilitates the plotting of data in a (stacked) bar graph.
14323: Puts plot definition data into the users environment in order for
14324: graph.png to plot it. Returns an <img> tag for the plot.
14325: The bars on the plot are labeled '1','2',...,'n'.
14326:
14327: Inputs:
14328:
14329: =over 4
14330:
14331: =item $Title: string, the title of the plot
14332:
14333: =item $xlabel: string, text describing the X-axis of the plot
14334:
14335: =item $ylabel: string, text describing the Y-axis of the plot
14336:
14337: =item $Max: scalar, the maximum Y value to use in the plot
14338: If $Max is < any data point, the graph will not be rendered.
14339:
1.140 matthew 14340: =item $colors: array ref holding the colors to be used for the data sets when
1.138 matthew 14341: they are plotted. If undefined, default values will be used.
14342:
1.178 matthew 14343: =item $labels: array ref holding the labels to use on the x-axis for the bars.
14344:
1.138 matthew 14345: =item @Values: An array of array references. Each array reference holds data
14346: to be plotted in a stacked bar chart.
14347:
1.239 matthew 14348: =item If the final element of @Values is a hash reference the key/value
14349: pairs will be added to the graph definition.
14350:
1.138 matthew 14351: =back
14352:
14353: Returns:
14354:
14355: An <img> tag which references graph.png and the appropriate identifying
14356: information for the plot.
14357:
1.127 matthew 14358: =cut
14359:
14360: ############################################################
14361: ############################################################
1.134 matthew 14362: sub DrawBarGraph {
1.178 matthew 14363: my ($Title,$xlabel,$ylabel,$Max,$colors,$labels,@Values)=@_;
1.134 matthew 14364: #
14365: if (! defined($colors)) {
14366: $colors = ['#33ff00',
14367: '#0033cc', '#990000', '#aaaa66', '#663399', '#ff9933',
14368: '#66ccff', '#ff9999', '#cccc33', '#660000', '#33cc66',
14369: ];
14370: }
1.228 matthew 14371: my $extra_settings = {};
14372: if (ref($Values[-1]) eq 'HASH') {
14373: $extra_settings = pop(@Values);
14374: }
1.127 matthew 14375: #
1.136 matthew 14376: my $identifier = &get_cgi_id();
14377: my $id = 'cgi.'.$identifier;
1.129 matthew 14378: if (! @Values || ref($Values[0]) ne 'ARRAY') {
1.127 matthew 14379: return '';
14380: }
1.225 matthew 14381: #
14382: my @Labels;
14383: if (defined($labels)) {
14384: @Labels = @$labels;
14385: } else {
14386: for (my $i=0;$i<@{$Values[0]};$i++) {
1.1263 raeburn 14387: push(@Labels,$i+1);
1.225 matthew 14388: }
14389: }
14390: #
1.129 matthew 14391: my $NumBars = scalar(@{$Values[0]});
1.225 matthew 14392: if ($NumBars < scalar(@Labels)) { $NumBars = scalar(@Labels); }
1.129 matthew 14393: my %ValuesHash;
14394: my $NumSets=1;
14395: foreach my $array (@Values) {
14396: next if (! ref($array));
1.136 matthew 14397: $ValuesHash{$id.'.data.'.$NumSets++} =
1.132 matthew 14398: join(',',@$array);
1.129 matthew 14399: }
1.127 matthew 14400: #
1.136 matthew 14401: my ($height,$width,$xskip,$bar_width) = (200,120,1,15);
1.225 matthew 14402: if ($NumBars < 3) {
14403: $width = 120+$NumBars*32;
1.220 matthew 14404: $xskip = 1;
1.225 matthew 14405: $bar_width = 30;
14406: } elsif ($NumBars < 5) {
14407: $width = 120+$NumBars*20;
14408: $xskip = 1;
14409: $bar_width = 20;
1.220 matthew 14410: } elsif ($NumBars < 10) {
1.136 matthew 14411: $width = 120+$NumBars*15;
14412: $xskip = 1;
14413: $bar_width = 15;
14414: } elsif ($NumBars <= 25) {
14415: $width = 120+$NumBars*11;
14416: $xskip = 5;
14417: $bar_width = 8;
14418: } elsif ($NumBars <= 50) {
14419: $width = 120+$NumBars*8;
14420: $xskip = 5;
14421: $bar_width = 4;
14422: } else {
14423: $width = 120+$NumBars*8;
14424: $xskip = 5;
14425: $bar_width = 4;
14426: }
14427: #
1.137 matthew 14428: $Max = 1 if ($Max < 1);
14429: if ( int($Max) < $Max ) {
14430: $Max++;
14431: $Max = int($Max);
14432: }
1.127 matthew 14433: $Title = '' if (! defined($Title));
14434: $xlabel = '' if (! defined($xlabel));
14435: $ylabel = '' if (! defined($ylabel));
1.369 www 14436: $ValuesHash{$id.'.title'} = &escape($Title);
14437: $ValuesHash{$id.'.xlabel'} = &escape($xlabel);
14438: $ValuesHash{$id.'.ylabel'} = &escape($ylabel);
1.137 matthew 14439: $ValuesHash{$id.'.y_max_value'} = $Max;
1.136 matthew 14440: $ValuesHash{$id.'.NumBars'} = $NumBars;
14441: $ValuesHash{$id.'.NumSets'} = $NumSets;
14442: $ValuesHash{$id.'.PlotType'} = 'bar';
14443: $ValuesHash{$id.'.Colors'} = join(',',@{$colors});
14444: $ValuesHash{$id.'.height'} = $height;
14445: $ValuesHash{$id.'.width'} = $width;
14446: $ValuesHash{$id.'.xskip'} = $xskip;
14447: $ValuesHash{$id.'.bar_width'} = $bar_width;
14448: $ValuesHash{$id.'.labels'} = join(',',@Labels);
1.127 matthew 14449: #
1.228 matthew 14450: # Deal with other parameters
14451: while (my ($key,$value) = each(%$extra_settings)) {
14452: $ValuesHash{$id.'.'.$key} = $value;
14453: }
14454: #
1.646 raeburn 14455: &Apache::lonnet::appenv(\%ValuesHash);
1.137 matthew 14456: return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
14457: }
14458:
14459: ############################################################
14460: ############################################################
14461:
14462: =pod
14463:
1.648 raeburn 14464: =item * &DrawXYGraph()
1.137 matthew 14465:
1.138 matthew 14466: Facilitates the plotting of data in an XY graph.
14467: Puts plot definition data into the users environment in order for
14468: graph.png to plot it. Returns an <img> tag for the plot.
14469:
14470: Inputs:
14471:
14472: =over 4
14473:
14474: =item $Title: string, the title of the plot
14475:
14476: =item $xlabel: string, text describing the X-axis of the plot
14477:
14478: =item $ylabel: string, text describing the Y-axis of the plot
14479:
14480: =item $Max: scalar, the maximum Y value to use in the plot
14481: If $Max is < any data point, the graph will not be rendered.
14482:
14483: =item $colors: Array ref containing the hex color codes for the data to be
14484: plotted in. If undefined, default values will be used.
14485:
14486: =item $Xlabels: Array ref containing the labels to be used for the X-axis.
14487:
14488: =item $Ydata: Array ref containing Array refs.
1.185 www 14489: Each of the contained arrays will be plotted as a separate curve.
1.138 matthew 14490:
14491: =item %Values: hash indicating or overriding any default values which are
14492: passed to graph.png.
14493: Possible values are: width, xskip, x_ticks, x_tick_offset, among others.
14494:
14495: =back
14496:
14497: Returns:
14498:
14499: An <img> tag which references graph.png and the appropriate identifying
14500: information for the plot.
14501:
1.137 matthew 14502: =cut
14503:
14504: ############################################################
14505: ############################################################
14506: sub DrawXYGraph {
14507: my ($Title,$xlabel,$ylabel,$Max,$colors,$Xlabels,$Ydata,%Values)=@_;
14508: #
14509: # Create the identifier for the graph
14510: my $identifier = &get_cgi_id();
14511: my $id = 'cgi.'.$identifier;
14512: #
14513: $Title = '' if (! defined($Title));
14514: $xlabel = '' if (! defined($xlabel));
14515: $ylabel = '' if (! defined($ylabel));
14516: my %ValuesHash =
14517: (
1.369 www 14518: $id.'.title' => &escape($Title),
14519: $id.'.xlabel' => &escape($xlabel),
14520: $id.'.ylabel' => &escape($ylabel),
1.137 matthew 14521: $id.'.y_max_value'=> $Max,
14522: $id.'.labels' => join(',',@$Xlabels),
14523: $id.'.PlotType' => 'XY',
14524: );
14525: #
14526: if (defined($colors) && ref($colors) eq 'ARRAY') {
14527: $ValuesHash{$id.'.Colors'} = join(',',@{$colors});
14528: }
14529: #
14530: if (! ref($Ydata) || ref($Ydata) ne 'ARRAY') {
14531: return '';
14532: }
14533: my $NumSets=1;
1.138 matthew 14534: foreach my $array (@{$Ydata}){
1.137 matthew 14535: next if (! ref($array));
14536: $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array);
14537: }
1.138 matthew 14538: $ValuesHash{$id.'.NumSets'} = $NumSets-1;
1.137 matthew 14539: #
14540: # Deal with other parameters
14541: while (my ($key,$value) = each(%Values)) {
14542: $ValuesHash{$id.'.'.$key} = $value;
1.127 matthew 14543: }
14544: #
1.646 raeburn 14545: &Apache::lonnet::appenv(\%ValuesHash);
1.136 matthew 14546: return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
14547: }
14548:
14549: ############################################################
14550: ############################################################
14551:
14552: =pod
14553:
1.648 raeburn 14554: =item * &DrawXYYGraph()
1.138 matthew 14555:
14556: Facilitates the plotting of data in an XY graph with two Y axes.
14557: Puts plot definition data into the users environment in order for
14558: graph.png to plot it. Returns an <img> tag for the plot.
14559:
14560: Inputs:
14561:
14562: =over 4
14563:
14564: =item $Title: string, the title of the plot
14565:
14566: =item $xlabel: string, text describing the X-axis of the plot
14567:
14568: =item $ylabel: string, text describing the Y-axis of the plot
14569:
14570: =item $colors: Array ref containing the hex color codes for the data to be
14571: plotted in. If undefined, default values will be used.
14572:
14573: =item $Xlabels: Array ref containing the labels to be used for the X-axis.
14574:
14575: =item $Ydata1: The first data set
14576:
14577: =item $Min1: The minimum value of the left Y-axis
14578:
14579: =item $Max1: The maximum value of the left Y-axis
14580:
14581: =item $Ydata2: The second data set
14582:
14583: =item $Min2: The minimum value of the right Y-axis
14584:
14585: =item $Max2: The maximum value of the left Y-axis
14586:
14587: =item %Values: hash indicating or overriding any default values which are
14588: passed to graph.png.
14589: Possible values are: width, xskip, x_ticks, x_tick_offset, among others.
14590:
14591: =back
14592:
14593: Returns:
14594:
14595: An <img> tag which references graph.png and the appropriate identifying
14596: information for the plot.
1.136 matthew 14597:
14598: =cut
14599:
14600: ############################################################
14601: ############################################################
1.137 matthew 14602: sub DrawXYYGraph {
14603: my ($Title,$xlabel,$ylabel,$colors,$Xlabels,$Ydata1,$Min1,$Max1,
14604: $Ydata2,$Min2,$Max2,%Values)=@_;
1.136 matthew 14605: #
14606: # Create the identifier for the graph
14607: my $identifier = &get_cgi_id();
14608: my $id = 'cgi.'.$identifier;
14609: #
14610: $Title = '' if (! defined($Title));
14611: $xlabel = '' if (! defined($xlabel));
14612: $ylabel = '' if (! defined($ylabel));
14613: my %ValuesHash =
14614: (
1.369 www 14615: $id.'.title' => &escape($Title),
14616: $id.'.xlabel' => &escape($xlabel),
14617: $id.'.ylabel' => &escape($ylabel),
1.136 matthew 14618: $id.'.labels' => join(',',@$Xlabels),
14619: $id.'.PlotType' => 'XY',
14620: $id.'.NumSets' => 2,
1.137 matthew 14621: $id.'.two_axes' => 1,
14622: $id.'.y1_max_value' => $Max1,
14623: $id.'.y1_min_value' => $Min1,
14624: $id.'.y2_max_value' => $Max2,
14625: $id.'.y2_min_value' => $Min2,
1.136 matthew 14626: );
14627: #
1.137 matthew 14628: if (defined($colors) && ref($colors) eq 'ARRAY') {
14629: $ValuesHash{$id.'.Colors'} = join(',',@{$colors});
14630: }
14631: #
14632: if (! ref($Ydata1) || ref($Ydata1) ne 'ARRAY' ||
14633: ! ref($Ydata2) || ref($Ydata2) ne 'ARRAY'){
1.136 matthew 14634: return '';
14635: }
14636: my $NumSets=1;
1.137 matthew 14637: foreach my $array ($Ydata1,$Ydata2){
1.136 matthew 14638: next if (! ref($array));
14639: $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array);
1.137 matthew 14640: }
14641: #
14642: # Deal with other parameters
14643: while (my ($key,$value) = each(%Values)) {
14644: $ValuesHash{$id.'.'.$key} = $value;
1.136 matthew 14645: }
14646: #
1.646 raeburn 14647: &Apache::lonnet::appenv(\%ValuesHash);
1.130 albertel 14648: return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
1.139 matthew 14649: }
14650:
14651: ############################################################
14652: ############################################################
14653:
14654: =pod
14655:
1.157 matthew 14656: =back
14657:
1.139 matthew 14658: =head1 Statistics helper routines?
14659:
14660: Bad place for them but what the hell.
14661:
1.157 matthew 14662: =over 4
14663:
1.648 raeburn 14664: =item * &chartlink()
1.139 matthew 14665:
14666: Returns a link to the chart for a specific student.
14667:
14668: Inputs:
14669:
14670: =over 4
14671:
14672: =item $linktext: The text of the link
14673:
14674: =item $sname: The students username
14675:
14676: =item $sdomain: The students domain
14677:
14678: =back
14679:
1.157 matthew 14680: =back
14681:
1.139 matthew 14682: =cut
14683:
14684: ############################################################
14685: ############################################################
14686: sub chartlink {
14687: my ($linktext, $sname, $sdomain) = @_;
14688: my $link = '<a href="/adm/statistics?reportSelected=student_assessment'.
1.369 www 14689: '&SelectedStudent='.&escape($sname.':'.$sdomain).
1.219 albertel 14690: '&chartoutputmode='.HTML::Entities::encode('html, with all links').
1.139 matthew 14691: '">'.$linktext.'</a>';
1.153 matthew 14692: }
14693:
14694: #######################################################
14695: #######################################################
14696:
14697: =pod
14698:
14699: =head1 Course Environment Routines
1.157 matthew 14700:
14701: =over 4
1.153 matthew 14702:
1.648 raeburn 14703: =item * &restore_course_settings()
1.153 matthew 14704:
1.648 raeburn 14705: =item * &store_course_settings()
1.153 matthew 14706:
14707: Restores/Store indicated form parameters from the course environment.
14708: Will not overwrite existing values of the form parameters.
14709:
14710: Inputs:
14711: a scalar describing the data (e.g. 'chart', 'problem_analysis')
14712:
14713: a hash ref describing the data to be stored. For example:
14714:
14715: %Save_Parameters = ('Status' => 'scalar',
14716: 'chartoutputmode' => 'scalar',
14717: 'chartoutputdata' => 'scalar',
14718: 'Section' => 'array',
1.373 raeburn 14719: 'Group' => 'array',
1.153 matthew 14720: 'StudentData' => 'array',
14721: 'Maps' => 'array');
14722:
14723: Returns: both routines return nothing
14724:
1.631 raeburn 14725: =back
14726:
1.153 matthew 14727: =cut
14728:
14729: #######################################################
14730: #######################################################
14731: sub store_course_settings {
1.496 albertel 14732: return &store_settings($env{'request.course.id'},@_);
14733: }
14734:
14735: sub store_settings {
1.153 matthew 14736: # save to the environment
14737: # appenv the same items, just to be safe
1.300 albertel 14738: my $udom = $env{'user.domain'};
14739: my $uname = $env{'user.name'};
1.496 albertel 14740: my ($context,$prefix,$Settings) = @_;
1.153 matthew 14741: my %SaveHash;
14742: my %AppHash;
14743: while (my ($setting,$type) = each(%$Settings)) {
1.496 albertel 14744: my $basename = join('.','internal',$context,$prefix,$setting);
1.300 albertel 14745: my $envname = 'environment.'.$basename;
1.258 albertel 14746: if (exists($env{'form.'.$setting})) {
1.153 matthew 14747: # Save this value away
14748: if ($type eq 'scalar' &&
1.258 albertel 14749: (! exists($env{$envname}) ||
14750: $env{$envname} ne $env{'form.'.$setting})) {
14751: $SaveHash{$basename} = $env{'form.'.$setting};
14752: $AppHash{$envname} = $env{'form.'.$setting};
1.153 matthew 14753: } elsif ($type eq 'array') {
14754: my $stored_form;
1.258 albertel 14755: if (ref($env{'form.'.$setting})) {
1.153 matthew 14756: $stored_form = join(',',
14757: map {
1.369 www 14758: &escape($_);
1.258 albertel 14759: } sort(@{$env{'form.'.$setting}}));
1.153 matthew 14760: } else {
14761: $stored_form =
1.369 www 14762: &escape($env{'form.'.$setting});
1.153 matthew 14763: }
14764: # Determine if the array contents are the same.
1.258 albertel 14765: if ($stored_form ne $env{$envname}) {
1.153 matthew 14766: $SaveHash{$basename} = $stored_form;
14767: $AppHash{$envname} = $stored_form;
14768: }
14769: }
14770: }
14771: }
14772: my $put_result = &Apache::lonnet::put('environment',\%SaveHash,
1.300 albertel 14773: $udom,$uname);
1.153 matthew 14774: if ($put_result !~ /^(ok|delayed)/) {
14775: &Apache::lonnet::logthis('unable to save form parameters, '.
14776: 'got error:'.$put_result);
14777: }
14778: # Make sure these settings stick around in this session, too
1.646 raeburn 14779: &Apache::lonnet::appenv(\%AppHash);
1.153 matthew 14780: return;
14781: }
14782:
14783: sub restore_course_settings {
1.499 albertel 14784: return &restore_settings($env{'request.course.id'},@_);
1.496 albertel 14785: }
14786:
14787: sub restore_settings {
14788: my ($context,$prefix,$Settings) = @_;
1.153 matthew 14789: while (my ($setting,$type) = each(%$Settings)) {
1.258 albertel 14790: next if (exists($env{'form.'.$setting}));
1.496 albertel 14791: my $envname = 'environment.internal.'.$context.'.'.$prefix.
1.153 matthew 14792: '.'.$setting;
1.258 albertel 14793: if (exists($env{$envname})) {
1.153 matthew 14794: if ($type eq 'scalar') {
1.258 albertel 14795: $env{'form.'.$setting} = $env{$envname};
1.153 matthew 14796: } elsif ($type eq 'array') {
1.258 albertel 14797: $env{'form.'.$setting} = [
1.153 matthew 14798: map {
1.369 www 14799: &unescape($_);
1.258 albertel 14800: } split(',',$env{$envname})
1.153 matthew 14801: ];
14802: }
14803: }
14804: }
1.127 matthew 14805: }
14806:
1.618 raeburn 14807: #######################################################
14808: #######################################################
14809:
14810: =pod
14811:
14812: =head1 Domain E-mail Routines
14813:
14814: =over 4
14815:
1.648 raeburn 14816: =item * &build_recipient_list()
1.618 raeburn 14817:
1.1144 raeburn 14818: Build recipient lists for following types of e-mail:
1.766 raeburn 14819: (a) Error Reports, (b) Package Updates, (c) lonstatus warnings/errors
1.1144 raeburn 14820: (d) Help requests, (e) Course requests needing approval, (f) loncapa
14821: module change checking, student/employee ID conflict checks, as
14822: generated by lonerrorhandler.pm, CHECKRPMS, loncron,
14823: lonsupportreq.pm, loncoursequeueadmin.pm, searchcat.pl respectively.
1.618 raeburn 14824:
14825: Inputs:
1.619 raeburn 14826: defmail (scalar - email address of default recipient),
1.1144 raeburn 14827: mailing type (scalar: errormail, packagesmail, helpdeskmail,
14828: requestsmail, updatesmail, or idconflictsmail).
14829:
1.619 raeburn 14830: defdom (domain for which to retrieve configuration settings),
1.1144 raeburn 14831:
1.619 raeburn 14832: origmail (scalar - email address of recipient from loncapa.conf,
1.1297 raeburn 14833: i.e., predates configuration by DC via domainprefs.pm
14834:
14835: $requname username of requester (if mailing type is helpdeskmail)
14836:
14837: $requdom domain of requester (if mailing type is helpdeskmail)
14838:
14839: $reqemail e-mail address of requester (if mailing type is helpdeskmail)
14840:
1.618 raeburn 14841:
1.655 raeburn 14842: Returns: comma separated list of addresses to which to send e-mail.
14843:
14844: =back
1.618 raeburn 14845:
14846: =cut
14847:
14848: ############################################################
14849: ############################################################
14850: sub build_recipient_list {
1.1297 raeburn 14851: my ($defmail,$mailing,$defdom,$origmail,$requname,$requdom,$reqemail) = @_;
1.618 raeburn 14852: my @recipients;
1.1270 raeburn 14853: my ($otheremails,$lastresort,$allbcc,$addtext);
1.618 raeburn 14854: my %domconfig =
1.1270 raeburn 14855: &Apache::lonnet::get_dom('configuration',['contacts'],$defdom);
1.618 raeburn 14856: if (ref($domconfig{'contacts'}) eq 'HASH') {
1.766 raeburn 14857: if (exists($domconfig{'contacts'}{$mailing})) {
14858: if (ref($domconfig{'contacts'}{$mailing}) eq 'HASH') {
14859: my @contacts = ('adminemail','supportemail');
14860: foreach my $item (@contacts) {
14861: if ($domconfig{'contacts'}{$mailing}{$item}) {
14862: my $addr = $domconfig{'contacts'}{$item};
14863: if (!grep(/^\Q$addr\E$/,@recipients)) {
14864: push(@recipients,$addr);
14865: }
1.619 raeburn 14866: }
1.1270 raeburn 14867: }
14868: $otheremails = $domconfig{'contacts'}{$mailing}{'others'};
14869: if ($mailing eq 'helpdeskmail') {
14870: if ($domconfig{'contacts'}{$mailing}{'bcc'}) {
14871: my @bccs = split(/,/,$domconfig{'contacts'}{$mailing}{'bcc'});
14872: my @ok_bccs;
14873: foreach my $bcc (@bccs) {
14874: $bcc =~ s/^\s+//g;
14875: $bcc =~ s/\s+$//g;
14876: if ($bcc =~ m/^[^\@]+\@[^\@]+$/) {
14877: if (!(grep(/^\Q$bcc\E$/,@ok_bccs))) {
14878: push(@ok_bccs,$bcc);
14879: }
14880: }
14881: }
14882: if (@ok_bccs > 0) {
14883: $allbcc = join(', ',@ok_bccs);
14884: }
14885: }
14886: $addtext = $domconfig{'contacts'}{$mailing}{'include'};
1.618 raeburn 14887: }
14888: }
1.766 raeburn 14889: } elsif ($origmail ne '') {
1.1270 raeburn 14890: $lastresort = $origmail;
1.618 raeburn 14891: }
1.1297 raeburn 14892: if ($mailing eq 'helpdeskmail') {
14893: if ((ref($domconfig{'contacts'}{'overrides'}) eq 'HASH') &&
14894: (keys(%{$domconfig{'contacts'}{'overrides'}}))) {
14895: my ($inststatus,$inststatus_checked);
14896: if (($env{'user.name'} ne '') && ($env{'user.domain'} ne '') &&
14897: ($env{'user.domain'} ne 'public')) {
14898: $inststatus_checked = 1;
14899: $inststatus = $env{'environment.inststatus'};
14900: }
14901: unless ($inststatus_checked) {
14902: if (($requname ne '') && ($requdom ne '')) {
14903: if (($requname =~ /^$match_username$/) &&
14904: ($requdom =~ /^$match_domain$/) &&
14905: (&Apache::lonnet::domain($requdom))) {
14906: my $requhome = &Apache::lonnet::homeserver($requname,
14907: $requdom);
14908: unless ($requhome eq 'no_host') {
14909: my %userenv = &Apache::lonnet::userenvironment($requdom,$requname,'inststatus');
14910: $inststatus = $userenv{'inststatus'};
14911: $inststatus_checked = 1;
14912: }
14913: }
14914: }
14915: }
14916: unless ($inststatus_checked) {
14917: if ($reqemail =~ /^[^\@]+\@[^\@]+$/) {
14918: my %srch = (srchby => 'email',
14919: srchdomain => $defdom,
14920: srchterm => $reqemail,
14921: srchtype => 'exact');
14922: my %srch_results = &Apache::lonnet::usersearch(\%srch);
14923: foreach my $uname (keys(%srch_results)) {
14924: if (ref($srch_results{$uname}{'inststatus'}) eq 'ARRAY') {
14925: $inststatus = join(',',@{$srch_results{$uname}{'inststatus'}});
14926: $inststatus_checked = 1;
14927: last;
14928: }
14929: }
14930: unless ($inststatus_checked) {
14931: my ($dirsrchres,%srch_results) = &Apache::lonnet::inst_directory_query(\%srch);
14932: if ($dirsrchres eq 'ok') {
14933: foreach my $uname (keys(%srch_results)) {
14934: if (ref($srch_results{$uname}{'inststatus'}) eq 'ARRAY') {
14935: $inststatus = join(',',@{$srch_results{$uname}{'inststatus'}});
14936: $inststatus_checked = 1;
14937: last;
14938: }
14939: }
14940: }
14941: }
14942: }
14943: }
14944: if ($inststatus ne '') {
14945: foreach my $status (split(/\:/,$inststatus)) {
14946: if (ref($domconfig{'contacts'}{'overrides'}{$status}) eq 'HASH') {
14947: my @contacts = ('adminemail','supportemail');
14948: foreach my $item (@contacts) {
14949: if ($domconfig{'contacts'}{'overrides'}{$status}{$item}) {
14950: my $addr = $domconfig{'contacts'}{'overrides'}{$status};
14951: if (!grep(/^\Q$addr\E$/,@recipients)) {
14952: push(@recipients,$addr);
14953: }
14954: }
14955: }
14956: $otheremails = $domconfig{'contacts'}{'overrides'}{$status}{'others'};
14957: if ($domconfig{'contacts'}{'overrides'}{$status}{'bcc'}) {
14958: my @bccs = split(/,/,$domconfig{'contacts'}{'overrides'}{$status}{'bcc'});
14959: my @ok_bccs;
14960: foreach my $bcc (@bccs) {
14961: $bcc =~ s/^\s+//g;
14962: $bcc =~ s/\s+$//g;
14963: if ($bcc =~ m/^[^\@]+\@[^\@]+$/) {
14964: if (!(grep(/^\Q$bcc\E$/,@ok_bccs))) {
14965: push(@ok_bccs,$bcc);
14966: }
14967: }
14968: }
14969: if (@ok_bccs > 0) {
14970: $allbcc = join(', ',@ok_bccs);
14971: }
14972: }
14973: $addtext = $domconfig{'contacts'}{'overrides'}{$status}{'include'};
14974: last;
14975: }
14976: }
14977: }
14978: }
14979: }
1.619 raeburn 14980: } elsif ($origmail ne '') {
1.1270 raeburn 14981: $lastresort = $origmail;
14982: }
1.1297 raeburn 14983: if (($mailing eq 'helpdeskmail') && ($lastresort ne '')) {
1.1270 raeburn 14984: unless (grep(/^\Q$defdom\E$/,&Apache::lonnet::current_machine_domains())) {
14985: my $lonhost = $Apache::lonnet::perlvar{'lonHostID'};
14986: my $machinedom = $Apache::lonnet::perlvar{'lonDefDomain'};
14987: my %what = (
14988: perlvar => 1,
14989: );
14990: my $primary = &Apache::lonnet::domain($defdom,'primary');
14991: if ($primary) {
14992: my $gotaddr;
14993: my ($result,$returnhash) =
14994: &Apache::lonnet::get_remote_globals($primary,{ perlvar => 1 });
14995: if (($result eq 'ok') && (ref($returnhash) eq 'HASH')) {
14996: if ($returnhash->{'lonSupportEMail'} =~ /^[^\@]+\@[^\@]+$/) {
14997: $lastresort = $returnhash->{'lonSupportEMail'};
14998: $gotaddr = 1;
14999: }
15000: }
15001: unless ($gotaddr) {
15002: my $uintdom = &Apache::lonnet::internet_dom($primary);
15003: my $intdom = &Apache::lonnet::internet_dom($lonhost);
15004: unless ($uintdom eq $intdom) {
15005: my %domconfig =
15006: &Apache::lonnet::get_dom('configuration',['contacts'],$machinedom);
15007: if (ref($domconfig{'contacts'}) eq 'HASH') {
15008: if (ref($domconfig{'contacts'}{'otherdomsmail'}) eq 'HASH') {
15009: my @contacts = ('adminemail','supportemail');
15010: foreach my $item (@contacts) {
15011: if ($domconfig{'contacts'}{'otherdomsmail'}{$item}) {
15012: my $addr = $domconfig{'contacts'}{$item};
15013: if (!grep(/^\Q$addr\E$/,@recipients)) {
15014: push(@recipients,$addr);
15015: }
15016: }
15017: }
15018: if ($domconfig{'contacts'}{'otherdomsmail'}{'others'}) {
15019: $otheremails = $domconfig{'contacts'}{'otherdomsmail'}{'others'};
15020: }
15021: if ($domconfig{'contacts'}{'otherdomsmail'}{'bcc'}) {
15022: my @bccs = split(/,/,$domconfig{'contacts'}{'otherdomsmail'}{'bcc'});
15023: my @ok_bccs;
15024: foreach my $bcc (@bccs) {
15025: $bcc =~ s/^\s+//g;
15026: $bcc =~ s/\s+$//g;
15027: if ($bcc =~ m/^[^\@]+\@[^\@]+$/) {
15028: if (!(grep(/^\Q$bcc\E$/,@ok_bccs))) {
15029: push(@ok_bccs,$bcc);
15030: }
15031: }
15032: }
15033: if (@ok_bccs > 0) {
15034: $allbcc = join(', ',@ok_bccs);
15035: }
15036: }
15037: $addtext = $domconfig{'contacts'}{'otherdomsmail'}{'include'};
15038: }
15039: }
15040: }
15041: }
15042: }
15043: }
1.618 raeburn 15044: }
1.688 raeburn 15045: if (defined($defmail)) {
15046: if ($defmail ne '') {
15047: push(@recipients,$defmail);
15048: }
1.618 raeburn 15049: }
15050: if ($otheremails) {
1.619 raeburn 15051: my @others;
15052: if ($otheremails =~ /,/) {
15053: @others = split(/,/,$otheremails);
1.618 raeburn 15054: } else {
1.619 raeburn 15055: push(@others,$otheremails);
15056: }
15057: foreach my $addr (@others) {
15058: if (!grep(/^\Q$addr\E$/,@recipients)) {
15059: push(@recipients,$addr);
15060: }
1.618 raeburn 15061: }
15062: }
1.1298 raeburn 15063: if ($mailing eq 'helpdeskmail') {
1.1270 raeburn 15064: if ((!@recipients) && ($lastresort ne '')) {
15065: push(@recipients,$lastresort);
15066: }
15067: } elsif ($lastresort ne '') {
15068: if (!grep(/^\Q$lastresort\E$/,@recipients)) {
15069: push(@recipients,$lastresort);
15070: }
15071: }
1.1271 raeburn 15072: my $recipientlist = join(',',@recipients);
1.1270 raeburn 15073: if (wantarray) {
15074: return ($recipientlist,$allbcc,$addtext);
15075: } else {
15076: return $recipientlist;
15077: }
1.618 raeburn 15078: }
15079:
1.127 matthew 15080: ############################################################
15081: ############################################################
1.154 albertel 15082:
1.655 raeburn 15083: =pod
15084:
1.1224 musolffc 15085: =over 4
15086:
1.1223 musolffc 15087: =item * &mime_email()
15088:
15089: Sends an email with a possible attachment
15090:
15091: Inputs:
15092:
15093: =over 4
15094:
15095: from - Sender's email address
15096:
15097: to - Email address of recipient
15098:
15099: subject - Subject of email
15100:
15101: body - Body of email
15102:
15103: cc_string - Carbon copy email address
15104:
15105: bcc - Blind carbon copy email address
15106:
15107: type - File type of attachment
15108:
15109: attachment_path - Path of file to be attached
15110:
15111: file_name - Name of file to be attached
15112:
15113: attachment_text - The body of an attachment of type "TEXT"
15114:
15115: =back
15116:
15117: =back
15118:
15119: =cut
15120:
15121: ############################################################
15122: ############################################################
15123:
15124: sub mime_email {
15125: my ($from, $to, $subject, $body, $cc_string, $bcc, $attachment_path,
15126: $file_name, $attachment_text) = @_;
15127: my $msg = MIME::Lite->new(
15128: From => $from,
15129: To => $to,
15130: Subject => $subject,
15131: Type =>'TEXT',
15132: Data => $body,
15133: );
15134: if ($cc_string ne '') {
15135: $msg->add("Cc" => $cc_string);
15136: }
15137: if ($bcc ne '') {
15138: $msg->add("Bcc" => $bcc);
15139: }
15140: $msg->attr("content-type" => "text/plain");
15141: $msg->attr("content-type.charset" => "UTF-8");
15142: # Attach file if given
15143: if ($attachment_path) {
15144: unless ($file_name) {
15145: if ($attachment_path =~ m-/([^/]+)$-) { $file_name = $1; }
15146: }
15147: my ($type, $encoding) = MIME::Types::by_suffix($attachment_path);
15148: $msg->attach(Type => $type,
15149: Path => $attachment_path,
15150: Filename => $file_name
15151: );
15152: # Otherwise attach text if given
15153: } elsif ($attachment_text) {
15154: $msg->attach(Type => 'TEXT',
15155: Data => $attachment_text);
15156: }
15157: # Send it
15158: $msg->send('sendmail');
15159: }
15160:
15161: ############################################################
15162: ############################################################
15163:
15164: =pod
15165:
1.655 raeburn 15166: =head1 Course Catalog Routines
15167:
15168: =over 4
15169:
15170: =item * &gather_categories()
15171:
15172: Converts category definitions - keys of categories hash stored in
15173: coursecategories in configuration.db on the primary library server in a
15174: domain - to an array. Also generates javascript and idx hash used to
15175: generate Domain Coordinator interface for editing Course Categories.
15176:
15177: Inputs:
1.663 raeburn 15178:
1.655 raeburn 15179: categories (reference to hash of category definitions).
1.663 raeburn 15180:
1.655 raeburn 15181: cats (reference to array of arrays/hashes which encapsulates hierarchy of
15182: categories and subcategories).
1.663 raeburn 15183:
1.655 raeburn 15184: idx (reference to hash of counters used in Domain Coordinator interface for
15185: editing Course Categories).
1.663 raeburn 15186:
1.655 raeburn 15187: jsarray (reference to array of categories used to create Javascript arrays for
15188: Domain Coordinator interface for editing Course Categories).
15189:
15190: Returns: nothing
15191:
15192: Side effects: populates cats, idx and jsarray.
15193:
15194: =cut
15195:
15196: sub gather_categories {
15197: my ($categories,$cats,$idx,$jsarray) = @_;
15198: my %counters;
15199: my $num = 0;
15200: foreach my $item (keys(%{$categories})) {
15201: my ($cat,$container,$depth) = map { &unescape($_); } split(/:/,$item);
15202: if ($container eq '' && $depth == 0) {
15203: $cats->[$depth][$categories->{$item}] = $cat;
15204: } else {
15205: $cats->[$depth]{$container}[$categories->{$item}] = $cat;
15206: }
15207: my ($escitem,$tail) = split(/:/,$item,2);
15208: if ($counters{$tail} eq '') {
15209: $counters{$tail} = $num;
15210: $num ++;
15211: }
15212: if (ref($idx) eq 'HASH') {
15213: $idx->{$item} = $counters{$tail};
15214: }
15215: if (ref($jsarray) eq 'ARRAY') {
15216: push(@{$jsarray->[$counters{$tail}]},$item);
15217: }
15218: }
15219: return;
15220: }
15221:
15222: =pod
15223:
15224: =item * &extract_categories()
15225:
15226: Used to generate breadcrumb trails for course categories.
15227:
15228: Inputs:
1.663 raeburn 15229:
1.655 raeburn 15230: categories (reference to hash of category definitions).
1.663 raeburn 15231:
1.655 raeburn 15232: cats (reference to array of arrays/hashes which encapsulates hierarchy of
15233: categories and subcategories).
1.663 raeburn 15234:
1.655 raeburn 15235: trails (reference to array of breacrumb trails for each category).
1.663 raeburn 15236:
1.655 raeburn 15237: allitems (reference to hash - key is category key
15238: (format: escaped(name):escaped(parent category):depth in hierarchy).
1.663 raeburn 15239:
1.655 raeburn 15240: idx (reference to hash of counters used in Domain Coordinator interface for
15241: editing Course Categories).
1.663 raeburn 15242:
1.655 raeburn 15243: jsarray (reference to array of categories used to create Javascript arrays for
15244: Domain Coordinator interface for editing Course Categories).
15245:
1.665 raeburn 15246: subcats (reference to hash of arrays containing all subcategories within each
15247: category, -recursive)
15248:
1.1321 raeburn 15249: maxd (reference to hash used to hold max depth for all top-level categories).
15250:
1.655 raeburn 15251: Returns: nothing
15252:
15253: Side effects: populates trails and allitems hash references.
15254:
15255: =cut
15256:
15257: sub extract_categories {
1.1321 raeburn 15258: my ($categories,$cats,$trails,$allitems,$idx,$jsarray,$subcats,$maxd) = @_;
1.655 raeburn 15259: if (ref($categories) eq 'HASH') {
15260: &gather_categories($categories,$cats,$idx,$jsarray);
15261: if (ref($cats->[0]) eq 'ARRAY') {
15262: for (my $i=0; $i<@{$cats->[0]}; $i++) {
15263: my $name = $cats->[0][$i];
15264: my $item = &escape($name).'::0';
15265: my $trailstr;
15266: if ($name eq 'instcode') {
15267: $trailstr = &mt('Official courses (with institutional codes)');
1.919 raeburn 15268: } elsif ($name eq 'communities') {
15269: $trailstr = &mt('Communities');
1.1239 raeburn 15270: } elsif ($name eq 'placement') {
15271: $trailstr = &mt('Placement Tests');
1.655 raeburn 15272: } else {
15273: $trailstr = $name;
15274: }
15275: if ($allitems->{$item} eq '') {
15276: push(@{$trails},$trailstr);
15277: $allitems->{$item} = scalar(@{$trails})-1;
15278: }
15279: my @parents = ($name);
15280: if (ref($cats->[1]{$name}) eq 'ARRAY') {
15281: for (my $j=0; $j<@{$cats->[1]{$name}}; $j++) {
15282: my $category = $cats->[1]{$name}[$j];
1.665 raeburn 15283: if (ref($subcats) eq 'HASH') {
15284: push(@{$subcats->{$item}},&escape($category).':'.&escape($name).':1');
15285: }
1.1321 raeburn 15286: &recurse_categories($cats,2,$category,$trails,$allitems,\@parents,$subcats,$maxd);
1.665 raeburn 15287: }
15288: } else {
15289: if (ref($subcats) eq 'HASH') {
15290: $subcats->{$item} = [];
1.655 raeburn 15291: }
1.1321 raeburn 15292: if (ref($maxd) eq 'HASH') {
15293: $maxd->{$name} = 1;
15294: }
1.655 raeburn 15295: }
15296: }
15297: }
15298: }
15299: return;
15300: }
15301:
15302: =pod
15303:
1.1162 raeburn 15304: =item * &recurse_categories()
1.655 raeburn 15305:
15306: Recursively used to generate breadcrumb trails for course categories.
15307:
15308: Inputs:
1.663 raeburn 15309:
1.655 raeburn 15310: cats (reference to array of arrays/hashes which encapsulates hierarchy of
15311: categories and subcategories).
1.663 raeburn 15312:
1.655 raeburn 15313: depth (current depth in hierarchy of categories and sub-categories - 0 indexed).
1.663 raeburn 15314:
15315: category (current course category, for which breadcrumb trail is being generated).
15316:
15317: trails (reference to array of breadcrumb trails for each category).
15318:
1.655 raeburn 15319: allitems (reference to hash - key is category key
15320: (format: escaped(name):escaped(parent category):depth in hierarchy).
1.663 raeburn 15321:
1.655 raeburn 15322: parents (array containing containers directories for current category,
15323: back to top level).
15324:
15325: Returns: nothing
15326:
15327: Side effects: populates trails and allitems hash references
15328:
15329: =cut
15330:
15331: sub recurse_categories {
1.1321 raeburn 15332: my ($cats,$depth,$category,$trails,$allitems,$parents,$subcats,$maxd) = @_;
1.655 raeburn 15333: my $shallower = $depth - 1;
15334: if (ref($cats->[$depth]{$category}) eq 'ARRAY') {
15335: for (my $k=0; $k<@{$cats->[$depth]{$category}}; $k++) {
15336: my $name = $cats->[$depth]{$category}[$k];
15337: my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower;
1.1321 raeburn 15338: my $trailstr = join(' » ',(@{$parents},$category));
1.655 raeburn 15339: if ($allitems->{$item} eq '') {
15340: push(@{$trails},$trailstr);
15341: $allitems->{$item} = scalar(@{$trails})-1;
15342: }
15343: my $deeper = $depth+1;
15344: push(@{$parents},$category);
1.665 raeburn 15345: if (ref($subcats) eq 'HASH') {
15346: my $subcat = &escape($name).':'.$category.':'.$depth;
15347: for (my $j=@{$parents}; $j>=0; $j--) {
15348: my $higher;
15349: if ($j > 0) {
15350: $higher = &escape($parents->[$j]).':'.
15351: &escape($parents->[$j-1]).':'.$j;
15352: } else {
15353: $higher = &escape($parents->[$j]).'::'.$j;
15354: }
15355: push(@{$subcats->{$higher}},$subcat);
15356: }
15357: }
15358: &recurse_categories($cats,$deeper,$name,$trails,$allitems,$parents,
1.1321 raeburn 15359: $subcats,$maxd);
1.655 raeburn 15360: pop(@{$parents});
15361: }
15362: } else {
15363: my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower;
1.1321 raeburn 15364: my $trailstr = join(' » ',(@{$parents},$category));
1.655 raeburn 15365: if ($allitems->{$item} eq '') {
15366: push(@{$trails},$trailstr);
15367: $allitems->{$item} = scalar(@{$trails})-1;
15368: }
1.1321 raeburn 15369: if (ref($maxd) eq 'HASH') {
15370: if ($depth > $maxd->{$parents->[0]}) {
15371: $maxd->{$parents->[0]} = $depth;
15372: }
15373: }
1.655 raeburn 15374: }
15375: return;
15376: }
15377:
1.663 raeburn 15378: =pod
15379:
1.1162 raeburn 15380: =item * &assign_categories_table()
1.663 raeburn 15381:
15382: Create a datatable for display of hierarchical categories in a domain,
15383: with checkboxes to allow a course to be categorized.
15384:
15385: Inputs:
15386:
15387: cathash - reference to hash of categories defined for the domain (from
15388: configuration.db)
15389:
15390: currcat - scalar with an & separated list of categories assigned to a course.
15391:
1.919 raeburn 15392: type - scalar contains course type (Course or Community).
15393:
1.1260 raeburn 15394: disabled - scalar (optional) contains disabled="disabled" if input elements are
15395: to be readonly (e.g., Domain Helpdesk role viewing course settings).
15396:
1.663 raeburn 15397: Returns: $output (markup to be displayed)
15398:
15399: =cut
15400:
15401: sub assign_categories_table {
1.1259 raeburn 15402: my ($cathash,$currcat,$type,$disabled) = @_;
1.663 raeburn 15403: my $output;
15404: if (ref($cathash) eq 'HASH') {
1.1321 raeburn 15405: my (@cats,@trails,%allitems,%idx,@jsarray,%maxd,@path,$maxdepth);
15406: &extract_categories($cathash,\@cats,\@trails,\%allitems,\%idx,\@jsarray,\%maxd);
1.663 raeburn 15407: $maxdepth = scalar(@cats);
15408: if (@cats > 0) {
15409: my $itemcount = 0;
15410: if (ref($cats[0]) eq 'ARRAY') {
15411: my @currcategories;
15412: if ($currcat ne '') {
15413: @currcategories = split('&',$currcat);
15414: }
1.919 raeburn 15415: my $table;
1.663 raeburn 15416: for (my $i=0; $i<@{$cats[0]}; $i++) {
15417: my $parent = $cats[0][$i];
1.919 raeburn 15418: next if ($parent eq 'instcode');
15419: if ($type eq 'Community') {
15420: next unless ($parent eq 'communities');
1.1239 raeburn 15421: } elsif ($type eq 'Placement') {
15422: next unless ($parent eq 'placement');
1.919 raeburn 15423: } else {
1.1239 raeburn 15424: next if (($parent eq 'communities') || ($parent eq 'placement'));
1.919 raeburn 15425: }
1.663 raeburn 15426: my $css_class = $itemcount%2?' class="LC_odd_row"':'';
15427: my $item = &escape($parent).'::0';
15428: my $checked = '';
15429: if (@currcategories > 0) {
15430: if (grep(/^\Q$item\E$/,@currcategories)) {
1.772 bisitz 15431: $checked = ' checked="checked"';
1.663 raeburn 15432: }
15433: }
1.919 raeburn 15434: my $parent_title = $parent;
15435: if ($parent eq 'communities') {
15436: $parent_title = &mt('Communities');
1.1239 raeburn 15437: } elsif ($parent eq 'placement') {
15438: $parent_title = &mt('Placement Tests');
1.919 raeburn 15439: }
15440: $table .= '<tr '.$css_class.'><td><span class="LC_nobreak">'.
15441: '<input type="checkbox" name="usecategory" value="'.
1.1259 raeburn 15442: $item.'"'.$checked.$disabled.' />'.$parent_title.'</span>'.
1.919 raeburn 15443: '<input type="hidden" name="catname" value="'.$parent.'" /></td>';
1.663 raeburn 15444: my $depth = 1;
15445: push(@path,$parent);
1.1259 raeburn 15446: $table .= &assign_category_rows($itemcount,\@cats,$depth,$parent,\@path,\@currcategories,$disabled);
1.663 raeburn 15447: pop(@path);
1.919 raeburn 15448: $table .= '</tr><tr><td colspan="'.$maxdepth.'" class="LC_row_separator"></td></tr>';
1.663 raeburn 15449: $itemcount ++;
15450: }
1.919 raeburn 15451: if ($itemcount) {
15452: $output = &Apache::loncommon::start_data_table().
15453: $table.
15454: &Apache::loncommon::end_data_table();
15455: }
1.663 raeburn 15456: }
15457: }
15458: }
15459: return $output;
15460: }
15461:
15462: =pod
15463:
1.1162 raeburn 15464: =item * &assign_category_rows()
1.663 raeburn 15465:
15466: Create a datatable row for display of nested categories in a domain,
15467: with checkboxes to allow a course to be categorized,called recursively.
15468:
15469: Inputs:
15470:
15471: itemcount - track row number for alternating colors
15472:
15473: cats - reference to array of arrays/hashes which encapsulates hierarchy of
15474: categories and subcategories.
15475:
15476: depth - current depth in hierarchy of categories and sub-categories - 0 indexed.
15477:
15478: parent - parent of current category item
15479:
15480: path - Array containing all categories back up through the hierarchy from the
15481: current category to the top level.
15482:
15483: currcategories - reference to array of current categories assigned to the course
15484:
1.1260 raeburn 15485: disabled - scalar (optional) contains disabled="disabled" if input elements are
15486: to be readonly (e.g., Domain Helpdesk role viewing course settings).
15487:
1.663 raeburn 15488: Returns: $output (markup to be displayed).
15489:
15490: =cut
15491:
15492: sub assign_category_rows {
1.1259 raeburn 15493: my ($itemcount,$cats,$depth,$parent,$path,$currcategories,$disabled) = @_;
1.663 raeburn 15494: my ($text,$name,$item,$chgstr);
15495: if (ref($cats) eq 'ARRAY') {
15496: my $maxdepth = scalar(@{$cats});
15497: if (ref($cats->[$depth]) eq 'HASH') {
15498: if (ref($cats->[$depth]{$parent}) eq 'ARRAY') {
15499: my $numchildren = @{$cats->[$depth]{$parent}};
15500: my $css_class = $itemcount%2?' class="LC_odd_row"':'';
1.1145 raeburn 15501: $text .= '<td><table class="LC_data_table">';
1.663 raeburn 15502: for (my $j=0; $j<$numchildren; $j++) {
15503: $name = $cats->[$depth]{$parent}[$j];
15504: $item = &escape($name).':'.&escape($parent).':'.$depth;
15505: my $deeper = $depth+1;
15506: my $checked = '';
15507: if (ref($currcategories) eq 'ARRAY') {
15508: if (@{$currcategories} > 0) {
15509: if (grep(/^\Q$item\E$/,@{$currcategories})) {
1.772 bisitz 15510: $checked = ' checked="checked"';
1.663 raeburn 15511: }
15512: }
15513: }
1.664 raeburn 15514: $text .= '<tr><td><span class="LC_nobreak"><label>'.
15515: '<input type="checkbox" name="usecategory" value="'.
1.1259 raeburn 15516: $item.'"'.$checked.$disabled.' />'.$name.'</label></span>'.
1.675 raeburn 15517: '<input type="hidden" name="catname" value="'.$name.'" />'.
15518: '</td><td>';
1.663 raeburn 15519: if (ref($path) eq 'ARRAY') {
15520: push(@{$path},$name);
1.1259 raeburn 15521: $text .= &assign_category_rows($itemcount,$cats,$deeper,$name,$path,$currcategories,$disabled);
1.663 raeburn 15522: pop(@{$path});
15523: }
15524: $text .= '</td></tr>';
15525: }
15526: $text .= '</table></td>';
15527: }
15528: }
15529: }
15530: return $text;
15531: }
15532:
1.1181 raeburn 15533: =pod
15534:
15535: =back
15536:
15537: =cut
15538:
1.655 raeburn 15539: ############################################################
15540: ############################################################
15541:
15542:
1.443 albertel 15543: sub commit_customrole {
1.664 raeburn 15544: my ($udom,$uname,$url,$three,$four,$five,$start,$end,$context) = @_;
1.630 raeburn 15545: my $output = &mt('Assigning custom role').' "'.$five.'" by '.$four.':'.$three.' in '.$url.
1.443 albertel 15546: ($start?', '.&mt('starting').' '.localtime($start):'').
15547: ($end?', ending '.localtime($end):'').': <b>'.
15548: &Apache::lonnet::assigncustomrole(
1.664 raeburn 15549: $udom,$uname,$url,$three,$four,$five,$end,$start,undef,undef,$context).
1.443 albertel 15550: '</b><br />';
15551: return $output;
15552: }
15553:
15554: sub commit_standardrole {
1.1116 raeburn 15555: my ($udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context,$credits) = @_;
1.541 raeburn 15556: my ($output,$logmsg,$linefeed);
15557: if ($context eq 'auto') {
15558: $linefeed = "\n";
15559: } else {
15560: $linefeed = "<br />\n";
15561: }
1.443 albertel 15562: if ($three eq 'st') {
1.541 raeburn 15563: my $result = &commit_studentrole(\$logmsg,$udom,$uname,$url,$three,$start,$end,
1.1116 raeburn 15564: $one,$two,$sec,$context,$credits);
1.541 raeburn 15565: if (($result =~ /^error/) || ($result eq 'not_in_class') ||
1.626 raeburn 15566: ($result eq 'unknown_course') || ($result eq 'refused')) {
15567: $output = $logmsg.' '.&mt('Error: ').$result."\n";
1.443 albertel 15568: } else {
1.541 raeburn 15569: $output = $logmsg.$linefeed.&mt('Assigning').' '.$three.' in '.$url.
1.443 albertel 15570: ($start?', '.&mt('starting').' '.localtime($start):'').
1.541 raeburn 15571: ($end?', '.&mt('ending').' '.localtime($end):'').': ';
15572: if ($context eq 'auto') {
15573: $output .= $result.$linefeed.&mt('Add to classlist').': ok';
15574: } else {
15575: $output .= '<b>'.$result.'</b>'.$linefeed.
15576: &mt('Add to classlist').': <b>ok</b>';
15577: }
15578: $output .= $linefeed;
1.443 albertel 15579: }
15580: } else {
15581: $output = &mt('Assigning').' '.$three.' in '.$url.
15582: ($start?', '.&mt('starting').' '.localtime($start):'').
1.541 raeburn 15583: ($end?', '.&mt('ending').' '.localtime($end):'').': ';
1.652 raeburn 15584: my $result = &Apache::lonnet::assignrole($udom,$uname,$url,$three,$end,$start,'','',$context);
1.541 raeburn 15585: if ($context eq 'auto') {
15586: $output .= $result.$linefeed;
15587: } else {
15588: $output .= '<b>'.$result.'</b>'.$linefeed;
15589: }
1.443 albertel 15590: }
15591: return $output;
15592: }
15593:
15594: sub commit_studentrole {
1.1116 raeburn 15595: my ($logmsg,$udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context,
15596: $credits) = @_;
1.626 raeburn 15597: my ($result,$linefeed,$oldsecurl,$newsecurl);
1.541 raeburn 15598: if ($context eq 'auto') {
15599: $linefeed = "\n";
15600: } else {
15601: $linefeed = '<br />'."\n";
15602: }
1.443 albertel 15603: if (defined($one) && defined($two)) {
15604: my $cid=$one.'_'.$two;
15605: my $oldsec=&Apache::lonnet::getsection($udom,$uname,$cid);
15606: my $secchange = 0;
15607: my $expire_role_result;
15608: my $modify_section_result;
1.628 raeburn 15609: if ($oldsec ne '-1') {
15610: if ($oldsec ne $sec) {
1.443 albertel 15611: $secchange = 1;
1.628 raeburn 15612: my $now = time;
1.443 albertel 15613: my $uurl='/'.$cid;
15614: $uurl=~s/\_/\//g;
15615: if ($oldsec) {
15616: $uurl.='/'.$oldsec;
15617: }
1.626 raeburn 15618: $oldsecurl = $uurl;
1.628 raeburn 15619: $expire_role_result =
1.652 raeburn 15620: &Apache::lonnet::assignrole($udom,$uname,$uurl,'st',$now,'','',$context);
1.628 raeburn 15621: if ($env{'request.course.sec'} ne '') {
15622: if ($expire_role_result eq 'refused') {
15623: my @roles = ('st');
15624: my @statuses = ('previous');
15625: my @roledoms = ($one);
15626: my $withsec = 1;
15627: my %roleshash =
15628: &Apache::lonnet::get_my_roles($uname,$udom,'userroles',
15629: \@statuses,\@roles,\@roledoms,$withsec);
15630: if (defined ($roleshash{$two.':'.$one.':st:'.$oldsec})) {
15631: my ($oldstart,$oldend) =
15632: split(':',$roleshash{$two.':'.$one.':st:'.$oldsec});
15633: if ($oldend > 0 && $oldend <= $now) {
15634: $expire_role_result = 'ok';
15635: }
15636: }
15637: }
15638: }
1.443 albertel 15639: $result = $expire_role_result;
15640: }
15641: }
15642: if (($expire_role_result eq 'ok') || ($secchange == 0)) {
1.1116 raeburn 15643: $modify_section_result =
15644: &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,
15645: undef,undef,undef,$sec,
15646: $end,$start,'','',$cid,
15647: '',$context,$credits);
1.443 albertel 15648: if ($modify_section_result =~ /^ok/) {
15649: if ($secchange == 1) {
1.628 raeburn 15650: if ($sec eq '') {
15651: $$logmsg .= &mt('Section for [_1] switched from (possibly expired) old section: [_2] to student role without a section.',$uname,$oldsec).$linefeed;
15652: } else {
15653: $$logmsg .= &mt('Section for [_1] switched from (possibly expired) old section: [_2] to new section: [_3].',$uname,$oldsec,$sec).$linefeed;
15654: }
1.443 albertel 15655: } elsif ($oldsec eq '-1') {
1.628 raeburn 15656: if ($sec eq '') {
15657: $$logmsg .= &mt('New student role without a section for [_1] in course [_2].',$uname,$cid).$linefeed;
15658: } else {
15659: $$logmsg .= &mt('New student role for [_1] in section [_2] in course [_3].',$uname,$sec,$cid).$linefeed;
15660: }
1.443 albertel 15661: } else {
1.628 raeburn 15662: if ($sec eq '') {
15663: $$logmsg .= &mt('Student [_1] assigned to course [_2] without a section.',$uname,$cid).$linefeed;
15664: } else {
15665: $$logmsg .= &mt('Student [_1] assigned to section [_2] in course [_3].',$uname,$sec,$cid).$linefeed;
15666: }
1.443 albertel 15667: }
15668: } else {
1.1115 raeburn 15669: if ($secchange) {
1.628 raeburn 15670: $$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;
15671: } else {
15672: $$logmsg .= &mt('Error when attempting to modify role for [_1] for section: "[_2]" in course [_3] -error:',$uname,$sec,$cid).' '.$modify_section_result.$linefeed;
15673: }
1.443 albertel 15674: }
15675: $result = $modify_section_result;
15676: } elsif ($secchange == 1) {
1.628 raeburn 15677: if ($oldsec eq '') {
1.1103 raeburn 15678: $$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 15679: } else {
15680: $$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;
15681: }
1.626 raeburn 15682: if ($expire_role_result eq 'refused') {
15683: my $newsecurl = '/'.$cid;
15684: $newsecurl =~ s/\_/\//g;
15685: if ($sec ne '') {
15686: $newsecurl.='/'.$sec;
15687: }
15688: if (&Apache::lonnet::allowed('cst',$newsecurl) && !(&Apache::lonnet::allowed('cst',$oldsecurl))) {
15689: if ($sec eq '') {
15690: $$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;
15691: } else {
15692: $$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;
15693: }
15694: }
15695: }
1.443 albertel 15696: }
15697: } else {
1.626 raeburn 15698: $$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 15699: $result = "error: incomplete course id\n";
15700: }
15701: return $result;
15702: }
15703:
1.1108 raeburn 15704: sub show_role_extent {
15705: my ($scope,$context,$role) = @_;
15706: $scope =~ s{^/}{};
15707: my @courseroles = &Apache::lonuserutils::roles_by_context('course',1);
15708: push(@courseroles,'co');
15709: my @authorroles = &Apache::lonuserutils::roles_by_context('author');
15710: if (($context eq 'course') || (grep(/^\Q$role\E/,@courseroles))) {
15711: $scope =~ s{/}{_};
15712: return '<span class="LC_cusr_emph">'.$env{'course.'.$scope.'.description'}.'</span>';
15713: } elsif (($context eq 'author') || (grep(/^\Q$role\E/,@authorroles))) {
15714: my ($audom,$auname) = split(/\//,$scope);
15715: return &mt('[_1] Author Space','<span class="LC_cusr_emph">'.
15716: &Apache::loncommon::plainname($auname,$audom).'</span>');
15717: } else {
15718: $scope =~ s{/$}{};
15719: return &mt('Domain: [_1]','<span class="LC_cusr_emph">'.
15720: &Apache::lonnet::domain($scope,'description').'</span>');
15721: }
15722: }
15723:
1.443 albertel 15724: ############################################################
15725: ############################################################
15726:
1.566 albertel 15727: sub check_clone {
1.578 raeburn 15728: my ($args,$linefeed) = @_;
1.566 albertel 15729: my $cloneid='/'.$args->{'clonedomain'}.'/'.$args->{'clonecourse'};
15730: my ($clonecrsudom,$clonecrsunum)= &LONCAPA::split_courseid($cloneid);
15731: my $clonehome=&Apache::lonnet::homeserver($clonecrsunum,$clonecrsudom);
15732: my $clonemsg;
15733: my $can_clone = 0;
1.944 raeburn 15734: my $lctype = lc($args->{'crstype'});
1.908 raeburn 15735: if ($lctype ne 'community') {
15736: $lctype = 'course';
15737: }
1.566 albertel 15738: if ($clonehome eq 'no_host') {
1.944 raeburn 15739: if ($args->{'crstype'} eq 'Community') {
1.908 raeburn 15740: $clonemsg = &mt('No new community created.').$linefeed.&mt('A new community could not be cloned from the specified original - [_1] - because it is a non-existent community.',$args->{'clonecourse'}.':'.$args->{'clonedomain'});
15741: } else {
15742: $clonemsg = &mt('No new course created.').$linefeed.&mt('A new course could not be cloned from the specified original - [_1] - because it is a non-existent course.',$args->{'clonecourse'}.':'.$args->{'clonedomain'});
15743: }
1.566 albertel 15744: } else {
15745: my %clonedesc = &Apache::lonnet::coursedescription($cloneid,{'one_time' => 1});
1.944 raeburn 15746: if ($args->{'crstype'} eq 'Community') {
1.908 raeburn 15747: if ($clonedesc{'type'} ne 'Community') {
1.1262 raeburn 15748: $clonemsg = &mt('No new community created.').$linefeed.&mt('A new community could not be cloned from the specified original - [_1] - because it is a course not a community.',$args->{'clonecourse'}.':'.$args->{'clonedomain'});
1.908 raeburn 15749: return ($can_clone, $clonemsg, $cloneid, $clonehome);
15750: }
15751: }
1.1262 raeburn 15752: if (($env{'request.role.domain'} eq $args->{'clonedomain'}) &&
1.882 raeburn 15753: (&Apache::lonnet::allowed('ccc',$env{'request.role.domain'}))) {
1.566 albertel 15754: $can_clone = 1;
15755: } else {
1.1221 raeburn 15756: my %clonehash = &Apache::lonnet::get('environment',['cloners','internal.coursecode'],
1.566 albertel 15757: $args->{'clonedomain'},$args->{'clonecourse'});
1.1221 raeburn 15758: if ($clonehash{'cloners'} eq '') {
15759: my %domdefs = &Apache::lonnet::get_domain_defaults($args->{'course_domain'});
15760: if ($domdefs{'canclone'}) {
15761: unless ($domdefs{'canclone'} eq 'none') {
15762: if ($domdefs{'canclone'} eq 'domain') {
15763: if ($args->{'ccdomain'} eq $args->{'clonedomain'}) {
15764: $can_clone = 1;
15765: }
15766: } elsif (($clonehash{'internal.coursecode'}) && ($args->{'crscode'}) &&
15767: ($args->{'clonedomain'} eq $args->{'course_domain'})) {
15768: if (&Apache::lonnet::default_instcode_cloning($args->{'clonedomain'},$domdefs{'canclone'},
15769: $clonehash{'internal.coursecode'},$args->{'crscode'})) {
15770: $can_clone = 1;
15771: }
15772: }
15773: }
15774: }
1.578 raeburn 15775: } else {
1.1221 raeburn 15776: my @cloners = split(/,/,$clonehash{'cloners'});
15777: if (grep(/^\*$/,@cloners)) {
1.942 raeburn 15778: $can_clone = 1;
1.1221 raeburn 15779: } elsif (grep(/^\*\:\Q$args->{'ccdomain'}\E$/,@cloners)) {
1.942 raeburn 15780: $can_clone = 1;
1.1225 raeburn 15781: } elsif (grep(/^\Q$args->{'ccuname'}\E:\Q$args->{'ccdomain'}\E$/,@cloners)) {
15782: $can_clone = 1;
1.1221 raeburn 15783: }
15784: unless ($can_clone) {
1.1225 raeburn 15785: if (($clonehash{'internal.coursecode'}) && ($args->{'crscode'}) &&
15786: ($args->{'clonedomain'} eq $args->{'course_domain'})) {
1.1221 raeburn 15787: my (%gotdomdefaults,%gotcodedefaults);
15788: foreach my $cloner (@cloners) {
15789: if (($cloner ne '*') && ($cloner !~ /^\*\:$match_domain$/) &&
15790: ($cloner !~ /^$match_username\:$match_domain$/) && ($cloner ne '')) {
15791: my (%codedefaults,@code_order);
15792: if (ref($gotcodedefaults{$args->{'clonedomain'}}) eq 'HASH') {
15793: if (ref($gotcodedefaults{$args->{'clonedomain'}}{'defaults'}) eq 'HASH') {
15794: %codedefaults = %{$gotcodedefaults{$args->{'clonedomain'}}{'defaults'}};
15795: }
15796: if (ref($gotcodedefaults{$args->{'clonedomain'}}{'order'}) eq 'ARRAY') {
15797: @code_order = @{$gotcodedefaults{$args->{'clonedomain'}}{'order'}};
15798: }
15799: } else {
15800: &Apache::lonnet::auto_instcode_defaults($args->{'clonedomain'},
15801: \%codedefaults,
15802: \@code_order);
15803: $gotcodedefaults{$args->{'clonedomain'}}{'defaults'} = \%codedefaults;
15804: $gotcodedefaults{$args->{'clonedomain'}}{'order'} = \@code_order;
15805: }
15806: if (@code_order > 0) {
15807: if (&Apache::lonnet::check_instcode_cloning(\%codedefaults,\@code_order,
15808: $cloner,$clonehash{'internal.coursecode'},
15809: $args->{'crscode'})) {
15810: $can_clone = 1;
15811: last;
15812: }
15813: }
15814: }
15815: }
15816: }
1.1225 raeburn 15817: }
15818: }
15819: unless ($can_clone) {
15820: my $ccrole = 'cc';
15821: if ($args->{'crstype'} eq 'Community') {
15822: $ccrole = 'co';
15823: }
15824: my %roleshash =
15825: &Apache::lonnet::get_my_roles($args->{'ccuname'},
15826: $args->{'ccdomain'},
15827: 'userroles',['active'],[$ccrole],
15828: [$args->{'clonedomain'}]);
15829: if ($roleshash{$args->{'clonecourse'}.':'.$args->{'clonedomain'}.':'.$ccrole}) {
15830: $can_clone = 1;
15831: } elsif (&Apache::lonnet::is_course_owner($args->{'clonedomain'},$args->{'clonecourse'},
15832: $args->{'ccuname'},$args->{'ccdomain'})) {
15833: $can_clone = 1;
1.1221 raeburn 15834: }
15835: }
15836: unless ($can_clone) {
15837: if ($args->{'crstype'} eq 'Community') {
15838: $clonemsg = &mt('No new community created.').$linefeed.&mt('The new community could not be cloned from the existing community because the new community owner ([_1]) does not have cloning rights in the existing community ([_2]).',$args->{'ccuname'}.':'.$args->{'ccdomain'},$clonedesc{'description'});
1.942 raeburn 15839: } else {
1.1221 raeburn 15840: $clonemsg = &mt('No new course created.').$linefeed.&mt('The new course could not be cloned from the existing course because the new course owner ([_1]) does not have cloning rights in the existing course ([_2]).',$args->{'ccuname'}.':'.$args->{'ccdomain'},$clonedesc{'description'});
15841: }
1.566 albertel 15842: }
1.578 raeburn 15843: }
1.566 albertel 15844: }
15845: return ($can_clone, $clonemsg, $cloneid, $clonehome);
15846: }
15847:
1.444 albertel 15848: sub construct_course {
1.1262 raeburn 15849: my ($args,$logmsg,$courseid,$crsudom,$crsunum,$udom,$uname,$context,
15850: $cnum,$category,$coderef) = @_;
1.444 albertel 15851: my $outcome;
1.541 raeburn 15852: my $linefeed = '<br />'."\n";
15853: if ($context eq 'auto') {
15854: $linefeed = "\n";
15855: }
1.566 albertel 15856:
15857: #
15858: # Are we cloning?
15859: #
15860: my ($can_clone, $clonemsg, $cloneid, $clonehome);
15861: if (($args->{'clonecourse'}) && ($args->{'clonedomain'})) {
1.578 raeburn 15862: ($can_clone, $clonemsg, $cloneid, $clonehome) = &check_clone($args,$linefeed);
1.566 albertel 15863: if ($context ne 'auto') {
1.578 raeburn 15864: if ($clonemsg ne '') {
15865: $clonemsg = '<span class="LC_error">'.$clonemsg.'</span>';
15866: }
1.566 albertel 15867: }
15868: $outcome .= $clonemsg.$linefeed;
15869:
15870: if (!$can_clone) {
15871: return (0,$outcome);
15872: }
15873: }
15874:
1.444 albertel 15875: #
15876: # Open course
15877: #
1.1239 raeburn 15878: my $showncrstype;
15879: if ($args->{'crstype'} eq 'Placement') {
15880: $showncrstype = 'placement test';
15881: } else {
15882: $showncrstype = lc($args->{'crstype'});
15883: }
1.444 albertel 15884: my %cenv=();
15885: $$courseid=&Apache::lonnet::createcourse($args->{'course_domain'},
15886: $args->{'cdescr'},
15887: $args->{'curl'},
15888: $args->{'course_home'},
15889: $args->{'nonstandard'},
15890: $args->{'crscode'},
15891: $args->{'ccuname'}.':'.
15892: $args->{'ccdomain'},
1.882 raeburn 15893: $args->{'crstype'},
1.885 raeburn 15894: $cnum,$context,$category);
1.444 albertel 15895:
15896: # Note: The testing routines depend on this being output; see
15897: # Utils::Course. This needs to at least be output as a comment
15898: # if anyone ever decides to not show this, and Utils::Course::new
15899: # will need to be suitably modified.
1.1239 raeburn 15900: $outcome .= &mt('New LON-CAPA [_1] ID: [_2]',$showncrstype,$$courseid).$linefeed;
1.943 raeburn 15901: if ($$courseid =~ /^error:/) {
15902: return (0,$outcome);
15903: }
15904:
1.444 albertel 15905: #
15906: # Check if created correctly
15907: #
1.479 albertel 15908: ($$crsudom,$$crsunum)= &LONCAPA::split_courseid($$courseid);
1.444 albertel 15909: my $crsuhome=&Apache::lonnet::homeserver($$crsunum,$$crsudom);
1.943 raeburn 15910: if ($crsuhome eq 'no_host') {
15911: $outcome .= &mt('Course creation failed, unrecognized course home server.').$linefeed;
15912: return (0,$outcome);
15913: }
1.541 raeburn 15914: $outcome .= &mt('Created on').': '.$crsuhome.$linefeed;
1.566 albertel 15915:
1.444 albertel 15916: #
1.566 albertel 15917: # Do the cloning
15918: #
15919: if ($can_clone && $cloneid) {
1.1239 raeburn 15920: $clonemsg = &mt('Cloning [_1] from [_2]',$showncrstype,$clonehome);
1.566 albertel 15921: if ($context ne 'auto') {
15922: $clonemsg = '<span class="LC_success">'.$clonemsg.'</span>';
15923: }
15924: $outcome .= $clonemsg.$linefeed;
15925: my %oldcenv=&Apache::lonnet::dump('environment',$$crsudom,$$crsunum);
1.444 albertel 15926: # Copy all files
1.637 www 15927: &Apache::lonclonecourse::copycoursefiles($cloneid,$$courseid,$args->{'datemode'},$args->{'dateshift'});
1.444 albertel 15928: # Restore URL
1.566 albertel 15929: $cenv{'url'}=$oldcenv{'url'};
1.444 albertel 15930: # Restore title
1.566 albertel 15931: $cenv{'description'}=$oldcenv{'description'};
1.955 raeburn 15932: # Restore creation date, creator and creation context.
15933: $cenv{'internal.created'}=$oldcenv{'internal.created'};
15934: $cenv{'internal.creator'}=$oldcenv{'internal.creator'};
15935: $cenv{'internal.creationcontext'}=$oldcenv{'internal.creationcontext'};
1.444 albertel 15936: # Mark as cloned
1.566 albertel 15937: $cenv{'clonedfrom'}=$cloneid;
1.638 www 15938: # Need to clone grading mode
15939: my %newenv=&Apache::lonnet::get('environment',['grading'],$$crsudom,$$crsunum);
15940: $cenv{'grading'}=$newenv{'grading'};
15941: # Do not clone these environment entries
15942: &Apache::lonnet::del('environment',
15943: ['default_enrollment_start_date',
15944: 'default_enrollment_end_date',
15945: 'question.email',
15946: 'policy.email',
15947: 'comment.email',
15948: 'pch.users.denied',
1.725 raeburn 15949: 'plc.users.denied',
15950: 'hidefromcat',
1.1121 raeburn 15951: 'checkforpriv',
1.1166 raeburn 15952: 'categories',
15953: 'internal.uniquecode'],
1.638 www 15954: $$crsudom,$$crsunum);
1.1170 raeburn 15955: if ($args->{'textbook'}) {
15956: $cenv{'internal.textbook'} = $args->{'textbook'};
15957: }
1.444 albertel 15958: }
1.566 albertel 15959:
1.444 albertel 15960: #
15961: # Set environment (will override cloned, if existing)
15962: #
15963: my @sections = ();
15964: my @xlists = ();
15965: if ($args->{'crstype'}) {
15966: $cenv{'type'}=$args->{'crstype'};
15967: }
15968: if ($args->{'crsid'}) {
15969: $cenv{'courseid'}=$args->{'crsid'};
15970: }
15971: if ($args->{'crscode'}) {
15972: $cenv{'internal.coursecode'}=$args->{'crscode'};
15973: }
15974: if ($args->{'crsquota'} ne '') {
15975: $cenv{'internal.coursequota'}=$args->{'crsquota'};
15976: } else {
15977: $cenv{'internal.coursequota'}=$args->{'crsquota'} = 20;
15978: }
15979: if ($args->{'ccuname'}) {
15980: $cenv{'internal.courseowner'} = $args->{'ccuname'}.
15981: ':'.$args->{'ccdomain'};
15982: } else {
15983: $cenv{'internal.courseowner'} = $args->{'curruser'};
15984: }
1.1116 raeburn 15985: if ($args->{'defaultcredits'}) {
15986: $cenv{'internal.defaultcredits'} = $args->{'defaultcredits'};
15987: }
1.444 albertel 15988: my @badclasses = (); # Used to accumulate sections/crosslistings that did not pass classlist access check for course owner.
15989: if ($args->{'crssections'}) {
15990: $cenv{'internal.sectionnums'} = '';
15991: if ($args->{'crssections'} =~ m/,/) {
15992: @sections = split/,/,$args->{'crssections'};
15993: } else {
15994: $sections[0] = $args->{'crssections'};
15995: }
15996: if (@sections > 0) {
15997: foreach my $item (@sections) {
15998: my ($sec,$gp) = split/:/,$item;
15999: my $class = $args->{'crscode'}.$sec;
16000: my $addcheck = &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$class,$cenv{'internal.courseowner'});
16001: $cenv{'internal.sectionnums'} .= $item.',';
16002: unless ($addcheck eq 'ok') {
1.1263 raeburn 16003: push(@badclasses,$class);
1.444 albertel 16004: }
16005: }
16006: $cenv{'internal.sectionnums'} =~ s/,$//;
16007: }
16008: }
16009: # do not hide course coordinator from staff listing,
16010: # even if privileged
16011: $cenv{'nothideprivileged'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
1.1121 raeburn 16012: # add course coordinator's domain to domains to check for privileged users
16013: # if different to course domain
16014: if ($$crsudom ne $args->{'ccdomain'}) {
16015: $cenv{'checkforpriv'} = $args->{'ccdomain'};
16016: }
1.444 albertel 16017: # add crosslistings
16018: if ($args->{'crsxlist'}) {
16019: $cenv{'internal.crosslistings'}='';
16020: if ($args->{'crsxlist'} =~ m/,/) {
16021: @xlists = split/,/,$args->{'crsxlist'};
16022: } else {
16023: $xlists[0] = $args->{'crsxlist'};
16024: }
16025: if (@xlists > 0) {
16026: foreach my $item (@xlists) {
16027: my ($xl,$gp) = split/:/,$item;
16028: my $addcheck = &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$xl,$cenv{'internal.courseowner'});
16029: $cenv{'internal.crosslistings'} .= $item.',';
16030: unless ($addcheck eq 'ok') {
1.1263 raeburn 16031: push(@badclasses,$xl);
1.444 albertel 16032: }
16033: }
16034: $cenv{'internal.crosslistings'} =~ s/,$//;
16035: }
16036: }
16037: if ($args->{'autoadds'}) {
16038: $cenv{'internal.autoadds'}=$args->{'autoadds'};
16039: }
16040: if ($args->{'autodrops'}) {
16041: $cenv{'internal.autodrops'}=$args->{'autodrops'};
16042: }
16043: # check for notification of enrollment changes
16044: my @notified = ();
16045: if ($args->{'notify_owner'}) {
16046: if ($args->{'ccuname'} ne '') {
16047: push(@notified,$args->{'ccuname'}.':'.$args->{'ccdomain'});
16048: }
16049: }
16050: if ($args->{'notify_dc'}) {
16051: if ($uname ne '') {
1.630 raeburn 16052: push(@notified,$uname.':'.$udom);
1.444 albertel 16053: }
16054: }
16055: if (@notified > 0) {
16056: my $notifylist;
16057: if (@notified > 1) {
16058: $notifylist = join(',',@notified);
16059: } else {
16060: $notifylist = $notified[0];
16061: }
16062: $cenv{'internal.notifylist'} = $notifylist;
16063: }
16064: if (@badclasses > 0) {
16065: my %lt=&Apache::lonlocal::texthash(
1.1264 raeburn 16066: 'tclb' => 'The courses listed below were included as sections or crosslistings affiliated with your new LON-CAPA course.',
16067: 'howi' => 'However, if automated course roster updates are enabled for this class, these particular sections/crosslistings are not guaranteed to contribute towards enrollment.',
16068: 'itis' => 'It is possible that rights to access enrollment for these classes will be available through assignment of co-owners.',
1.444 albertel 16069: );
1.1264 raeburn 16070: my $badclass_msg = $lt{'tclb'}.$linefeed.$lt{'howi'}.$linefeed.
16071: &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 16072: if ($context eq 'auto') {
16073: $outcome .= $badclass_msg.$linefeed;
1.1261 raeburn 16074: } else {
1.566 albertel 16075: $outcome .= '<div class="LC_warning">'.$badclass_msg.$linefeed.'<ul>'."\n";
1.1261 raeburn 16076: }
16077: foreach my $item (@badclasses) {
1.541 raeburn 16078: if ($context eq 'auto') {
1.1261 raeburn 16079: $outcome .= " - $item\n";
1.541 raeburn 16080: } else {
1.1261 raeburn 16081: $outcome .= "<li>$item</li>\n";
1.541 raeburn 16082: }
1.1261 raeburn 16083: }
16084: if ($context eq 'auto') {
16085: $outcome .= $linefeed;
16086: } else {
16087: $outcome .= "</ul><br /><br /></div>\n";
1.541 raeburn 16088: }
1.444 albertel 16089: }
16090: if ($args->{'no_end_date'}) {
16091: $args->{'endaccess'} = 0;
16092: }
16093: $cenv{'internal.autostart'}=$args->{'enrollstart'};
16094: $cenv{'internal.autoend'}=$args->{'enrollend'};
16095: $cenv{'default_enrollment_start_date'}=$args->{'startaccess'};
16096: $cenv{'default_enrollment_end_date'}=$args->{'endaccess'};
16097: if ($args->{'showphotos'}) {
16098: $cenv{'internal.showphotos'}=$args->{'showphotos'};
16099: }
16100: $cenv{'internal.authtype'} = $args->{'authtype'};
16101: $cenv{'internal.autharg'} = $args->{'autharg'};
16102: if ( ($cenv{'internal.authtype'} =~ /^krb/) && ($cenv{'internal.autoadds'} == 1)) {
16103: if (! defined($cenv{'internal.autharg'}) || $cenv{'internal.autharg'} eq '') {
1.541 raeburn 16104: 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');
16105: if ($context eq 'auto') {
16106: $outcome .= $krb_msg;
16107: } else {
1.566 albertel 16108: $outcome .= '<span class="LC_error">'.$krb_msg.'</span>';
1.541 raeburn 16109: }
16110: $outcome .= $linefeed;
1.444 albertel 16111: }
16112: }
16113: if (($args->{'ccdomain'}) && ($args->{'ccuname'})) {
16114: if ($args->{'setpolicy'}) {
16115: $cenv{'policy.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
16116: }
16117: if ($args->{'setcontent'}) {
16118: $cenv{'question.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
16119: }
1.1251 raeburn 16120: if ($args->{'setcomment'}) {
16121: $cenv{'comment.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
16122: }
1.444 albertel 16123: }
16124: if ($args->{'reshome'}) {
16125: $cenv{'reshome'}=$args->{'reshome'}.'/';
16126: $cenv{'reshome'}=~s/\/+$/\//;
16127: }
16128: #
16129: # course has keyed access
16130: #
16131: if ($args->{'setkeys'}) {
16132: $cenv{'keyaccess'}='yes';
16133: }
16134: # if specified, key authority is not course, but user
16135: # only active if keyaccess is yes
16136: if ($args->{'keyauth'}) {
1.487 albertel 16137: my ($user,$domain) = split(':',$args->{'keyauth'});
16138: $user = &LONCAPA::clean_username($user);
16139: $domain = &LONCAPA::clean_username($domain);
1.488 foxr 16140: if ($user ne '' && $domain ne '') {
1.487 albertel 16141: $cenv{'keyauth'}=$user.':'.$domain;
1.444 albertel 16142: }
16143: }
16144:
1.1166 raeburn 16145: #
1.1167 raeburn 16146: # generate and store uniquecode (available to course requester), if course should have one.
1.1166 raeburn 16147: #
16148: if ($args->{'uniquecode'}) {
16149: my ($code,$error) = &make_unique_code($$crsudom,$$crsunum);
16150: if ($code) {
16151: $cenv{'internal.uniquecode'} = $code;
1.1167 raeburn 16152: my %crsinfo =
16153: &Apache::lonnet::courseiddump($$crsudom,'.',1,'.','.',$$crsunum,undef,undef,'.');
16154: if (ref($crsinfo{$$crsudom.'_'.$$crsunum}) eq 'HASH') {
16155: $crsinfo{$$crsudom.'_'.$$crsunum}{'uniquecode'} = $code;
16156: my $putres = &Apache::lonnet::courseidput($$crsudom,\%crsinfo,$crsuhome,'notime');
16157: }
1.1166 raeburn 16158: if (ref($coderef)) {
16159: $$coderef = $code;
16160: }
16161: }
16162: }
16163:
1.444 albertel 16164: if ($args->{'disresdis'}) {
16165: $cenv{'pch.roles.denied'}='st';
16166: }
16167: if ($args->{'disablechat'}) {
16168: $cenv{'plc.roles.denied'}='st';
16169: }
16170:
16171: # Record we've not yet viewed the Course Initialization Helper for this
16172: # course
16173: $cenv{'course.helper.not.run'} = 1;
16174: #
16175: # Use new Randomseed
16176: #
16177: $cenv{'rndseed'}=&Apache::lonnet::latest_rnd_algorithm_id();;
16178: $cenv{'receiptalg'}=&Apache::lonnet::latest_receipt_algorithm_id();;
16179: #
16180: # The encryption code and receipt prefix for this course
16181: #
16182: $cenv{'internal.encseed'}=$Apache::lonnet::perlvar{'lonReceipt'}.$$.time.int(rand(9999));
16183: $cenv{'internal.encpref'}=100+int(9*rand(99));
16184: #
16185: # By default, use standard grading
16186: if (!defined($cenv{'grading'})) { $cenv{'grading'} = 'standard'; }
16187:
1.541 raeburn 16188: $outcome .= $linefeed.&mt('Setting environment').': '.
16189: &Apache::lonnet::put('environment',\%cenv,$$crsudom,$$crsunum).$linefeed;
1.444 albertel 16190: #
16191: # Open all assignments
16192: #
16193: if ($args->{'openall'}) {
16194: my $storeunder=$$crsudom.'_'.$$crsunum.'.0.opendate';
16195: my %storecontent = ($storeunder => time,
16196: $storeunder.'.type' => 'date_start');
16197:
16198: $outcome .= &mt('Opening all assignments').': '.&Apache::lonnet::cput
1.541 raeburn 16199: ('resourcedata',\%storecontent,$$crsudom,$$crsunum).$linefeed;
1.444 albertel 16200: }
16201: #
16202: # Set first page
16203: #
16204: unless (($args->{'nonstandard'}) || ($args->{'firstres'} eq 'blank')
16205: || ($cloneid)) {
1.445 albertel 16206: use LONCAPA::map;
1.444 albertel 16207: $outcome .= &mt('Setting first resource').': ';
1.445 albertel 16208:
16209: my $map = '/uploaded/'.$$crsudom.'/'.$$crsunum.'/default.sequence';
16210: my ($errtext,$fatal)=&LONCAPA::map::mapread($map);
16211:
1.444 albertel 16212: $outcome .= ($fatal?$errtext:'read ok').' - ';
16213: my $title; my $url;
16214: if ($args->{'firstres'} eq 'syl') {
1.690 bisitz 16215: $title=&mt('Syllabus');
1.444 albertel 16216: $url='/public/'.$$crsudom.'/'.$$crsunum.'/syllabus';
16217: } else {
1.963 raeburn 16218: $title=&mt('Table of Contents');
1.444 albertel 16219: $url='/adm/navmaps';
16220: }
1.445 albertel 16221:
16222: $LONCAPA::map::resources[1]=$title.':'.$url.':false:start:res';
16223: (my $outtext,$errtext) = &LONCAPA::map::storemap($map,1);
16224:
16225: if ($errtext) { $fatal=2; }
1.541 raeburn 16226: $outcome .= ($fatal?$errtext:'write ok').$linefeed;
1.444 albertel 16227: }
1.566 albertel 16228:
1.1237 raeburn 16229: #
16230: # Set params for Placement Tests
16231: #
1.1239 raeburn 16232: if ($args->{'crstype'} eq 'Placement') {
16233: my %storecontent;
16234: my $prefix=$$crsudom.'_'.$$crsunum.'.0.';
16235: my %defaults = (
16236: buttonshide => { value => 'yes',
16237: type => 'string_yesno',},
16238: type => { value => 'randomizetry',
16239: type => 'string_questiontype',},
16240: maxtries => { value => 1,
16241: type => 'int_pos',},
16242: problemstatus => { value => 'no',
16243: type => 'string_problemstatus',},
16244: );
16245: foreach my $key (keys(%defaults)) {
16246: $storecontent{$prefix.$key} = $defaults{$key}{'value'};
16247: $storecontent{$prefix.$key.'.type'} = $defaults{$key}{'type'};
16248: }
1.1237 raeburn 16249: &Apache::lonnet::cput
16250: ('resourcedata',\%storecontent,$$crsudom,$$crsunum);
16251: }
16252:
1.566 albertel 16253: return (1,$outcome);
1.444 albertel 16254: }
16255:
1.1166 raeburn 16256: sub make_unique_code {
16257: my ($cdom,$cnum) = @_;
16258: # get lock on uniquecodes db
16259: my $lockhash = {
16260: $cnum."\0".'uniquecodes' => $env{'user.name'}.
16261: ':'.$env{'user.domain'},
16262: };
16263: my $tries = 0;
16264: my $gotlock = &Apache::lonnet::newput_dom('uniquecodes',$lockhash,$cdom);
16265: my ($code,$error);
16266:
16267: while (($gotlock ne 'ok') && ($tries<3)) {
16268: $tries ++;
16269: sleep 1;
16270: $gotlock = &Apache::lonnet::newput_dom('uniquecodes',$lockhash,$cdom);
16271: }
16272: if ($gotlock eq 'ok') {
16273: my %currcodes = &Apache::lonnet::dump_dom('uniquecodes',$cdom);
16274: my $gotcode;
16275: my $attempts = 0;
16276: while ((!$gotcode) && ($attempts < 100)) {
16277: $code = &generate_code();
16278: if (!exists($currcodes{$code})) {
16279: $gotcode = 1;
16280: unless (&Apache::lonnet::newput_dom('uniquecodes',{ $code => $cnum },$cdom) eq 'ok') {
16281: $error = 'nostore';
16282: }
16283: }
16284: $attempts ++;
16285: }
16286: my @del_lock = ($cnum."\0".'uniquecodes');
16287: my $dellockoutcome = &Apache::lonnet::del_dom('uniquecodes',\@del_lock,$cdom);
16288: } else {
16289: $error = 'nolock';
16290: }
16291: return ($code,$error);
16292: }
16293:
16294: sub generate_code {
16295: my $code;
16296: my @letts = qw(B C D G H J K M N P Q R S T V W X Z);
16297: for (my $i=0; $i<6; $i++) {
16298: my $lettnum = int (rand 2);
16299: my $item = '';
16300: if ($lettnum) {
16301: $item = $letts[int( rand(18) )];
16302: } else {
16303: $item = 1+int( rand(8) );
16304: }
16305: $code .= $item;
16306: }
16307: return $code;
16308: }
16309:
1.444 albertel 16310: ############################################################
16311: ############################################################
16312:
1.1237 raeburn 16313: # Community, Course and Placement Test
1.378 raeburn 16314: sub course_type {
16315: my ($cid) = @_;
16316: if (!defined($cid)) {
16317: $cid = $env{'request.course.id'};
16318: }
1.404 albertel 16319: if (defined($env{'course.'.$cid.'.type'})) {
16320: return $env{'course.'.$cid.'.type'};
1.378 raeburn 16321: } else {
16322: return 'Course';
1.377 raeburn 16323: }
16324: }
1.156 albertel 16325:
1.406 raeburn 16326: sub group_term {
16327: my $crstype = &course_type();
16328: my %names = (
16329: 'Course' => 'group',
1.865 raeburn 16330: 'Community' => 'group',
1.1237 raeburn 16331: 'Placement' => 'group',
1.406 raeburn 16332: );
16333: return $names{$crstype};
16334: }
16335:
1.902 raeburn 16336: sub course_types {
1.1310 raeburn 16337: my @types = ('official','unofficial','community','textbook','placement','lti');
1.902 raeburn 16338: my %typename = (
16339: official => 'Official course',
16340: unofficial => 'Unofficial course',
16341: community => 'Community',
1.1165 raeburn 16342: textbook => 'Textbook course',
1.1237 raeburn 16343: placement => 'Placement test',
1.1310 raeburn 16344: lti => 'LTI provider',
1.902 raeburn 16345: );
16346: return (\@types,\%typename);
16347: }
16348:
1.156 albertel 16349: sub icon {
16350: my ($file)=@_;
1.505 albertel 16351: my $curfext = lc((split(/\./,$file))[-1]);
1.168 albertel 16352: my $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/unknown.gif';
1.156 albertel 16353: my $embstyle = &Apache::loncommon::fileembstyle($curfext);
1.168 albertel 16354: if (!(!defined($embstyle) || $embstyle eq 'unk' || $embstyle eq 'hdn')) {
16355: if (-e $Apache::lonnet::perlvar{'lonDocRoot'}.'/'.
16356: $Apache::lonnet::perlvar{'lonIconsURL'}.'/'.
16357: $curfext.".gif") {
16358: $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/'.
16359: $curfext.".gif";
16360: }
16361: }
1.249 albertel 16362: return &lonhttpdurl($iconname);
1.154 albertel 16363: }
1.84 albertel 16364:
1.575 albertel 16365: sub lonhttpdurl {
1.692 www 16366: #
16367: # Had been used for "small fry" static images on separate port 8080.
16368: # Modify here if lightweight http functionality desired again.
16369: # Currently eliminated due to increasing firewall issues.
16370: #
1.575 albertel 16371: my ($url)=@_;
1.692 www 16372: return $url;
1.215 albertel 16373: }
16374:
1.213 albertel 16375: sub connection_aborted {
16376: my ($r)=@_;
16377: $r->print(" ");$r->rflush();
16378: my $c = $r->connection;
16379: return $c->aborted();
16380: }
16381:
1.221 foxr 16382: # Escapes strings that may have embedded 's that will be put into
1.222 foxr 16383: # strings as 'strings'.
16384: sub escape_single {
1.221 foxr 16385: my ($input) = @_;
1.223 albertel 16386: $input =~ s/\\/\\\\/g; # Escape the \'s..(must be first)>
1.221 foxr 16387: $input =~ s/\'/\\\'/g; # Esacpe the 's....
16388: return $input;
16389: }
1.223 albertel 16390:
1.222 foxr 16391: # Same as escape_single, but escape's "'s This
16392: # can be used for "strings"
16393: sub escape_double {
16394: my ($input) = @_;
16395: $input =~ s/\\/\\\\/g; # Escape the /'s..(must be first)>
16396: $input =~ s/\"/\\\"/g; # Esacpe the "s....
16397: return $input;
16398: }
1.223 albertel 16399:
1.222 foxr 16400: # Escapes the last element of a full URL.
16401: sub escape_url {
16402: my ($url) = @_;
1.238 raeburn 16403: my @urlslices = split(/\//, $url,-1);
1.369 www 16404: my $lastitem = &escape(pop(@urlslices));
1.1203 raeburn 16405: return &HTML::Entities::encode(join('/',@urlslices),"'").'/'.$lastitem;
1.222 foxr 16406: }
1.462 albertel 16407:
1.820 raeburn 16408: sub compare_arrays {
16409: my ($arrayref1,$arrayref2) = @_;
16410: my (@difference,%count);
16411: @difference = ();
16412: %count = ();
16413: if ((ref($arrayref1) eq 'ARRAY') && (ref($arrayref2) eq 'ARRAY')) {
16414: foreach my $element (@{$arrayref1}, @{$arrayref2}) { $count{$element}++; }
16415: foreach my $element (keys(%count)) {
16416: if ($count{$element} == 1) {
16417: push(@difference,$element);
16418: }
16419: }
16420: }
16421: return @difference;
16422: }
16423:
1.1322 raeburn 16424: sub lon_status_items {
16425: my %defaults = (
16426: E => 100,
16427: W => 4,
16428: N => 1,
1.1324 raeburn 16429: U => 5,
1.1322 raeburn 16430: threshold => 200,
16431: sysmail => 2500,
16432: );
16433: my %names = (
16434: E => 'Errors',
16435: W => 'Warnings',
16436: N => 'Notices',
1.1324 raeburn 16437: U => 'Unsent',
1.1322 raeburn 16438: );
16439: return (\%defaults,\%names);
16440: }
16441:
1.817 bisitz 16442: # -------------------------------------------------------- Initialize user login
1.462 albertel 16443: sub init_user_environment {
1.463 albertel 16444: my ($r, $username, $domain, $authhost, $form, $args) = @_;
1.462 albertel 16445: my $lonids=$Apache::lonnet::perlvar{'lonIDsDir'};
16446:
16447: my $public=($username eq 'public' && $domain eq 'public');
16448:
1.1062 raeburn 16449: my ($filename,$cookie,$userroles,$firstaccenv,$timerintenv);
1.462 albertel 16450: my $now=time;
16451:
16452: if ($public) {
16453: my $max_public=100;
16454: my $oldest;
16455: my $oldest_time=0;
16456: for(my $next=1;$next<=$max_public;$next++) {
16457: if (-e $lonids."/publicuser_$next.id") {
16458: my $mtime=(stat($lonids."/publicuser_$next.id"))[9];
16459: if ($mtime<$oldest_time || !$oldest_time) {
16460: $oldest_time=$mtime;
16461: $oldest=$next;
16462: }
16463: } else {
16464: $cookie="publicuser_$next";
16465: last;
16466: }
16467: }
16468: if (!$cookie) { $cookie="publicuser_$oldest"; }
16469: } else {
1.1275 raeburn 16470: # See if old ID present, if so, remove if this isn't a robot,
16471: # killing any existing non-robot sessions
1.463 albertel 16472: if (!$args->{'robot'}) {
16473: opendir(DIR,$lonids);
16474: while ($filename=readdir(DIR)) {
16475: if ($filename=~/^$username\_\d+\_$domain\_$authhost\.id$/) {
1.1320 raeburn 16476: if (tie(my %oldenv,'GDBM_File',"$lonids/$filename",
16477: &GDBM_READER(),0640)) {
1.1295 raeburn 16478: my $linkedfile;
1.1320 raeburn 16479: if (exists($oldenv{'user.linkedenv'})) {
16480: $linkedfile = $oldenv{'user.linkedenv'};
1.1295 raeburn 16481: }
1.1320 raeburn 16482: untie(%oldenv);
16483: if (unlink("$lonids/$filename")) {
16484: if ($linkedfile =~ /^[a-f0-9]+_linked$/) {
16485: if (-l "$lonids/$linkedfile.id") {
16486: unlink("$lonids/$linkedfile.id");
16487: }
1.1295 raeburn 16488: }
16489: }
16490: } else {
16491: unlink($lonids.'/'.$filename);
16492: }
1.463 albertel 16493: }
1.462 albertel 16494: }
1.463 albertel 16495: closedir(DIR);
1.1204 raeburn 16496: # If there is a undeleted lockfile for the user's paste buffer remove it.
16497: my $namespace = 'nohist_courseeditor';
16498: my $lockingkey = 'paste'."\0".'locked_num';
16499: my %lockhash = &Apache::lonnet::get($namespace,[$lockingkey],
16500: $domain,$username);
16501: if (exists($lockhash{$lockingkey})) {
16502: my $delresult = &Apache::lonnet::del($namespace,[$lockingkey],$domain,$username);
16503: unless ($delresult eq 'ok') {
16504: &Apache::lonnet::logthis("Failed to delete paste buffer locking key in $namespace for ".$username.":".$domain." Result was: $delresult");
16505: }
16506: }
1.462 albertel 16507: }
16508: # Give them a new cookie
1.463 albertel 16509: my $id = ($args->{'robot'} ? 'robot'.$args->{'robot'}
1.684 www 16510: : $now.$$.int(rand(10000)));
1.463 albertel 16511: $cookie="$username\_$id\_$domain\_$authhost";
1.462 albertel 16512:
16513: # Initialize roles
16514:
1.1062 raeburn 16515: ($userroles,$firstaccenv,$timerintenv) =
16516: &Apache::lonnet::rolesinit($domain,$username,$authhost);
1.462 albertel 16517: }
16518: # ------------------------------------ Check browser type and MathML capability
16519:
1.1194 raeburn 16520: my ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,$clientunicode,
16521: $clientos,$clientmobile,$clientinfo,$clientosversion) = &decode_user_agent($r);
1.462 albertel 16522:
16523: # ------------------------------------------------------------- Get environment
16524:
16525: my %userenv = &Apache::lonnet::dump('environment',$domain,$username);
16526: my ($tmp) = keys(%userenv);
1.1275 raeburn 16527: if ($tmp =~ /^(con_lost|error|no_such_host)/i) {
1.462 albertel 16528: undef(%userenv);
16529: }
16530: if (($userenv{'interface'}) && (!$form->{'interface'})) {
16531: $form->{'interface'}=$userenv{'interface'};
16532: }
16533: if ($userenv{'texengine'} eq 'ttm') { $clientmathml=1; }
16534:
16535: # --------------- Do not trust query string to be put directly into environment
1.817 bisitz 16536: foreach my $option ('interface','localpath','localres') {
16537: $form->{$option}=~s/[\n\r\=]//gs;
1.462 albertel 16538: }
16539: # --------------------------------------------------------- Write first profile
16540:
16541: {
16542: my %initial_env =
16543: ("user.name" => $username,
16544: "user.domain" => $domain,
16545: "user.home" => $authhost,
16546: "browser.type" => $clientbrowser,
16547: "browser.version" => $clientversion,
16548: "browser.mathml" => $clientmathml,
16549: "browser.unicode" => $clientunicode,
16550: "browser.os" => $clientos,
1.1137 raeburn 16551: "browser.mobile" => $clientmobile,
1.1141 raeburn 16552: "browser.info" => $clientinfo,
1.1194 raeburn 16553: "browser.osversion" => $clientosversion,
1.462 albertel 16554: "server.domain" => $Apache::lonnet::perlvar{'lonDefDomain'},
16555: "request.course.fn" => '',
16556: "request.course.uri" => '',
16557: "request.course.sec" => '',
16558: "request.role" => 'cm',
16559: "request.role.adv" => $env{'user.adv'},
16560: "request.host" => $ENV{'REMOTE_ADDR'},);
16561:
16562: if ($form->{'localpath'}) {
16563: $initial_env{"browser.localpath"} = $form->{'localpath'};
16564: $initial_env{"browser.localres"} = $form->{'localres'};
16565: }
16566:
16567: if ($form->{'interface'}) {
16568: $form->{'interface'}=~s/\W//gs;
16569: $initial_env{"browser.interface"} = $form->{'interface'};
16570: $env{'browser.interface'}=$form->{'interface'};
16571: }
16572:
1.1157 raeburn 16573: if ($form->{'iptoken'}) {
16574: my $lonhost = $r->dir_config('lonHostID');
16575: $initial_env{"user.noloadbalance"} = $lonhost;
16576: $env{'user.noloadbalance'} = $lonhost;
16577: }
16578:
1.1268 raeburn 16579: if ($form->{'noloadbalance'}) {
16580: my @hosts = &Apache::lonnet::current_machine_ids();
16581: my $hosthere = $form->{'noloadbalance'};
16582: if (grep(/^\Q$hosthere\E$/,@hosts)) {
16583: $initial_env{"user.noloadbalance"} = $hosthere;
16584: $env{'user.noloadbalance'} = $hosthere;
16585: }
16586: }
16587:
1.1016 raeburn 16588: unless ($domain eq 'public') {
1.1273 raeburn 16589: my %is_adv = ( is_adv => $env{'user.adv'} );
16590: my %domdef = &Apache::lonnet::get_domain_defaults($domain);
16591:
16592: foreach my $tool ('aboutme','blog','webdav','portfolio') {
16593: $userenv{'availabletools.'.$tool} =
16594: &Apache::lonnet::usertools_access($username,$domain,$tool,'reload',
16595: undef,\%userenv,\%domdef,\%is_adv);
16596: }
1.980 raeburn 16597:
1.1311 raeburn 16598: foreach my $crstype ('official','unofficial','community','textbook','placement','lti') {
1.1273 raeburn 16599: $userenv{'canrequest.'.$crstype} =
16600: &Apache::lonnet::usertools_access($username,$domain,$crstype,
16601: 'reload','requestcourses',
16602: \%userenv,\%domdef,\%is_adv);
16603: }
1.724 raeburn 16604:
1.1273 raeburn 16605: $userenv{'canrequest.author'} =
16606: &Apache::lonnet::usertools_access($username,$domain,'requestauthor',
16607: 'reload','requestauthor',
1.980 raeburn 16608: \%userenv,\%domdef,\%is_adv);
1.1273 raeburn 16609: my %reqauthor = &Apache::lonnet::get('requestauthor',['author_status','author'],
16610: $domain,$username);
16611: my $reqstatus = $reqauthor{'author_status'};
16612: if ($reqstatus eq 'approval' || $reqstatus eq 'approved') {
16613: if (ref($reqauthor{'author'}) eq 'HASH') {
16614: $userenv{'requestauthorqueued'} = $reqstatus.':'.
16615: $reqauthor{'author'}{'timestamp'};
16616: }
1.1092 raeburn 16617: }
1.1287 raeburn 16618: my ($types,$typename) = &course_types();
16619: if (ref($types) eq 'ARRAY') {
16620: my @options = ('approval','validate','autolimit');
16621: my $optregex = join('|',@options);
16622: my (%willtrust,%trustchecked);
16623: foreach my $type (@{$types}) {
16624: my $dom_str = $env{'environment.reqcrsotherdom.'.$type};
16625: if ($dom_str ne '') {
16626: my $updatedstr = '';
16627: my @possdomains = split(',',$dom_str);
16628: foreach my $entry (@possdomains) {
16629: my ($extdom,$extopt) = split(':',$entry);
16630: unless ($trustchecked{$extdom}) {
16631: $willtrust{$extdom} = &Apache::lonnet::will_trust('reqcrs',$domain,$extdom);
16632: $trustchecked{$extdom} = 1;
16633: }
16634: if ($willtrust{$extdom}) {
16635: $updatedstr .= $entry.',';
16636: }
16637: }
16638: $updatedstr =~ s/,$//;
16639: if ($updatedstr) {
16640: $userenv{'reqcrsotherdom.'.$type} = $updatedstr;
16641: } else {
16642: delete($userenv{'reqcrsotherdom.'.$type});
16643: }
16644: }
16645: }
16646: }
1.1092 raeburn 16647: }
1.462 albertel 16648: $env{'user.environment'} = "$lonids/$cookie.id";
1.1062 raeburn 16649:
1.462 albertel 16650: if (tie(my %disk_env,'GDBM_File',"$lonids/$cookie.id",
16651: &GDBM_WRCREAT(),0640)) {
16652: &_add_to_env(\%disk_env,\%initial_env);
16653: &_add_to_env(\%disk_env,\%userenv,'environment.');
16654: &_add_to_env(\%disk_env,$userroles);
1.1062 raeburn 16655: if (ref($firstaccenv) eq 'HASH') {
16656: &_add_to_env(\%disk_env,$firstaccenv);
16657: }
16658: if (ref($timerintenv) eq 'HASH') {
16659: &_add_to_env(\%disk_env,$timerintenv);
16660: }
1.463 albertel 16661: if (ref($args->{'extra_env'})) {
16662: &_add_to_env(\%disk_env,$args->{'extra_env'});
16663: }
1.462 albertel 16664: untie(%disk_env);
16665: } else {
1.705 tempelho 16666: &Apache::lonnet::logthis("<span style=\"color:blue;\">WARNING: ".
16667: 'Could not create environment storage in lonauth: '.$!.'</span>');
1.462 albertel 16668: return 'error: '.$!;
16669: }
16670: }
16671: $env{'request.role'}='cm';
16672: $env{'request.role.adv'}=$env{'user.adv'};
16673: $env{'browser.type'}=$clientbrowser;
16674:
16675: return $cookie;
16676:
16677: }
16678:
16679: sub _add_to_env {
16680: my ($idf,$env_data,$prefix) = @_;
1.676 raeburn 16681: if (ref($env_data) eq 'HASH') {
16682: while (my ($key,$value) = each(%$env_data)) {
16683: $idf->{$prefix.$key} = $value;
16684: $env{$prefix.$key} = $value;
16685: }
1.462 albertel 16686: }
16687: }
16688:
1.685 tempelho 16689: # --- Get the symbolic name of a problem and the url
16690: sub get_symb {
16691: my ($request,$silent) = @_;
1.726 raeburn 16692: (my $url=$env{'form.url'}) =~ s-^https?\://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
1.685 tempelho 16693: my $symb=($env{'form.symb'} ne '' ? $env{'form.symb'} : (&Apache::lonnet::symbread($url)));
16694: if ($symb eq '') {
16695: if (!$silent) {
1.1071 raeburn 16696: if (ref($request)) {
16697: $request->print("Unable to handle ambiguous references:$url:.");
16698: }
1.685 tempelho 16699: return ();
16700: }
16701: }
16702: &Apache::lonenc::check_decrypt(\$symb);
16703: return ($symb);
16704: }
16705:
16706: # --------------------------------------------------------------Get annotation
16707:
16708: sub get_annotation {
16709: my ($symb,$enc) = @_;
16710:
16711: my $key = $symb;
16712: if (!$enc) {
16713: $key =
16714: &Apache::lonnet::clutter((&Apache::lonnet::decode_symb($symb))[2]);
16715: }
16716: my %annotation=&Apache::lonnet::get('nohist_annotations',[$key]);
16717: return $annotation{$key};
16718: }
16719:
16720: sub clean_symb {
1.731 raeburn 16721: my ($symb,$delete_enc) = @_;
1.685 tempelho 16722:
16723: &Apache::lonenc::check_decrypt(\$symb);
16724: my $enc = $env{'request.enc'};
1.731 raeburn 16725: if ($delete_enc) {
1.730 raeburn 16726: delete($env{'request.enc'});
16727: }
1.685 tempelho 16728:
16729: return ($symb,$enc);
16730: }
1.462 albertel 16731:
1.1181 raeburn 16732: ############################################################
16733: ############################################################
16734:
16735: =pod
16736:
16737: =head1 Routines for building display used to search for courses
16738:
16739:
16740: =over 4
16741:
16742: =item * &build_filters()
16743:
16744: Create markup for a table used to set filters to use when selecting
1.1182 raeburn 16745: courses in a domain. Used by lonpickcourse.pm, lonmodifycourse.pm
16746: and quotacheck.pl
16747:
1.1181 raeburn 16748:
16749: Inputs:
16750:
16751: filterlist - anonymous array of fields to include as potential filters
16752:
16753: crstype - course type
16754:
16755: roleelement - fifth arg in selectcourse_link() populates fifth arg in javascript: opencrsbrowser() function, used
16756: to pop-open a course selector (will contain "extra element").
16757:
16758: multelement - if multiple course selections will be allowed, this will be a hidden form element: name: multiple; value: 1
16759:
16760: filter - anonymous hash of criteria and their values
16761:
16762: action - form action
16763:
16764: numfiltersref - ref to scalar (count of number of elements in institutional codes -- e.g., 4 for year, semester, department, and number)
16765:
1.1182 raeburn 16766: caller - caller context (e.g., set to 'modifycourse' when routine is called from lonmodifycourse.pm)
1.1181 raeburn 16767:
16768: cloneruname - username of owner of new course who wants to clone
16769:
16770: clonerudom - domain of owner of new course who wants to clone
16771:
16772: typeelem - text to use for left column in row containing course type (i.e., Course, Community or Course/Community)
16773:
16774: codetitlesref - reference to array of titles of components in institutional codes (official courses)
16775:
16776: codedom - domain
16777:
16778: formname - value of form element named "form".
16779:
16780: fixeddom - domain, if fixed.
16781:
16782: prevphase - value to assign to form element named "phase" when going back to the previous screen
16783:
16784: cnameelement - name of form element in form on opener page which will receive title of selected course
16785:
16786: cnumelement - name of form element in form on opener page which will receive courseID of selected course
16787:
16788: cdomelement - name of form element in form on opener page which will receive domain of selected course
16789:
16790: setroles - includes access constraint identifier when setting a roles-based condition for acces to a portfolio file
16791:
16792: clonetext - hidden form elements containing list of courses cloneable by intended course owner when DC creates a course
16793:
16794: clonewarning - warning message about missing information for intended course owner when DC creates a course
16795:
1.1182 raeburn 16796:
1.1181 raeburn 16797: Returns: $output - HTML for display of search criteria, and hidden form elements.
16798:
1.1182 raeburn 16799:
1.1181 raeburn 16800: Side Effects: None
16801:
16802: =cut
16803:
16804: # ---------------------------------------------- search for courses based on last activity etc.
16805:
16806: sub build_filters {
16807: my ($filterlist,$crstype,$roleelement,$multelement,$filter,$action,
16808: $numtitlesref,$caller,$cloneruname,$clonerudom,$typeelement,
16809: $codetitlesref,$codedom,$formname,$fixeddom,$prevphase,
16810: $cnameelement,$cnumelement,$cdomelement,$setroles,
16811: $clonetext,$clonewarning) = @_;
1.1182 raeburn 16812: my ($list,$jscript);
1.1181 raeburn 16813: my $onchange = 'javascript:updateFilters(this)';
16814: my ($domainselectform,$sincefilterform,$createdfilterform,
16815: $ownerdomselectform,$persondomselectform,$instcodeform,
16816: $typeselectform,$instcodetitle);
16817: if ($formname eq '') {
16818: $formname = $caller;
16819: }
16820: foreach my $item (@{$filterlist}) {
16821: unless (($item eq 'descriptfilter') || ($item eq 'instcodefilter') ||
16822: ($item eq 'sincefilter') || ($item eq 'createdfilter')) {
16823: if ($item eq 'domainfilter') {
16824: $filter->{$item} = &LONCAPA::clean_domain($filter->{$item});
16825: } elsif ($item eq 'coursefilter') {
16826: $filter->{$item} = &LONCAPA::clean_courseid($filter->{$item});
16827: } elsif ($item eq 'ownerfilter') {
16828: $filter->{$item} = &LONCAPA::clean_username($filter->{$item});
16829: } elsif ($item eq 'ownerdomfilter') {
16830: $filter->{'ownerdomfilter'} =
16831: &LONCAPA::clean_domain($filter->{$item});
16832: $ownerdomselectform = &select_dom_form($filter->{'ownerdomfilter'},
16833: 'ownerdomfilter',1);
16834: } elsif ($item eq 'personfilter') {
16835: $filter->{$item} = &LONCAPA::clean_username($filter->{$item});
16836: } elsif ($item eq 'persondomfilter') {
16837: $persondomselectform = &select_dom_form($filter->{'persondomfilter'},
16838: 'persondomfilter',1);
16839: } else {
16840: $filter->{$item} =~ s/\W//g;
16841: }
16842: if (!$filter->{$item}) {
16843: $filter->{$item} = '';
16844: }
16845: }
16846: if ($item eq 'domainfilter') {
16847: my $allow_blank = 1;
16848: if ($formname eq 'portform') {
16849: $allow_blank=0;
16850: } elsif ($formname eq 'studentform') {
16851: $allow_blank=0;
16852: }
16853: if ($fixeddom) {
16854: $domainselectform = '<input type="hidden" name="domainfilter"'.
16855: ' value="'.$codedom.'" />'.
16856: &Apache::lonnet::domain($codedom,'description');
16857: } else {
16858: $domainselectform = &select_dom_form($filter->{$item},
16859: 'domainfilter',
16860: $allow_blank,'',$onchange);
16861: }
16862: } else {
16863: $list->{$item} = &HTML::Entities::encode($filter->{$item},'<>&"');
16864: }
16865: }
16866:
16867: # last course activity filter and selection
16868: $sincefilterform = &timebased_select_form('sincefilter',$filter);
16869:
16870: # course created filter and selection
16871: if (exists($filter->{'createdfilter'})) {
16872: $createdfilterform = &timebased_select_form('createdfilter',$filter);
16873: }
16874:
1.1239 raeburn 16875: my $prefix = $crstype;
16876: if ($crstype eq 'Placement') {
16877: $prefix = 'Placement Test'
16878: }
1.1181 raeburn 16879: my %lt = &Apache::lonlocal::texthash(
1.1239 raeburn 16880: 'cac' => "$prefix Activity",
16881: 'ccr' => "$prefix Created",
16882: 'cde' => "$prefix Title",
16883: 'cdo' => "$prefix Domain",
1.1181 raeburn 16884: 'ins' => 'Institutional Code',
16885: 'inc' => 'Institutional Categorization',
1.1239 raeburn 16886: 'cow' => "$prefix Owner/Co-owner",
16887: 'cop' => "$prefix Personnel Includes",
1.1181 raeburn 16888: 'cog' => 'Type',
16889: );
16890:
16891: if (($formname eq 'ccrs') || ($formname eq 'requestcrs')) {
16892: my $typeval = 'Course';
16893: if ($crstype eq 'Community') {
16894: $typeval = 'Community';
1.1239 raeburn 16895: } elsif ($crstype eq 'Placement') {
16896: $typeval = 'Placement';
1.1181 raeburn 16897: }
16898: $typeselectform = '<input type="hidden" name="type" value="'.$typeval.'" />';
16899: } else {
16900: $typeselectform = '<select name="type" size="1"';
16901: if ($onchange) {
16902: $typeselectform .= ' onchange="'.$onchange.'"';
16903: }
16904: $typeselectform .= '>'."\n";
1.1237 raeburn 16905: foreach my $posstype ('Course','Community','Placement') {
1.1239 raeburn 16906: my $shown;
16907: if ($posstype eq 'Placement') {
16908: $shown = &mt('Placement Test');
16909: } else {
16910: $shown = &mt($posstype);
16911: }
1.1181 raeburn 16912: $typeselectform.='<option value="'.$posstype.'"'.
1.1239 raeburn 16913: ($posstype eq $crstype ? ' selected="selected" ' : ''). ">".$shown."</option>\n";
1.1181 raeburn 16914: }
16915: $typeselectform.="</select>";
16916: }
16917:
16918: my ($cloneableonlyform,$cloneabletitle);
16919: if (exists($filter->{'cloneableonly'})) {
16920: my $cloneableon = '';
16921: my $cloneableoff = ' checked="checked"';
16922: if ($filter->{'cloneableonly'}) {
16923: $cloneableon = $cloneableoff;
16924: $cloneableoff = '';
16925: }
16926: $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>';
16927: if ($formname eq 'ccrs') {
1.1187 bisitz 16928: $cloneabletitle = &mt('Cloneable for [_1]',$cloneruname.':'.$clonerudom);
1.1181 raeburn 16929: } else {
16930: $cloneabletitle = &mt('Cloneable by you');
16931: }
16932: }
16933: my $officialjs;
16934: if ($crstype eq 'Course') {
16935: if (exists($filter->{'instcodefilter'})) {
1.1182 raeburn 16936: # if (($fixeddom) || ($formname eq 'requestcrs') ||
16937: # ($formname eq 'modifycourse') || ($formname eq 'filterpicker')) {
16938: if ($codedom) {
1.1181 raeburn 16939: $officialjs = 1;
16940: ($instcodeform,$jscript,$$numtitlesref) =
16941: &Apache::courseclassifier::instcode_selectors($codedom,'filterpicker',
16942: $officialjs,$codetitlesref);
16943: if ($jscript) {
1.1182 raeburn 16944: $jscript = '<script type="text/javascript">'."\n".
16945: '// <![CDATA['."\n".
16946: $jscript."\n".
16947: '// ]]>'."\n".
16948: '</script>'."\n";
1.1181 raeburn 16949: }
16950: }
16951: if ($instcodeform eq '') {
16952: $instcodeform =
16953: '<input type="text" name="instcodefilter" size="10" value="'.
16954: $list->{'instcodefilter'}.'" />';
16955: $instcodetitle = $lt{'ins'};
16956: } else {
16957: $instcodetitle = $lt{'inc'};
16958: }
16959: if ($fixeddom) {
16960: $instcodetitle .= '<br />('.$codedom.')';
16961: }
16962: }
16963: }
16964: my $output = qq|
16965: <form method="post" name="filterpicker" action="$action">
16966: <input type="hidden" name="form" value="$formname" />
16967: |;
16968: if ($formname eq 'modifycourse') {
16969: $output .= '<input type="hidden" name="phase" value="courselist" />'."\n".
16970: '<input type="hidden" name="prevphase" value="'.
16971: $prevphase.'" />'."\n";
1.1198 musolffc 16972: } elsif ($formname eq 'quotacheck') {
16973: $output .= qq|
16974: <input type="hidden" name="sortby" value="" />
16975: <input type="hidden" name="sortorder" value="" />
16976: |;
16977: } else {
1.1181 raeburn 16978: my $name_input;
16979: if ($cnameelement ne '') {
16980: $name_input = '<input type="hidden" name="cnameelement" value="'.
16981: $cnameelement.'" />';
16982: }
16983: $output .= qq|
1.1182 raeburn 16984: <input type="hidden" name="cnumelement" value="$cnumelement" />
16985: <input type="hidden" name="cdomelement" value="$cdomelement" />
1.1181 raeburn 16986: $name_input
16987: $roleelement
16988: $multelement
16989: $typeelement
16990: |;
16991: if ($formname eq 'portform') {
16992: $output .= '<input type="hidden" name="setroles" value="'.$setroles.'" />'."\n";
16993: }
16994: }
16995: if ($fixeddom) {
16996: $output .= '<input type="hidden" name="fixeddom" value="'.$fixeddom.'" />'."\n";
16997: }
16998: $output .= "<br />\n".&Apache::lonhtmlcommon::start_pick_box();
16999: if ($sincefilterform) {
17000: $output .= &Apache::lonhtmlcommon::row_title($lt{'cac'})
17001: .$sincefilterform
17002: .&Apache::lonhtmlcommon::row_closure();
17003: }
17004: if ($createdfilterform) {
17005: $output .= &Apache::lonhtmlcommon::row_title($lt{'ccr'})
17006: .$createdfilterform
17007: .&Apache::lonhtmlcommon::row_closure();
17008: }
17009: if ($domainselectform) {
17010: $output .= &Apache::lonhtmlcommon::row_title($lt{'cdo'})
17011: .$domainselectform
17012: .&Apache::lonhtmlcommon::row_closure();
17013: }
17014: if ($typeselectform) {
17015: if (($formname eq 'ccrs') || ($formname eq 'requestcrs')) {
17016: $output .= $typeselectform;
17017: } else {
17018: $output .= &Apache::lonhtmlcommon::row_title($lt{'cog'})
17019: .$typeselectform
17020: .&Apache::lonhtmlcommon::row_closure();
17021: }
17022: }
17023: if ($instcodeform) {
17024: $output .= &Apache::lonhtmlcommon::row_title($instcodetitle)
17025: .$instcodeform
17026: .&Apache::lonhtmlcommon::row_closure();
17027: }
17028: if (exists($filter->{'ownerfilter'})) {
17029: $output .= &Apache::lonhtmlcommon::row_title($lt{'cow'}).
17030: '<table><tr><td>'.&mt('Username').'<br />'.
17031: '<input type="text" name="ownerfilter" size="20" value="'.
17032: $list->{'ownerfilter'}.'" /></td><td>'.&mt('Domain').'<br />'.
17033: $ownerdomselectform.'</td></tr></table>'.
17034: &Apache::lonhtmlcommon::row_closure();
17035: }
17036: if (exists($filter->{'personfilter'})) {
17037: $output .= &Apache::lonhtmlcommon::row_title($lt{'cop'}).
17038: '<table><tr><td>'.&mt('Username').'<br />'.
17039: '<input type="text" name="personfilter" size="20" value="'.
17040: $list->{'personfilter'}.'" /></td><td>'.&mt('Domain').'<br />'.
17041: $persondomselectform.'</td></tr></table>'.
17042: &Apache::lonhtmlcommon::row_closure();
17043: }
17044: if (exists($filter->{'coursefilter'})) {
17045: $output .= &Apache::lonhtmlcommon::row_title(&mt('LON-CAPA course ID'))
17046: .'<input type="text" name="coursefilter" size="25" value="'
17047: .$list->{'coursefilter'}.'" />'
17048: .&Apache::lonhtmlcommon::row_closure();
17049: }
17050: if ($cloneableonlyform) {
17051: $output .= &Apache::lonhtmlcommon::row_title($cloneabletitle).
17052: $cloneableonlyform.&Apache::lonhtmlcommon::row_closure();
17053: }
17054: if (exists($filter->{'descriptfilter'})) {
17055: $output .= &Apache::lonhtmlcommon::row_title($lt{'cde'})
17056: .'<input type="text" name="descriptfilter" size="40" value="'
17057: .$list->{'descriptfilter'}.'" />'
17058: .&Apache::lonhtmlcommon::row_closure(1);
17059: }
17060: $output .= &Apache::lonhtmlcommon::end_pick_box().'<p>'.$clonetext."\n".
17061: '<input type="hidden" name="updater" value="" />'."\n".
17062: '<input type="submit" name="gosearch" value="'.
17063: &mt('Search').'" /></p>'."\n".'</form>'."\n".'<hr />'."\n";
17064: return $jscript.$clonewarning.$output;
17065: }
17066:
17067: =pod
17068:
17069: =item * &timebased_select_form()
17070:
1.1182 raeburn 17071: Create markup for a dropdown list used to select a time-based
1.1181 raeburn 17072: filter e.g., Course Activity, Course Created, when searching for courses
17073: or communities
17074:
17075: Inputs:
17076:
17077: item - name of form element (sincefilter or createdfilter)
17078:
17079: filter - anonymous hash of criteria and their values
17080:
17081: Returns: HTML for a select box contained a blank, then six time selections,
17082: with value set in incoming form variables currently selected.
17083:
17084: Side Effects: None
17085:
17086: =cut
17087:
17088: sub timebased_select_form {
17089: my ($item,$filter) = @_;
17090: if (ref($filter) eq 'HASH') {
17091: $filter->{$item} =~ s/[^\d-]//g;
17092: if (!$filter->{$item}) { $filter->{$item}=-1; }
17093: return &select_form(
17094: $filter->{$item},
17095: $item,
17096: { '-1' => '',
17097: '86400' => &mt('today'),
17098: '604800' => &mt('last week'),
17099: '2592000' => &mt('last month'),
17100: '7776000' => &mt('last three months'),
17101: '15552000' => &mt('last six months'),
17102: '31104000' => &mt('last year'),
17103: 'select_form_order' =>
17104: ['-1','86400','604800','2592000','7776000',
17105: '15552000','31104000']});
17106: }
17107: }
17108:
17109: =pod
17110:
17111: =item * &js_changer()
17112:
17113: Create script tag containing Javascript used to submit course search form
1.1183 raeburn 17114: when course type or domain is changed, and also to hide 'Searching ...' on
17115: page load completion for page showing search result.
1.1181 raeburn 17116:
17117: Inputs: None
17118:
1.1183 raeburn 17119: Returns: markup containing updateFilters() and hideSearching() javascript functions.
1.1181 raeburn 17120:
17121: Side Effects: None
17122:
17123: =cut
17124:
17125: sub js_changer {
17126: return <<ENDJS;
17127: <script type="text/javascript">
17128: // <![CDATA[
17129: function updateFilters(caller) {
17130: if (typeof(caller) != "undefined") {
17131: document.filterpicker.updater.value = caller.name;
17132: }
17133: document.filterpicker.submit();
17134: }
1.1183 raeburn 17135:
17136: function hideSearching() {
17137: if (document.getElementById('searching')) {
17138: document.getElementById('searching').style.display = 'none';
17139: }
17140: return;
17141: }
17142:
1.1181 raeburn 17143: // ]]>
17144: </script>
17145:
17146: ENDJS
17147: }
17148:
17149: =pod
17150:
1.1182 raeburn 17151: =item * &search_courses()
17152:
17153: Process selected filters form course search form and pass to lonnet::courseiddump
17154: to retrieve a hash for which keys are courseIDs which match the selected filters.
17155:
17156: Inputs:
17157:
17158: dom - domain being searched
17159:
17160: type - course type ('Course' or 'Community' or '.' if any).
17161:
17162: filter - anonymous hash of criteria and their values
17163:
17164: numtitles - for institutional codes - number of categories
17165:
17166: cloneruname - optional username of new course owner
17167:
17168: clonerudom - optional domain of new course owner
17169:
1.1221 raeburn 17170: domcloner - optional "domcloner" flag; has value=1 if user has ccc priv in domain being filtered by,
1.1182 raeburn 17171: (used when DC is using course creation form)
17172:
17173: codetitles - reference to array of titles of components in institutional codes (official courses).
17174:
1.1221 raeburn 17175: cc_clone - escaped comma separated list of courses for which course cloner has active CC role
17176: (and so can clone automatically)
17177:
17178: reqcrsdom - domain of new course, where search_courses is used to identify potential courses to clone
17179:
17180: reqinstcode - institutional code of new course, where search_courses is used to identify potential
17181: courses to clone
1.1182 raeburn 17182:
17183: Returns: %courses - hash of courses satisfying search criteria, keys = course IDs, values are corresponding colon-separated escaped description, institutional code, owner and type.
17184:
17185:
17186: Side Effects: None
17187:
17188: =cut
17189:
17190:
17191: sub search_courses {
1.1221 raeburn 17192: my ($dom,$type,$filter,$numtitles,$cloneruname,$clonerudom,$domcloner,$codetitles,
17193: $cc_clone,$reqcrsdom,$reqinstcode) = @_;
1.1182 raeburn 17194: my (%courses,%showcourses,$cloner);
17195: if (($filter->{'ownerfilter'} ne '') ||
17196: ($filter->{'ownerdomfilter'} ne '')) {
17197: $filter->{'combownerfilter'} = $filter->{'ownerfilter'}.':'.
17198: $filter->{'ownerdomfilter'};
17199: }
17200: foreach my $item ('descriptfilter','coursefilter','combownerfilter') {
17201: if (!$filter->{$item}) {
17202: $filter->{$item}='.';
17203: }
17204: }
17205: my $now = time;
17206: my $timefilter =
17207: ($filter->{'sincefilter'}==-1?1:$now-$filter->{'sincefilter'});
17208: my ($createdbefore,$createdafter);
17209: if (($filter->{'createdfilter'} ne '') && ($filter->{'createdfilter'} !=-1)) {
17210: $createdbefore = $now;
17211: $createdafter = $now-$filter->{'createdfilter'};
17212: }
17213: my ($instcodefilter,$regexpok);
17214: if ($numtitles) {
17215: if ($env{'form.official'} eq 'on') {
17216: $instcodefilter =
17217: &Apache::courseclassifier::instcode_search_str($dom,$numtitles,$codetitles);
17218: $regexpok = 1;
17219: } elsif ($env{'form.official'} eq 'off') {
17220: $instcodefilter = &Apache::courseclassifier::instcode_search_str($dom,$numtitles,$codetitles);
17221: unless ($instcodefilter eq '') {
17222: $regexpok = -1;
17223: }
17224: }
17225: } else {
17226: $instcodefilter = $filter->{'instcodefilter'};
17227: }
17228: if ($instcodefilter eq '') { $instcodefilter = '.'; }
17229: if ($type eq '') { $type = '.'; }
17230:
17231: if (($clonerudom ne '') && ($cloneruname ne '')) {
17232: $cloner = $cloneruname.':'.$clonerudom;
17233: }
17234: %courses = &Apache::lonnet::courseiddump($dom,
17235: $filter->{'descriptfilter'},
17236: $timefilter,
17237: $instcodefilter,
17238: $filter->{'combownerfilter'},
17239: $filter->{'coursefilter'},
17240: undef,undef,$type,$regexpok,undef,undef,
1.1221 raeburn 17241: undef,undef,$cloner,$cc_clone,
1.1182 raeburn 17242: $filter->{'cloneableonly'},
17243: $createdbefore,$createdafter,undef,
1.1221 raeburn 17244: $domcloner,undef,$reqcrsdom,$reqinstcode);
1.1182 raeburn 17245: if (($filter->{'personfilter'} ne '') && ($filter->{'persondomfilter'} ne '')) {
17246: my $ccrole;
17247: if ($type eq 'Community') {
17248: $ccrole = 'co';
17249: } else {
17250: $ccrole = 'cc';
17251: }
17252: my %rolehash = &Apache::lonnet::get_my_roles($filter->{'personfilter'},
17253: $filter->{'persondomfilter'},
17254: 'userroles',undef,
17255: [$ccrole,'in','ad','ep','ta','cr'],
17256: $dom);
17257: foreach my $role (keys(%rolehash)) {
17258: my ($cnum,$cdom,$courserole) = split(':',$role);
17259: my $cid = $cdom.'_'.$cnum;
17260: if (exists($courses{$cid})) {
17261: if (ref($courses{$cid}) eq 'HASH') {
17262: if (ref($courses{$cid}{roles}) eq 'ARRAY') {
17263: if (!grep(/^\Q$courserole\E$/,@{$courses{$cid}{roles}})) {
1.1263 raeburn 17264: push(@{$courses{$cid}{roles}},$courserole);
1.1182 raeburn 17265: }
17266: } else {
17267: $courses{$cid}{roles} = [$courserole];
17268: }
17269: $showcourses{$cid} = $courses{$cid};
17270: }
17271: }
17272: }
17273: %courses = %showcourses;
17274: }
17275: return %courses;
17276: }
17277:
17278: =pod
17279:
1.1181 raeburn 17280: =back
17281:
1.1207 raeburn 17282: =head1 Routines for version requirements for current course.
17283:
17284: =over 4
17285:
17286: =item * &check_release_required()
17287:
17288: Compares required LON-CAPA version with version on server, and
17289: if required version is newer looks for a server with the required version.
17290:
17291: Looks first at servers in user's owen domain; if none suitable, looks at
17292: servers in course's domain are permitted to host sessions for user's domain.
17293:
17294: Inputs:
17295:
17296: $loncaparev - Version on current server (format: Major.Minor.Subrelease-datestamp)
17297:
17298: $courseid - Course ID of current course
17299:
17300: $rolecode - User's current role in course (for switchserver query string).
17301:
17302: $required - LON-CAPA version needed by course (format: Major.Minor).
17303:
17304:
17305: Returns:
17306:
17307: $switchserver - query string tp append to /adm/switchserver call (if
17308: current server's LON-CAPA version is too old.
17309:
17310: $warning - Message is displayed if no suitable server could be found.
17311:
17312: =cut
17313:
17314: sub check_release_required {
17315: my ($loncaparev,$courseid,$rolecode,$required) = @_;
17316: my ($switchserver,$warning);
17317: if ($required ne '') {
17318: my ($reqdmajor,$reqdminor) = ($required =~ /^(\d+)\.(\d+)$/);
17319: my ($major,$minor) = ($loncaparev =~ /^\'?(\d+)\.(\d+)\.[\w.\-]+\'?$/);
17320: if ($reqdmajor ne '' && $reqdminor ne '') {
17321: my $otherserver;
17322: if (($major eq '' && $minor eq '') ||
17323: (($reqdmajor > $major) || (($reqdmajor == $major) && ($reqdminor > $minor)))) {
17324: my ($userdomserver) = &Apache::lonnet::choose_server($env{'user.domain'},undef,$required,1);
17325: my $switchlcrev =
17326: &Apache::lonnet::get_server_loncaparev($env{'user.domain'},
17327: $userdomserver);
17328: my ($swmajor,$swminor) = ($switchlcrev =~ /^\'?(\d+)\.(\d+)\.[\w.\-]+\'?$/);
17329: if (($swmajor eq '' && $swminor eq '') || ($reqdmajor > $swmajor) ||
17330: (($reqdmajor == $swmajor) && ($reqdminor > $swminor))) {
17331: my $cdom = $env{'course.'.$courseid.'.domain'};
17332: if ($cdom ne $env{'user.domain'}) {
17333: my ($coursedomserver,$coursehostname) = &Apache::lonnet::choose_server($cdom,undef,$required,1);
17334: my $serverhomeID = &Apache::lonnet::get_server_homeID($coursehostname);
17335: my $serverhomedom = &Apache::lonnet::host_domain($serverhomeID);
17336: my %defdomdefaults = &Apache::lonnet::get_domain_defaults($serverhomedom);
17337: my %udomdefaults = &Apache::lonnet::get_domain_defaults($env{'user.domain'});
17338: my $remoterev = &Apache::lonnet::get_server_loncaparev($serverhomedom,$coursedomserver);
17339: my $canhost =
17340: &Apache::lonnet::can_host_session($env{'user.domain'},
17341: $coursedomserver,
17342: $remoterev,
17343: $udomdefaults{'remotesessions'},
17344: $defdomdefaults{'hostedsessions'});
17345:
17346: if ($canhost) {
17347: $otherserver = $coursedomserver;
17348: } else {
17349: $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.");
17350: }
17351: } else {
17352: $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).");
17353: }
17354: } else {
17355: $otherserver = $userdomserver;
17356: }
17357: }
17358: if ($otherserver ne '') {
17359: $switchserver = 'otherserver='.$otherserver.'&role='.$rolecode;
17360: }
17361: }
17362: }
17363: return ($switchserver,$warning);
17364: }
17365:
17366: =pod
17367:
17368: =item * &check_release_result()
17369:
17370: Inputs:
17371:
17372: $switchwarning - Warning message if no suitable server found to host session.
17373:
17374: $switchserver - query string to append to /adm/switchserver containing lonHostID
17375: and current role.
17376:
17377: Returns: HTML to display with information about requirement to switch server.
17378: Either displaying warning with link to Roles/Courses screen or
17379: display link to switchserver.
17380:
1.1181 raeburn 17381: =cut
17382:
1.1207 raeburn 17383: sub check_release_result {
17384: my ($switchwarning,$switchserver) = @_;
17385: my $output = &start_page('Selected course unavailable on this server').
17386: '<p class="LC_warning">';
17387: if ($switchwarning) {
17388: $output .= $switchwarning.'<br /><a href="/adm/roles">';
17389: if (&show_course()) {
17390: $output .= &mt('Display courses');
17391: } else {
17392: $output .= &mt('Display roles');
17393: }
17394: $output .= '</a>';
17395: } elsif ($switchserver) {
17396: $output .= &mt('This course requires a newer version of LON-CAPA than is installed on this server.').
17397: '<br />'.
17398: '<a href="/adm/switchserver?'.$switchserver.'">'.
17399: &mt('Switch Server').
17400: '</a>';
17401: }
17402: $output .= '</p>'.&end_page();
17403: return $output;
17404: }
17405:
17406: =pod
17407:
17408: =item * &needs_coursereinit()
17409:
17410: Determine if course contents stored for user's session needs to be
17411: refreshed, because content has changed since "Big Hash" last tied.
17412:
17413: Check for change is made if time last checked is more than 10 minutes ago
17414: (by default).
17415:
17416: Inputs:
17417:
17418: $loncaparev - Version on current server (format: Major.Minor.Subrelease-datestamp)
17419:
17420: $interval (optional) - Time which may elapse (in s) between last check for content
17421: change in current course. (default: 600 s).
17422:
17423: Returns: an array; first element is:
17424:
17425: =over 4
17426:
17427: 'switch' - if content updates mean user's session
17428: needs to be switched to a server running a newer LON-CAPA version
17429:
17430: 'update' - if course session needs to be refreshed (i.e., Big Hash needs to be reloaded)
17431: on current server hosting user's session
17432:
17433: '' - if no action required.
17434:
17435: =back
17436:
17437: If first item element is 'switch':
17438:
17439: second item is $switchwarning - Warning message if no suitable server found to host session.
17440:
17441: third item is $switchserver - query string to append to /adm/switchserver containing lonHostID
17442: and current role.
17443:
17444: otherwise: no other elements returned.
17445:
17446: =back
17447:
17448: =cut
17449:
17450: sub needs_coursereinit {
17451: my ($loncaparev,$interval) = @_;
17452: return() unless ($env{'request.course.id'} && $env{'request.course.tied'});
17453: my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
17454: my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
17455: my $now = time;
17456: if ($interval eq '') {
17457: $interval = 600;
17458: }
17459: if (($now-$env{'request.course.timechecked'})>$interval) {
1.1282 raeburn 17460: &Apache::lonnet::appenv({'request.course.timechecked'=>$now});
1.1283 raeburn 17461: my $blocked = &blocking_status('reinit',$cnum,$cdom,undef,1);
1.1282 raeburn 17462: if ($blocked) {
17463: return ();
17464: }
1.1207 raeburn 17465: my $lastchange = &Apache::lonnet::get_coursechange($cdom,$cnum);
17466: if ($lastchange > $env{'request.course.tied'}) {
17467: my %curr_reqd_hash = &Apache::lonnet::userenvironment($cdom,$cnum,'internal.releaserequired');
17468: if ($curr_reqd_hash{'internal.releaserequired'} ne '') {
17469: my $required = $env{'course.'.$cdom.'_'.$cnum.'.internal.releaserequired'};
17470: if ($curr_reqd_hash{'internal.releaserequired'} ne $required) {
17471: &Apache::lonnet::appenv({'course.'.$cdom.'_'.$cnum.'.internal.releaserequired' =>
17472: $curr_reqd_hash{'internal.releaserequired'}});
17473: my ($switchserver,$switchwarning) =
17474: &check_release_required($loncaparev,$cdom.'_'.$cnum,$env{'request.role'},
17475: $curr_reqd_hash{'internal.releaserequired'});
17476: if ($switchwarning ne '' || $switchserver ne '') {
17477: return ('switch',$switchwarning,$switchserver);
17478: }
17479: }
17480: }
17481: return ('update');
17482: }
17483: }
17484: return ();
17485: }
1.1181 raeburn 17486:
1.1083 raeburn 17487: sub update_content_constraints {
1.1326 raeburn 17488: my ($cdom,$cnum,$chome,$cid,$keeporder) = @_;
1.1083 raeburn 17489: my %curr_reqd_hash = &Apache::lonnet::userenvironment($cdom,$cnum,'internal.releaserequired');
17490: my ($reqdmajor,$reqdminor) = split(/\./,$curr_reqd_hash{'internal.releaserequired'});
1.1307 raeburn 17491: my (%checkresponsetypes,%checkcrsrestypes);
1.1083 raeburn 17492: foreach my $key (keys(%Apache::lonnet::needsrelease)) {
1.1236 raeburn 17493: my ($item,$name,$value) = split(/:/,$key);
1.1083 raeburn 17494: if ($item eq 'resourcetag') {
17495: if ($name eq 'responsetype') {
17496: $checkresponsetypes{$value} = $Apache::lonnet::needsrelease{$key}
17497: }
1.1307 raeburn 17498: } elsif ($item eq 'course') {
17499: if ($name eq 'courserestype') {
17500: $checkcrsrestypes{$value} = $Apache::lonnet::needsrelease{$key};
17501: }
1.1083 raeburn 17502: }
17503: }
17504: my $navmap = Apache::lonnavmaps::navmap->new();
17505: if (defined($navmap)) {
1.1307 raeburn 17506: my (%allresponses,%allcrsrestypes);
17507: foreach my $res ($navmap->retrieveResources(undef,sub { $_[0]->is_problem() || $_[0]->is_tool() },1,0)) {
17508: if ($res->is_tool()) {
17509: if ($allcrsrestypes{'exttool'}) {
17510: $allcrsrestypes{'exttool'} ++;
17511: } else {
17512: $allcrsrestypes{'exttool'} = 1;
17513: }
17514: next;
17515: }
1.1083 raeburn 17516: my %responses = $res->responseTypes();
17517: foreach my $key (keys(%responses)) {
17518: next unless(exists($checkresponsetypes{$key}));
17519: $allresponses{$key} += $responses{$key};
17520: }
17521: }
17522: foreach my $key (keys(%allresponses)) {
17523: my ($major,$minor) = split(/\./,$checkresponsetypes{$key});
17524: if (($major > $reqdmajor) || ($major == $reqdmajor && $minor > $reqdminor)) {
17525: ($reqdmajor,$reqdminor) = ($major,$minor);
17526: }
17527: }
1.1307 raeburn 17528: foreach my $key (keys(%allcrsrestypes)) {
1.1308 raeburn 17529: my ($major,$minor) = split(/\./,$checkcrsrestypes{$key});
1.1307 raeburn 17530: if (($major > $reqdmajor) || ($major == $reqdmajor && $minor > $reqdminor)) {
17531: ($reqdmajor,$reqdminor) = ($major,$minor);
17532: }
17533: }
1.1083 raeburn 17534: undef($navmap);
17535: }
1.1326 raeburn 17536: my (@resources,@order,@resparms,@zombies);
17537: if ($keeporder) {
17538: use LONCAPA::map;
17539: @resources = @LONCAPA::map::resources;
17540: @order = @LONCAPA::map::order;
17541: @resparms = @LONCAPA::map::resparms;
17542: @zombies = @LONCAPA::map::zombies;
17543: }
1.1308 raeburn 17544: my $suppmap = 'supplemental.sequence';
17545: my ($suppcount,$supptools,$errors) = (0,0,0);
17546: ($suppcount,$supptools,$errors) = &recurse_supplemental($cnum,$cdom,$suppmap,
17547: $suppcount,$supptools,$errors);
1.1326 raeburn 17548: if ($keeporder) {
17549: @LONCAPA::map::resources = @resources;
17550: @LONCAPA::map::order = @order;
17551: @LONCAPA::map::resparms = @resparms;
17552: @LONCAPA::map::zombies = @zombies;
17553: }
1.1308 raeburn 17554: if ($supptools) {
17555: my ($major,$minor) = split(/\./,$checkcrsrestypes{'exttool'});
17556: if (($major > $reqdmajor) || ($major == $reqdmajor && $minor > $reqdminor)) {
17557: ($reqdmajor,$reqdminor) = ($major,$minor);
17558: }
17559: }
1.1083 raeburn 17560: unless (($reqdmajor eq '') && ($reqdminor eq '')) {
17561: &Apache::lonnet::update_released_required($reqdmajor.'.'.$reqdminor,$cdom,$cnum,$chome,$cid);
17562: }
17563: return;
17564: }
17565:
1.1110 raeburn 17566: sub allmaps_incourse {
17567: my ($cdom,$cnum,$chome,$cid) = @_;
17568: if ($cdom eq '' || $cnum eq '' || $chome eq '' || $cid eq '') {
17569: $cid = $env{'request.course.id'};
17570: $cdom = $env{'course.'.$cid.'.domain'};
17571: $cnum = $env{'course.'.$cid.'.num'};
17572: $chome = $env{'course.'.$cid.'.home'};
17573: }
17574: my %allmaps = ();
17575: my $lastchange =
17576: &Apache::lonnet::get_coursechange($cdom,$cnum);
17577: if ($lastchange > $env{'request.course.tied'}) {
17578: my ($furl,$ferr) = &Apache::lonuserstate::readmap("$cdom/$cnum");
17579: unless ($ferr) {
1.1326 raeburn 17580: &update_content_constraints($cdom,$cnum,$chome,$cid,1);
1.1110 raeburn 17581: }
17582: }
17583: my $navmap = Apache::lonnavmaps::navmap->new();
17584: if (defined($navmap)) {
17585: foreach my $res ($navmap->retrieveResources(undef,sub { $_[0]->is_map() },1,0,1)) {
17586: $allmaps{$res->src()} = 1;
17587: }
17588: }
17589: return \%allmaps;
17590: }
17591:
1.1083 raeburn 17592: sub parse_supplemental_title {
17593: my ($title) = @_;
17594:
17595: my ($foldertitle,$renametitle);
17596: if ($title =~ /&&&/) {
17597: $title = &HTML::Entites::decode($title);
17598: }
17599: if ($title =~ m/^(\d+)___&&&___($match_username)___&&&___($match_domain)___&&&___(.*)$/) {
17600: $renametitle=$4;
17601: my ($time,$uname,$udom) = ($1,$2,$3);
17602: $foldertitle=&Apache::lontexconvert::msgtexconverted($4);
17603: my $name = &plainname($uname,$udom);
17604: $name = &HTML::Entities::encode($name,'"<>&\'');
17605: $renametitle = &HTML::Entities::encode($renametitle,'"<>&\'');
17606: $title='<i>'.&Apache::lonlocal::locallocaltime($time).'</i> '.
17607: $name.': <br />'.$foldertitle;
17608: }
17609: if (wantarray) {
17610: return ($title,$foldertitle,$renametitle);
17611: }
17612: return $title;
17613: }
17614:
1.1143 raeburn 17615: sub recurse_supplemental {
1.1308 raeburn 17616: my ($cnum,$cdom,$suppmap,$numfiles,$numexttools,$errors) = @_;
1.1143 raeburn 17617: if ($suppmap) {
17618: my ($errtext,$fatal) = &LONCAPA::map::mapread('/uploaded/'.$cdom.'/'.$cnum.'/'.$suppmap);
17619: if ($fatal) {
17620: $errors ++;
17621: } else {
17622: if ($#LONCAPA::map::resources > 0) {
17623: foreach my $res (@LONCAPA::map::resources) {
17624: my ($title,$src,$ext,$type,$status)=split(/\:/,$res);
17625: if (($src ne '') && ($status eq 'res')) {
1.1146 raeburn 17626: if ($src =~ m{^\Q/uploaded/$cdom/$cnum/\E(supplemental_\d+\.sequence)$}) {
1.1308 raeburn 17627: ($numfiles,$numexttools,$errors) = &recurse_supplemental($cnum,$cdom,$1,
17628: $numfiles,$numexttools,$errors);
1.1143 raeburn 17629: } else {
1.1308 raeburn 17630: if ($src =~ m{^/adm/$cdom/$cnum/\d+/ext\.tool$}) {
17631: $numexttools ++;
17632: }
1.1143 raeburn 17633: $numfiles ++;
17634: }
17635: }
17636: }
17637: }
17638: }
17639: }
1.1308 raeburn 17640: return ($numfiles,$numexttools,$errors);
1.1143 raeburn 17641: }
17642:
1.1101 raeburn 17643: sub symb_to_docspath {
1.1267 raeburn 17644: my ($symb,$navmapref) = @_;
17645: return unless ($symb && ref($navmapref));
1.1101 raeburn 17646: my ($mapurl,$id,$resurl) = &Apache::lonnet::decode_symb($symb);
17647: if ($resurl=~/\.(sequence|page)$/) {
17648: $mapurl=$resurl;
17649: } elsif ($resurl eq 'adm/navmaps') {
17650: $mapurl=$env{'course.'.$env{'request.course.id'}.'.url'};
17651: }
17652: my $mapresobj;
1.1267 raeburn 17653: unless (ref($$navmapref)) {
17654: $$navmapref = Apache::lonnavmaps::navmap->new();
17655: }
17656: if (ref($$navmapref)) {
17657: $mapresobj = $$navmapref->getResourceByUrl($mapurl);
1.1101 raeburn 17658: }
17659: $mapurl=~s{^.*/([^/]+)\.(\w+)$}{$1};
17660: my $type=$2;
17661: my $path;
17662: if (ref($mapresobj)) {
17663: my $pcslist = $mapresobj->map_hierarchy();
17664: if ($pcslist ne '') {
17665: foreach my $pc (split(/,/,$pcslist)) {
17666: next if ($pc <= 1);
1.1267 raeburn 17667: my $res = $$navmapref->getByMapPc($pc);
1.1101 raeburn 17668: if (ref($res)) {
17669: my $thisurl = $res->src();
17670: $thisurl=~s{^.*/([^/]+)\.\w+$}{$1};
17671: my $thistitle = $res->title();
17672: $path .= '&'.
17673: &Apache::lonhtmlcommon::entity_encode($thisurl).'&'.
1.1146 raeburn 17674: &escape($thistitle).
1.1101 raeburn 17675: ':'.$res->randompick().
17676: ':'.$res->randomout().
17677: ':'.$res->encrypted().
17678: ':'.$res->randomorder().
17679: ':'.$res->is_page();
17680: }
17681: }
17682: }
17683: $path =~ s/^\&//;
17684: my $maptitle = $mapresobj->title();
17685: if ($mapurl eq 'default') {
1.1129 raeburn 17686: $maptitle = 'Main Content';
1.1101 raeburn 17687: }
17688: $path .= (($path ne '')? '&' : '').
17689: &Apache::lonhtmlcommon::entity_encode($mapurl).'&'.
1.1146 raeburn 17690: &escape($maptitle).
1.1101 raeburn 17691: ':'.$mapresobj->randompick().
17692: ':'.$mapresobj->randomout().
17693: ':'.$mapresobj->encrypted().
17694: ':'.$mapresobj->randomorder().
17695: ':'.$mapresobj->is_page();
17696: } else {
17697: my $maptitle = &Apache::lonnet::gettitle($mapurl);
17698: my $ispage = (($type eq 'page')? 1 : '');
17699: if ($mapurl eq 'default') {
1.1129 raeburn 17700: $maptitle = 'Main Content';
1.1101 raeburn 17701: }
17702: $path = &Apache::lonhtmlcommon::entity_encode($mapurl).'&'.
1.1146 raeburn 17703: &escape($maptitle).':::::'.$ispage;
1.1101 raeburn 17704: }
17705: unless ($mapurl eq 'default') {
17706: $path = 'default&'.
1.1146 raeburn 17707: &escape('Main Content').
1.1101 raeburn 17708: ':::::&'.$path;
17709: }
17710: return $path;
17711: }
17712:
1.1094 raeburn 17713: sub captcha_display {
1.1327 raeburn 17714: my ($context,$lonhost,$defdom) = @_;
1.1094 raeburn 17715: my ($output,$error);
1.1234 raeburn 17716: my ($captcha,$pubkey,$privkey,$version) =
1.1327 raeburn 17717: &get_captcha_config($context,$lonhost,$defdom);
1.1095 raeburn 17718: if ($captcha eq 'original') {
1.1094 raeburn 17719: $output = &create_captcha();
17720: unless ($output) {
1.1172 raeburn 17721: $error = 'captcha';
1.1094 raeburn 17722: }
17723: } elsif ($captcha eq 'recaptcha') {
1.1234 raeburn 17724: $output = &create_recaptcha($pubkey,$version);
1.1094 raeburn 17725: unless ($output) {
1.1172 raeburn 17726: $error = 'recaptcha';
1.1094 raeburn 17727: }
17728: }
1.1234 raeburn 17729: return ($output,$error,$captcha,$version);
1.1094 raeburn 17730: }
17731:
17732: sub captcha_response {
1.1327 raeburn 17733: my ($context,$lonhost,$defdom) = @_;
1.1094 raeburn 17734: my ($captcha_chk,$captcha_error);
1.1327 raeburn 17735: my ($captcha,$pubkey,$privkey,$version) = &get_captcha_config($context,$lonhost,$defdom);
1.1095 raeburn 17736: if ($captcha eq 'original') {
1.1094 raeburn 17737: ($captcha_chk,$captcha_error) = &check_captcha();
17738: } elsif ($captcha eq 'recaptcha') {
1.1234 raeburn 17739: $captcha_chk = &check_recaptcha($privkey,$version);
1.1094 raeburn 17740: } else {
17741: $captcha_chk = 1;
17742: }
17743: return ($captcha_chk,$captcha_error);
17744: }
17745:
17746: sub get_captcha_config {
1.1327 raeburn 17747: my ($context,$lonhost,$dom_in_effect) = @_;
1.1234 raeburn 17748: my ($captcha,$pubkey,$privkey,$version,$hashtocheck);
1.1094 raeburn 17749: my $hostname = &Apache::lonnet::hostname($lonhost);
17750: my $serverhomeID = &Apache::lonnet::get_server_homeID($hostname);
17751: my $serverhomedom = &Apache::lonnet::host_domain($serverhomeID);
1.1095 raeburn 17752: if ($context eq 'usercreation') {
17753: my %domconfig = &Apache::lonnet::get_dom('configuration',[$context],$serverhomedom);
17754: if (ref($domconfig{$context}) eq 'HASH') {
17755: $hashtocheck = $domconfig{$context}{'cancreate'};
17756: if (ref($hashtocheck) eq 'HASH') {
17757: if ($hashtocheck->{'captcha'} eq 'recaptcha') {
17758: if (ref($hashtocheck->{'recaptchakeys'}) eq 'HASH') {
17759: $pubkey = $hashtocheck->{'recaptchakeys'}{'public'};
17760: $privkey = $hashtocheck->{'recaptchakeys'}{'private'};
17761: }
17762: if ($privkey && $pubkey) {
17763: $captcha = 'recaptcha';
1.1234 raeburn 17764: $version = $hashtocheck->{'recaptchaversion'};
17765: if ($version ne '2') {
17766: $version = 1;
17767: }
1.1095 raeburn 17768: } else {
17769: $captcha = 'original';
17770: }
17771: } elsif ($hashtocheck->{'captcha'} ne 'notused') {
17772: $captcha = 'original';
17773: }
1.1094 raeburn 17774: }
1.1095 raeburn 17775: } else {
17776: $captcha = 'captcha';
17777: }
17778: } elsif ($context eq 'login') {
17779: my %domconfhash = &Apache::loncommon::get_domainconf($serverhomedom);
17780: if ($domconfhash{$serverhomedom.'.login.captcha'} eq 'recaptcha') {
17781: $pubkey = $domconfhash{$serverhomedom.'.login.recaptchakeys_public'};
17782: $privkey = $domconfhash{$serverhomedom.'.login.recaptchakeys_private'};
1.1094 raeburn 17783: if ($privkey && $pubkey) {
17784: $captcha = 'recaptcha';
1.1234 raeburn 17785: $version = $domconfhash{$serverhomedom.'.login.recaptchaversion'};
17786: if ($version ne '2') {
17787: $version = 1;
17788: }
1.1095 raeburn 17789: } else {
17790: $captcha = 'original';
1.1094 raeburn 17791: }
1.1095 raeburn 17792: } elsif ($domconfhash{$serverhomedom.'.login.captcha'} eq 'original') {
17793: $captcha = 'original';
1.1094 raeburn 17794: }
1.1327 raeburn 17795: } elsif ($context eq 'passwords') {
17796: if ($dom_in_effect) {
17797: my %passwdconf = &Apache::lonnet::get_passwdconf($dom_in_effect);
17798: if ($passwdconf{'captcha'} eq 'recaptcha') {
17799: if (ref($passwdconf{'recaptchakeys'}) eq 'HASH') {
17800: $pubkey = $passwdconf{'recaptchakeys'}{'public'};
17801: $privkey = $passwdconf{'recaptchakeys'}{'private'};
17802: }
17803: if ($privkey && $pubkey) {
17804: $captcha = 'recaptcha';
17805: $version = $passwdconf{'recaptchaversion'};
17806: if ($version ne '2') {
17807: $version = 1;
17808: }
17809: } else {
17810: $captcha = 'original';
17811: }
17812: } elsif ($passwdconf{'captcha'} ne 'notused') {
17813: $captcha = 'original';
17814: }
17815: }
17816: }
1.1234 raeburn 17817: return ($captcha,$pubkey,$privkey,$version);
1.1094 raeburn 17818: }
17819:
17820: sub create_captcha {
17821: my %captcha_params = &captcha_settings();
17822: my ($output,$maxtries,$tries) = ('',10,0);
17823: while ($tries < $maxtries) {
17824: $tries ++;
17825: my $captcha = Authen::Captcha->new (
17826: output_folder => $captcha_params{'output_dir'},
17827: data_folder => $captcha_params{'db_dir'},
17828: );
17829: my $md5sum = $captcha->generate_code($captcha_params{'numchars'});
17830:
17831: if (-e $Apache::lonnet::perlvar{'lonCaptchaDir'}.'/'.$md5sum.'.png') {
17832: $output = '<input type="hidden" name="crypt" value="'.$md5sum.'" />'."\n".
17833: &mt('Type in the letters/numbers shown below').' '.
1.1176 raeburn 17834: '<input type="text" size="5" name="code" value="" autocomplete="off" />'.
17835: '<br />'.
17836: '<img src="'.$captcha_params{'www_output_dir'}.'/'.$md5sum.'.png" alt="captcha" />';
1.1094 raeburn 17837: last;
17838: }
17839: }
1.1323 raeburn 17840: if ($output eq '') {
17841: &Apache::lonnet::logthis("Failed to create Captcha code after $tries attempts.");
17842: }
1.1094 raeburn 17843: return $output;
17844: }
17845:
17846: sub captcha_settings {
17847: my %captcha_params = (
17848: output_dir => $Apache::lonnet::perlvar{'lonCaptchaDir'},
17849: www_output_dir => "/captchaspool",
17850: db_dir => $Apache::lonnet::perlvar{'lonCaptchaDb'},
17851: numchars => '5',
17852: );
17853: return %captcha_params;
17854: }
17855:
17856: sub check_captcha {
17857: my ($captcha_chk,$captcha_error);
17858: my $code = $env{'form.code'};
17859: my $md5sum = $env{'form.crypt'};
17860: my %captcha_params = &captcha_settings();
17861: my $captcha = Authen::Captcha->new(
17862: output_folder => $captcha_params{'output_dir'},
17863: data_folder => $captcha_params{'db_dir'},
17864: );
1.1109 raeburn 17865: $captcha_chk = $captcha->check_code($code,$md5sum);
1.1094 raeburn 17866: my %captcha_hash = (
17867: 0 => 'Code not checked (file error)',
17868: -1 => 'Failed: code expired',
17869: -2 => 'Failed: invalid code (not in database)',
17870: -3 => 'Failed: invalid code (code does not match crypt)',
17871: );
17872: if ($captcha_chk != 1) {
17873: $captcha_error = $captcha_hash{$captcha_chk}
17874: }
17875: return ($captcha_chk,$captcha_error);
17876: }
17877:
17878: sub create_recaptcha {
1.1234 raeburn 17879: my ($pubkey,$version) = @_;
17880: if ($version >= 2) {
17881: return '<div class="g-recaptcha" data-sitekey="'.$pubkey.'"></div>';
17882: } else {
17883: my $use_ssl;
17884: if ($ENV{'SERVER_PORT'} == 443) {
17885: $use_ssl = 1;
17886: }
17887: my $captcha = Captcha::reCAPTCHA->new;
17888: return $captcha->get_options_setter({theme => 'white'})."\n".
17889: $captcha->get_html($pubkey,undef,$use_ssl).
17890: &mt('If the text is hard to read, [_1] will replace them.',
17891: '<img src="/res/adm/pages/refresh.gif" alt="reCAPTCHA refresh" />').
17892: '<br /><br />';
17893: }
1.1094 raeburn 17894: }
17895:
17896: sub check_recaptcha {
1.1234 raeburn 17897: my ($privkey,$version) = @_;
1.1094 raeburn 17898: my $captcha_chk;
1.1234 raeburn 17899: if ($version >= 2) {
17900: my %info = (
17901: secret => $privkey,
17902: response => $env{'form.g-recaptcha-response'},
17903: remoteip => $ENV{'REMOTE_ADDR'},
17904: );
1.1280 raeburn 17905: my $request=new HTTP::Request('POST','https://www.google.com/recaptcha/api/siteverify');
17906: $request->content(join('&',map {
17907: my $name = escape($_);
17908: "$name=" . ( ref($info{$_}) eq 'ARRAY'
17909: ? join("&$name=", map {escape($_) } @{$info{$_}})
17910: : &escape($info{$_}) );
17911: } keys(%info)));
17912: my $response = &LONCAPA::LWPReq::makerequest('',$request,'','',10,1);
1.1234 raeburn 17913: if ($response->is_success) {
17914: my $data = JSON::DWIW->from_json($response->decoded_content);
17915: if (ref($data) eq 'HASH') {
17916: if ($data->{'success'}) {
17917: $captcha_chk = 1;
17918: }
17919: }
17920: }
17921: } else {
17922: my $captcha = Captcha::reCAPTCHA->new;
17923: my $captcha_result =
17924: $captcha->check_answer(
17925: $privkey,
17926: $ENV{'REMOTE_ADDR'},
17927: $env{'form.recaptcha_challenge_field'},
17928: $env{'form.recaptcha_response_field'},
17929: );
17930: if ($captcha_result->{is_valid}) {
17931: $captcha_chk = 1;
17932: }
1.1094 raeburn 17933: }
17934: return $captcha_chk;
17935: }
17936:
1.1174 raeburn 17937: sub emailusername_info {
1.1244 raeburn 17938: my @fields = ('firstname','lastname','institution','web','location','officialemail','id');
1.1174 raeburn 17939: my %titles = &Apache::lonlocal::texthash (
17940: lastname => 'Last Name',
17941: firstname => 'First Name',
17942: institution => 'School/college/university',
17943: location => "School's city, state/province, country",
17944: web => "School's web address",
17945: officialemail => 'E-mail address at institution (if different)',
1.1244 raeburn 17946: id => 'Student/Employee ID',
1.1174 raeburn 17947: );
17948: return (\@fields,\%titles);
17949: }
17950:
1.1161 raeburn 17951: sub cleanup_html {
17952: my ($incoming) = @_;
17953: my $outgoing;
17954: if ($incoming ne '') {
17955: $outgoing = $incoming;
17956: $outgoing =~ s/;/;/g;
17957: $outgoing =~ s/\#/#/g;
17958: $outgoing =~ s/\&/&/g;
17959: $outgoing =~ s/</</g;
17960: $outgoing =~ s/>/>/g;
17961: $outgoing =~ s/\(/(/g;
17962: $outgoing =~ s/\)/)/g;
17963: $outgoing =~ s/"/"/g;
17964: $outgoing =~ s/'/'/g;
17965: $outgoing =~ s/\$/$/g;
17966: $outgoing =~ s{/}{/}g;
17967: $outgoing =~ s/=/=/g;
17968: $outgoing =~ s/\\/\/g
17969: }
17970: return $outgoing;
17971: }
17972:
1.1190 musolffc 17973: # Checks for critical messages and returns a redirect url if one exists.
17974: # $interval indicates how often to check for messages.
1.1282 raeburn 17975: # $context is the calling context -- roles, grades, contents, menu or flip.
1.1190 musolffc 17976: sub critical_redirect {
1.1282 raeburn 17977: my ($interval,$context) = @_;
1.1190 musolffc 17978: if ((time-$env{'user.criticalcheck.time'})>$interval) {
1.1282 raeburn 17979: if (($env{'request.course.id'}) && (($context eq 'flip') || ($context eq 'contents'))) {
17980: my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
17981: my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
17982: my $blocked = &blocking_status('alert',$cnum,$cdom,undef,1);
17983: if ($blocked) {
17984: my $checkrole = "cm./$cdom/$cnum";
17985: if ($env{'request.course.sec'} ne '') {
17986: $checkrole .= "/$env{'request.course.sec'}";
17987: }
17988: unless ((&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) &&
17989: ($env{'request.role'} !~ m{^st\./$cdom/$cnum})) {
17990: return;
17991: }
17992: }
17993: }
1.1190 musolffc 17994: my @what=&Apache::lonnet::dump('critical', $env{'user.domain'},
17995: $env{'user.name'});
17996: &Apache::lonnet::appenv({'user.criticalcheck.time'=>time});
1.1191 raeburn 17997: my $redirecturl;
1.1190 musolffc 17998: if ($what[0]) {
17999: if (($what[0] ne 'con_lost') && ($what[0]!~/^error\:/)) {
18000: $redirecturl='/adm/email?critical=display';
1.1191 raeburn 18001: my $url=&Apache::lonnet::absolute_url().$redirecturl;
18002: return (1, $url);
1.1190 musolffc 18003: }
1.1191 raeburn 18004: }
18005: }
18006: return ();
1.1190 musolffc 18007: }
18008:
1.1174 raeburn 18009: # Use:
18010: # my $answer=reply("encrypt:passwd:$udom:$uname:$upass",$tryserver);
18011: #
18012: ##################################################
18013: # password associated functions #
18014: ##################################################
18015: sub des_keys {
18016: # Make a new key for DES encryption.
18017: # Each key has two parts which are returned separately.
18018: # Please note: Each key must be passed through the &hex function
18019: # before it is output to the web browser. The hex versions cannot
18020: # be used to decrypt.
18021: my @hexstr=('0','1','2','3','4','5','6','7',
18022: '8','9','a','b','c','d','e','f');
18023: my $lkey='';
18024: for (0..7) {
18025: $lkey.=$hexstr[rand(15)];
18026: }
18027: my $ukey='';
18028: for (0..7) {
18029: $ukey.=$hexstr[rand(15)];
18030: }
18031: return ($lkey,$ukey);
18032: }
18033:
18034: sub des_decrypt {
18035: my ($key,$cyphertext) = @_;
18036: my $keybin=pack("H16",$key);
18037: my $cypher;
18038: if ($Crypt::DES::VERSION>=2.03) {
18039: $cypher=new Crypt::DES $keybin;
18040: } else {
18041: $cypher=new DES $keybin;
18042: }
1.1233 raeburn 18043: my $plaintext='';
18044: my $cypherlength = length($cyphertext);
18045: my $numchunks = int($cypherlength/32);
18046: for (my $j=0; $j<$numchunks; $j++) {
18047: my $start = $j*32;
18048: my $cypherblock = substr($cyphertext,$start,32);
18049: my $chunk =
18050: $cypher->decrypt(unpack("a8",pack("H16",substr($cypherblock,0,16))));
18051: $chunk .=
18052: $cypher->decrypt(unpack("a8",pack("H16",substr($cypherblock,16,16))));
18053: $chunk=substr($chunk,1,ord(substr($chunk,0,1)) );
18054: $plaintext .= $chunk;
18055: }
1.1174 raeburn 18056: return $plaintext;
18057: }
18058:
1.1309 raeburn 18059: sub make_short_symbs {
18060: my ($cdom,$cnum,$navmap) = @_;
18061: return unless (ref($navmap));
18062: my ($numnew,@errors);
18063: my @toshorten = &Apache::loncommon::get_env_multiple('form.addtiny');
18064: if (@toshorten) {
18065: my (%maps,%resources,%titles);
18066: &Apache::loncourserespicker::enumerate_course_contents($navmap,\%maps,\%resources,\%titles,
18067: 'shorturls',$cdom,$cnum);
18068: my %tocreate;
18069: if (keys(%resources)) {
18070: foreach my $item (sort {$a <=> $b} (@toshorten)) {
18071: my $symb = $resources{$item};
18072: if ($symb) {
18073: $tocreate{$cnum.'&'.$symb} = 1;
18074: }
18075: }
18076: }
18077: if (keys(%tocreate)) {
18078: my %coursetiny = &Apache::lonnet::dump('tiny',$cdom,$cnum);
18079: my $su = Short::URL->new(no_vowels => 1);
18080: my $init = '';
18081: my (%newunique,%addcourse,%courseonly,%failed);
18082: # get lock on tiny db
18083: my $now = time;
18084: my $lockhash = {
18085: "lock\0$now" => $env{'user.name'}.
18086: ':'.$env{'user.domain'},
18087: };
18088: my $tries = 0;
18089: my $gotlock = &Apache::lonnet::newput_dom('tiny',$lockhash,$cdom);
18090: my ($code,$error);
18091: while (($gotlock ne 'ok') && ($tries<3)) {
18092: $tries ++;
18093: sleep 1;
1.1319 raeburn 18094: $gotlock = &Apache::lonnet::newput_dom('tiny',$lockhash,$cdom);
1.1309 raeburn 18095: }
18096: if ($gotlock eq 'ok') {
18097: $init = &shorten_symbs($cdom,$init,$su,\%coursetiny,\%tocreate,\%newunique,
18098: \%addcourse,\%courseonly,\%failed);
18099: if (keys(%failed)) {
18100: my $numfailed = scalar(keys(%failed));
18101: push(@errors,&mt('error: could not obtain unique six character URL for [quant,_1,resource]',$numfailed));
18102: }
18103: if (keys(%newunique)) {
18104: my $putres = &Apache::lonnet::newput_dom('tiny',\%newunique,$cdom);
18105: if ($putres eq 'ok') {
18106: $numnew = scalar(keys(%newunique));
18107: my $newputres = &Apache::lonnet::newput('tiny',\%addcourse,$cdom,$cnum);
18108: unless ($newputres eq 'ok') {
18109: push(@errors,&mt('error: could not store course look-up of short URLs'));
18110: }
18111: } else {
18112: push(@errors,&mt('error: could not store unique six character URLs'));
18113: }
18114: }
18115: my $dellockres = &Apache::lonnet::del_dom('tiny',["lock\0$now"],$cdom);
18116: unless ($dellockres eq 'ok') {
18117: push(@errors,&mt('error: could not release lockfile'));
18118: }
18119: } else {
18120: push(@errors,&mt('error: could not obtain lockfile'));
18121: }
18122: if (keys(%courseonly)) {
18123: my $result = &Apache::lonnet::newput('tiny',\%courseonly,$cdom,$cnum);
18124: if ($result ne 'ok') {
18125: push(@errors,&mt('error: could not update course look-up of short URLs'));
18126: }
18127: }
18128: }
18129: }
18130: return ($numnew,\@errors);
18131: }
18132:
18133: sub shorten_symbs {
18134: my ($cdom,$init,$su,$coursetiny,$tocreate,$newunique,$addcourse,$courseonly,$failed) = @_;
18135: return unless ((ref($su)) && (ref($coursetiny) eq 'HASH') && (ref($tocreate) eq 'HASH') &&
18136: (ref($newunique) eq 'HASH') && (ref($addcourse) eq 'HASH') &&
18137: (ref($courseonly) eq 'HASH') && (ref($failed) eq 'HASH'));
18138: my (%possibles,%collisions);
18139: foreach my $key (keys(%{$tocreate})) {
18140: my $num = String::CRC32::crc32($key);
18141: my $tiny = $su->encode($num,$init);
18142: if ($tiny) {
18143: $possibles{$tiny} = $key;
18144: }
18145: }
18146: if (!$init) {
18147: $init = 1;
18148: } else {
18149: $init ++;
18150: }
18151: if (keys(%possibles)) {
18152: my @posstiny = keys(%possibles);
18153: my $configuname = &Apache::lonnet::get_domainconfiguser($cdom);
18154: my %currtiny = &Apache::lonnet::get('tiny',\@posstiny,$cdom,$configuname);
18155: if (keys(%currtiny)) {
18156: foreach my $key (keys(%currtiny)) {
18157: next if ($currtiny{$key} eq '');
18158: if ($currtiny{$key} eq $possibles{$key}) {
18159: my ($tcnum,$tsymb) = split(/\&/,$currtiny{$key});
18160: unless (($coursetiny->{$tsymb} eq $key) || ($addcourse->{$tsymb} eq $key) || ($courseonly->{$tsymb} eq $key)) {
18161: $courseonly->{$tsymb} = $key;
18162: }
18163: } else {
18164: $collisions{$possibles{$key}} = 1;
18165: }
18166: delete($possibles{$key});
18167: }
18168: }
18169: foreach my $key (keys(%possibles)) {
18170: $newunique->{$key} = $possibles{$key};
18171: my ($tcnum,$tsymb) = split(/\&/,$possibles{$key});
18172: unless (($coursetiny->{$tsymb} eq $key) || ($addcourse->{$tsymb} eq $key) || ($courseonly->{$tsymb} eq $key)) {
18173: $addcourse->{$tsymb} = $key;
18174: }
18175: }
18176: }
18177: if (keys(%collisions)) {
18178: if ($init <5) {
18179: if (!$init) {
18180: $init = 1;
18181: } else {
18182: $init ++;
18183: }
18184: $init = &shorten_symbs($cdom,$init,$su,$coursetiny,\%collisions,
18185: $newunique,$addcourse,$courseonly,$failed);
18186: } else {
18187: foreach my $key (keys(%collisions)) {
18188: $failed->{$key} = 1;
18189: }
18190: }
18191: }
18192: return $init;
18193: }
18194:
1.1328 raeburn 18195: sub is_nonframeable {
1.1329 raeburn 18196: my ($url,$absolute,$hostname,$ip,$nocache) = @_;
18197: my ($remprotocol,$remhost) = ($url =~ m{^(https?)\://(([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,})}i);
1.1330 ! raeburn 18198: return if (($remprotocol eq '') || ($remhost eq ''));
1.1329 raeburn 18199:
18200: $remprotocol = lc($remprotocol);
18201: $remhost = lc($remhost);
18202: my $remport = 80;
18203: if ($remprotocol eq 'https') {
18204: $remport = 443;
18205: }
1.1330 ! raeburn 18206: my ($result,$cached) = &Apache::lonnet::is_cached_new('noiframe',$remhost.':'.$remport);
1.1329 raeburn 18207: if ($cached) {
18208: unless ($nocache) {
18209: if ($result) {
18210: return 1;
18211: } else {
18212: return 0;
18213: }
18214: }
18215: }
1.1328 raeburn 18216: my $uselink;
18217: my $request = new HTTP::Request('HEAD',$url);
18218: my $response = &LONCAPA::LWPReq::makerequest('',$request,'','',5);
18219: if ($response->is_success()) {
18220: my $secpolicy = lc($response->header('content-security-policy'));
18221: my $xframeop = lc($response->header('x-frame-options'));
18222: $secpolicy =~ s/^\s+|\s+$//g;
18223: $xframeop =~ s/^\s+|\s+$//g;
18224: if (($secpolicy ne '') || ($xframeop ne '')) {
1.1329 raeburn 18225: my $remotehost = $remprotocol.'://'.$remhost;
1.1328 raeburn 18226: my ($origin,$protocol,$port);
18227: if ($ENV{'SERVER_PORT'} =~/^\d+$/) {
18228: $port = $ENV{'SERVER_PORT'};
18229: } else {
18230: $port = 80;
18231: }
18232: if ($absolute eq '') {
18233: $protocol = 'http:';
18234: if ($port == 443) {
18235: $protocol = 'https:';
18236: }
18237: $origin = $protocol.'//'.lc($hostname);
18238: } else {
18239: $origin = lc($absolute);
18240: ($protocol,$hostname) = ($absolute =~ m{^(https?:)//([^/]+)$});
18241: }
18242: if (($secpolicy) && ($secpolicy =~ /\Qframe-ancestors\E([^;]*)(;|$)/)) {
18243: my $framepolicy = $1;
18244: $framepolicy =~ s/^\s+|\s+$//g;
18245: my @policies = split(/\s+/,$framepolicy);
18246: if (@policies) {
18247: if (grep(/^\Q'none'\E$/,@policies)) {
18248: $uselink = 1;
18249: } else {
18250: $uselink = 1;
18251: if ((grep(/^\Q*\E$/,@policies)) || (grep(/^\Q$protocol\E$/,@policies)) ||
18252: (($origin ne '') && (grep(/^\Q$origin\E$/,@policies))) ||
18253: (($ip ne '') && (grep(/^\Q$ip\E$/,@policies)))) {
18254: undef($uselink);
18255: }
18256: if ($uselink) {
18257: if (grep(/^\Q'self'\E$/,@policies)) {
18258: if (($origin ne '') && ($remotehost eq $origin)) {
18259: undef($uselink);
18260: }
18261: }
18262: }
18263: if ($uselink) {
18264: my @possok;
18265: if ($ip ne '') {
18266: push(@possok,$ip);
18267: }
18268: my $hoststr = '';
18269: foreach my $part (reverse(split(/\./,$hostname))) {
18270: if ($hoststr eq '') {
18271: $hoststr = $part;
18272: } else {
18273: $hoststr = "$part.$hoststr";
18274: }
18275: if ($hoststr eq $hostname) {
18276: push(@possok,$hostname);
18277: } else {
18278: push(@possok,"*.$hoststr");
18279: }
18280: }
18281: if (@possok) {
18282: foreach my $poss (@possok) {
18283: last if (!$uselink);
18284: foreach my $policy (@policies) {
18285: if ($policy =~ m{^(\Q$protocol\E//|)\Q$poss\E(\Q:$port\E|)$}) {
18286: undef($uselink);
18287: last;
18288: }
18289: }
18290: }
18291: }
18292: }
18293: }
18294: }
18295: } elsif ($xframeop ne '') {
18296: $uselink = 1;
18297: my @policies = split(/\s*,\s*/,$xframeop);
18298: if (@policies) {
18299: unless (grep(/^deny$/,@policies)) {
18300: if ($origin ne '') {
18301: if (grep(/^sameorigin$/,@policies)) {
18302: if ($remotehost eq $origin) {
18303: undef($uselink);
18304: }
18305: }
18306: if ($uselink) {
18307: foreach my $policy (@policies) {
18308: if ($policy =~ /^allow-from\s*(.+)$/) {
18309: my $allowfrom = $1;
18310: if (($allowfrom ne '') && ($allowfrom eq $origin)) {
18311: undef($uselink);
18312: last;
18313: }
18314: }
18315: }
18316: }
18317: }
18318: }
18319: }
18320: }
18321: }
18322: }
1.1329 raeburn 18323: if ($nocache) {
18324: if ($cached) {
18325: my $devalidate;
18326: if ($uselink && !$result) {
18327: $devalidate = 1;
18328: } elsif (!$uselink && $result) {
18329: $devalidate = 1;
18330: }
18331: if ($devalidate) {
18332: &Apache::lonnet::devalidate_cache_new('noiframe',$remhost.':'.$remport);
18333: }
18334: }
18335: } else {
18336: if ($uselink) {
18337: $result = 1;
18338: } else {
18339: $result = 0;
18340: }
18341: &Apache::lonnet::do_cache_new('noiframe',$remhost.':'.$remport,$result,3600);
18342: }
1.1328 raeburn 18343: return $uselink;
18344: }
18345:
1.112 bowersj2 18346: 1;
18347: __END__;
1.41 ng 18348:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>